How to hack a company by circumventing its WAF for fun and profit – part 3

One of the tools we are more proud of in absolute is Richsploit that we have released few months ago. It allows the exploitation of some EL injection and java deserialization vulnerabilities on multiple versions of RichFaces, an infamous Java library included with many JSF frameworks. Anyway the reason of our pride is not simply connected to the pleasure of being able to execute arbitrary code in a remote system. Also the good time spent to make Richsploit work in certain environments is something that we take into consideration and that is at the basis of the insecurity stories with WAF, and other security devices at layer 7, we are going to share with you today.

To scope or not to scope, that’s the problem

As part of our preparation process for a security test or exercise we normally go through a scoping document together with the customer in order to gain as much information as possible about the environment we will have to test. This is done because sometimes from the list of components, middleware solutions, libraries, etc… so gained, we are able to spot in advance elements useful for the definition of a potential attack path, which saves us a lot of time during the actual security test. The starting point of our story is exactly this. From the scoping document of one of our tests we have identified a component that used the Richfaces 3.x library known to be vulnerable, even though no public exploit existed for it. So, what does the little sympathetic Red Timmy normally do in these cases? Of course he installs that component and with it the exact version of the Richfaces vulnerable library in his local lab, in order to develop and test the exploit over there. Eventually that’s what we have done. Basically, two weeks before the official task’s start date, we had in our hand a full-fledged exploit!

Then it comes the day of the security test in the real customer environment. Same component…same version of Richfaces, but this time the target application is put at the very end of a chain of proxies, load balancers and firewalls of which we knew nothing about. And of course the exploit developed and tested in our lab refuses to work there…

An ASCII character will save us all

Immediately we have started to search the root cause for that, and here what we have discovered. Richfaces implements a custom base64 encoder that does something a normal base64 encoder would never dream to do: it produces in output an encoded object (a string to simplify…) containing one or more exclamation marks. Something like this:

To exploit the vulnerability, the malicious encoded object generated with the Richfaces custom base64 encoder must be passed to the server in the URI of an HTTP GET request, along with the ‘!’ symbol(s). Something like that:

However, for some reason the target application had no intention to serve requests coming with an exclamation mark in the path. Weird! In our local lab everything worked fine. To put it simple, something was preventing our payload to turn out to a beautiful RCE primitive. We had never hated the exclamation mark symbol so much before. A possible explanation for that was that one of the front-end network devices was unhappy with such type of requests. But honestly we will never know, as the case was not investigated at the end of the activity.

Meantime we noticed that the target application used a semicolon character to separate path and querystring, like this:

https://www.bucamitutto.com/appname/path/;jsessionid=DC3DE8B0 [...]

According to RFC 3986a semicolon is often used to delimit parameters and parameter values”.

What if we put a semicolon ‘;’ character in front of the Richfaces encoded object as shown below?

  • The server interprets the encoded object (in purple) not as part of the path but of the query string where the symbol ! is instead allowed. So WAF, load balancers and proxies have nothing to complain.
  • The Richfaces base64 decoding will be still successful, because the semicolon is a non-base64 character, and according to RFC 2045 “Any characters outside of the base64 alphabet are to be ignored”.
  • Because of what the RFC above mandates, the Richfaces base64 decoder will not terminate or throw an error when encounters the semicolon, but skip over it and continue the decoding process as if the character is not there.
  • The decoded malicious payload is then correctly decoded and processed by Richfaces.
  • The payload is executed and RCE condition achieved. Victory!  \o/

The last resort

When one moves from exploiting Richfaces 3.x to Richfaces 4.x, the endpoint to shoot on is instead different compared to the one seen in the previous paragraph. With Richfaces 4.x it is something similar to:

https://www.bucamitutta.com/rfRes/org.richfaces.resource.MediaOutputResource.faces?do=<malicious_encoded_payload>

The request can be sent either as GET or POST: Richfaces handles both methods.

One day the astute Red Timmy faced an unusual (until that moment) situation for him. The application under analysis presented the traces of some resources normally decorating the Richfaces front-end elements, but a F5 Big-IP device blocked any access to the “MediaOutputResource” component.

The device had the Application Security Manager (ASM) and Local Traffic Manager (LTM) modules enabled and a set of custom iRules deployed. The WAF policy was configured in blocking mode, meaning that an error message with a support ID was returned back in case of attack detection.

The custom rules checked that:

  1. the path of each HTTP request did not point to the URL of the “MediaOutputResource” component and the URI did not contain the “do” parameter.
  2. In case of POST request, in addition to the check on the path, the presence of the parameter “do” was verified in the request’s body and not in the URI.

Specifically the rule “1” was applicable to all HTTP methods, not only GET. If the checks were not passed, the request was blocked and an error message returned back to the client.

In addition, differently than how seen in another of our blog posts, no percent-decoding misconfigurations could be appreciated. Everything looked fine. With such a kind configuration in place nothing can go wrong, right?

However, at a certain point we noticed that our target application was exposing the servlet “auth”. It was reachable via POST and took in input the parameter “res” that in the request we intercepted with Burp was pointing to a URL inside the target domain.

For example if “res=https://someserver.somedomain/path?querystring” is provided in the request’s body, as consequence of that the servlet issues a subsequent request like this:

HEAD /path?querystring HTTP/1.1
Host: someserver.somedomain
[...]

Fundamentally, it sends out an HTTP HEAD request based on the content of the parameter “res”. It means we are totally in control of the path, the query string and the target server. We cannot control the HTTP method (all the requests are sent out with the HEAD method by default) but that’s not a problem, as Richfaces 4.x can be exploited through GET which is syntactically similar to HEAD. The only difference is the fact that with HEAD the HTTP body is not returned in the reply, but we will survive to that.

Honestly we are not even “totally” in control of the target server. There is a restriction based on the domain. We could not go outside of the domain we were targeting in that moment (somedomain in the example above). But that was not a problem too, as the Richfaces instance we wanted to exploit was just living into that domain.

Given these premises, what if we set the parameter “res” to the value “https://allowedHost.somedomain/rfRes/org.richfaces.resource.MediaOutputResource.faces?do=<malicious_encoded_payload>” ?

The request crosses the chain of load balancers, firewalls, proxies, etc… completely untouched. Why untouched? Because rule “1” and “2” are not applicable in this case. According to these rules:

  • The path must not point to the URL of MediaOutputResource -> indeed it is set to “/auth”.
  • The URI must not contain the “do” parameter -> in fact there is no “do” parameter.
  • In case of POST request (…and we have a match here) the “do” parameter must not be present in the request’s body. And indeed in the request body there is only the parameter “res”. The value “do” exists only as part of the content pointed to by the “res” parameter (and not as a parameter per se) which is not sufficient condition to block the request.

So, now that we are convinced the request passes through, let’s see what comes next. The request finally lands to the “auth” servlet in the web application server. Here the content of the “res” parameter is extracted and the subsequent HTTP request is issued:

HEAD /rfRes/org.richfaces.resource.MediaOutputResource.faces?do=<malicious_encoded_payload> HTTP/1.1
Host: allowedHost.somedomain
[...]

Due to the WAF configuration mentioned before, this request would be blocked *if* sent from the internet. Indeed the rule “1” would be triggered (the path points to the MediaOutputResource component and the “do” parameter is present in the URI now). However when a malicious request is reflected via SSRF, like in our case, it normally comes from inside the targeted network, ideally localhost if the SSRF endpoint shares the same vhost with the Richfaces component or even a different vhost but configured in the same server. In such cases the request does not cross the WAF(s), load balancers or other security appliances anymore, which are all bypassed (see the picture below).

Indeed after our payload has triggered the vulnerability and we have managed to compromise the target system, a grep for “127.0.0.1” in the web application server’s log files has revealed the following thing:

In simple terms, the HEAD request to “/rfRes/org.richfaces.resource.MediaOutputResource.faces?do=<malicious_encoded_payload>” was clearly coming from localhost, where the vulnerable Richfaces component resided as well, without leaving the targeted system.

And that was another great victory. \o/ \o/

The bottom line

As the Red Timmy’s grandfather always said: “When you need to bypass WAF, the last resort is SSRF”.

These and other WAF policies and signatures bypass techniques (like shell globbing, undefined variables, self-reference folder, etc…) will be taught, along with additional updated content, in our Practical Web Application Hacking Advanced Training on 3-4 August. This year the course will be totally delivered in virtual format. Follow the news in the main page!

And don’t forget to follow us on twitter too: https://twitter.com/redtimmysec

Apache Tomcat RCE by deserialization (CVE-2020-9484) – write-up and exploit

A few days ago, a new remote code execution vulnerability was disclosed for Apache Tomcat. Affected versions are:

  • Apache Tomcat 10.x < 10.0.0-M5
  • Apache Tomcat 9.x < 9.0.35
  • Apache Tomcat 8.x < 8.5.55
  • Apache Tomcat 7.x < 7.0.104

In other words, all versions of tomcat 7, 8, 9 and 10 released before April 2020. This most certainly means you have to update your instance of tomcat in order not to be vulnerable.

Prerequisites

There are a number of prerequisites for this vulnerability to be exploitable.

  1. The PersistentManager is enabled and it’s using a FileStore
  2. The attacker is able to upload a file with arbitrary content, has control over the filename and knows the location where it is uploaded
  3. There are gadgets in the classpath that can be used for a Java deserialization attack

Tomcat PersistentManager

First some words about the PersistentManager. Tomcat uses the word “Manager” to describe the component that does session management. Sessions are used to preserve state between client requests, and there are multiple decisions to be made about how to do that. For example:

  • Where is the session information stored? In memory or on disk?
  • In which form is it stored? JSON, serialized object, etc.
  • How are sessions IDs generated?
  • Which sessions attributes do we want to preserve?

Tomcat provides two implementations that can be used:

  • org.apache.catalina.session.StandardManager (default)
  • org.apache.catalina.session.PersistentManager

The StandardManager will keep sessions in memory. If tomcat is gracefully closed, it will store the sessions in a serialized object on disk (named “SESSIONS.ser” by default).

The PersistentManager does the same thing, but with a little extra: swapping out idle sessions. If a session has been idle for x seconds, it will be swapped out to disk. It’s a way to reduce memory usage.

You can specify where and how you want swapped sessions to be stored. Tomcat provides two options:

  • FileStore: specify a directory on disk, where each swapped session will be stored as a file with the name based on the session ID
  • JDBCStore: specify a table in the database, where each swapped session will be stored as individual row

Configuration

By default, tomcat will run with the StandardManager enabled. An administrator can configure to use the PersistentManager instead, by modifying conf/context.xml:

<Manager className="org.apache.catalina.session.PersistentManager"
         maxIdleSwap="15">  
  <Store className="org.apache.catalina.session.FileStore"
         directory="./session/" />
</Manager>  

When there is no Manager tag written in context.xml, the StandardManager will be used.

The exploit

When Tomcat receives a HTTP request with a JSESSIONID cookie, it will ask the Manager to check if this session already exists. Because the attacker can control the value sent in the request, what would happen if he put something like “../../../../../../tmp/12345“?

  1. Tomcat requests the Manager to check if a session with session ID “../../../../../../tmp/12345” exists
  2. It will first check if it has that session in memory.
  3. It does not. But the currently running Manager is a PersistentManager, so it will also check if it has the session on disk.
  4. It will check at location directory + sessionid + ".session", which evaluates to “./session/../../../../../../tmp/12345.session
  5. If the file exists, it will deserialize it and parse the session information from it
The web application will return HTTP 500 error upon exploitation, because it encounters a malicious serialized object instead of one that contains session information as it expects.

From the line in the stacktrace of the above image marked in red we see that the PersistentManager tries to load the session from the FileStore. The blue line then shows that it tries to deserialize the object. The error is thrown after deserialization succeeded but when it tried to interpret the object as a session (which it is not). The malicious code has already been executed at that point.

Of course, all that is left to exploit the vulnerability is for the attacker to put a malicious serialized object (i.e. generated by ysoserial) at location /tmp/12345.session

It doesn’t make much sense to create an exploit as it’s just one HTTP request. There is however a very quick and convenient PoC written by masahiro311, see this GitHub page.

Conclusion

This attack can have high impact (RCE), but the conditions that need to be met make the likelihood of exploitation low.

  • PersistentManager needs to be enabled manually by the tomcat administrator. This is likely to happen only on websites with high traffic loads (but not too high, as it will be more likely that a JDBC Store is used instead of a File Store)
  • The attacker has to find a separate file upload vulnerability to place the malicious serialized file on the server.
  • There have to be libraries on the classpath which are vulnerable to be exploited by a Java deserialization attack (e.g. gadgets).

However, a large range of versions of tomcat are affected.

To learn more about advanced web application attacks like these, register for our virtual course at BlackHat USA 2020: Practical Web Application Hacking Advanced.

And don’t forget to follow us on twitter: @redtimmysec

References

How to hack a company by circumventing its WAF for fun and profit – part 2

So far, one of the most successful and visited posts in our blog has been this one. Following the interest of readers on the topic, we have decided to publish more contents like that. What we will be doing periodically in these spaces is to recount a few stories extrapolated from circumstances that happened for real. We had a lot of fun by living them in first person during penetration testing sessions, red team operations or well deserved bug bounties. We hope you will have too.

The “single-encoded-byte” bypass

Some time ago, we have been contacted to perform the revalidation of a RCE web vulnerability that could be triggered by visiting a URL similar to this:

https://victim.com/appname/MalPath/something?badparam=%rce_payload%

The application was hosted behind a Big-IP device with ASM (Application Security Manager) module enabled, a layer 7 web application firewall (WAF) available on F5’s BIG-IP platforms. To react immediately and not leave the solution exposed to the internet, the network engineers thought well of deploying a custom WAF rule to protect the endpoint before a formal fix was actually introduced in the application’s code. Our role consisted of investigating the effectiveness and robustness of this rule which looked like something similar to this:

The meaning of it was that in case a HTTP GET request had been intercepted (line 2) and the lowercase-converted URI contained both the strings “/malpath/” and “badparam=” (lines 3 and 4), the request would have been logged first (line 7) and then blocked by returning the HTTP error 404 to the client (line 8).

Actually, after deploying the custom WAF rule, by visiting the URL:

https://victim.com/appname/MalPath/something?badparam=%rce_payload%

…we receive the 404 HTTP error, meaning that the rule is working and the WAF is doing properly its job.

However, by reading the F5 documentation we discovered that the directive “HTTP::uri” (lines 3 and 4) returns the URI of the request without first fully decoding it! So if we visit the URL:

https://victim.com/appname/%4dalPath/something?badparam=%rce_payload%

…with “%4d” being the URL-encoded form of the character ‘M’, this is what happens:

  • The WAF intercepts the GET request. The URI in there “/appname/%4dalPath/something?badparam=%rce_payload%” is first entirely converted to lowercase because of the “tolower” function in the WAF rule, and becomes “/appname/%4dalpath/something?badparam=%rce_payload%”.
  • Then the URI is looked for the presence of the strings “/malpath/” and “badparam=”. Anyway, in the first case the string “/%4dalpath/” we sent does not match with the string “/malpath/” in the WAF rule (line 3), which hence is not hit.
  • Once the WAF control is bypassed, the HTTP request reaches the backend server where the URI canonicalization and decoding processes take place. So the request containing the URI converted from “/appname/%4dalPath/something?badparam=%rce_payload%” to “/appname/MalPath/something?badparam=%rce_payload%” is passed to the target web  application which in turn serves it.
  • The “%rce_payload%” is now executed and produces its effects (opening of a reverse shell, invocation of an operating system command, etc…)

Here the lesson learnt of the day is: always ensure that URI is decoded before comparing and checking it. Absolute lack of URI decoding is very bad. The usage of “contains” keyword in a WAF rule is even worse.

Partial URL decoding = full bypass

A couple of days later we were called back again to carry out the revalidation of the revalidation (lol) as the fix in the application code was taking more time than expected.

This time visiting both the URLs:

https://victim.com/appname/MalPath/something?badparam=%rce_payload%

and

https://victim.com/appname/%4dalPath/something?badparam=%rce_payload%

returned the 404 error page. In simple words, the WAF blocked us.

Then we attempted to double encode the letter “M” of “/Malpath/” with the sequence “%254d”. The final URL to visit was:

https://victim.com/appname/%254dalPath/something?badparam=%rce_payload%

Nothing. The web application firewall was blocking this request too. The reimplementation of the WAF rule now looks something like this:

Here the lines from 15 to 24 are equivalent to the lines 1-10 of the previous rule’s version. However this time the strings “/malpath/” and “badparam=” are checked against a variable called “$decodeUri” (lines 17 and 18) and not “HTTP::uri” as done before. What is this and where does it come from? Actually the lines from 1 to 14 are defining its content. In simple terms the “$decodeUri” variable is just the URI of the request (“[HTTP::uri]” at line 2). It is URI-decoded at most “max” times (with “max” being defined at line 4) through a “while” loop (lines 9-13) by using a counter variable (“cnt” declared at line 5). The loop is interrupted in case there are no more encoded characters left or the iteration in the cycle has occurred for “max” times (with “cnt” increased during each iteration).

To better understand what’s going on here, it could be helpful to browse this page and perform fourfold percent-encoding of the letter “M” by writing the sequence “%252525254d” into the text box. Now press over the “Decode” button (see picture below) for four times and during each pause see what happens. Have you seen it? This is exactly what the WAF rule above is doing with our URI.

So to bypass it, we have simply visited the following URL:

https://victim.com/appname/%252525254dalPath/something?badparam=%rce_payload%

On reception the WAF decodes the URI as follows:

  • (1st iteration of the loop):
    /appname/%2525254dalPath/something?badparam=%rce_payload%
  • (2nd iteration of the loop):
    /appname/%25254dalPath/something?badparam=%rce_payload%
  • (3rd iteration of the loop):
    /appname/%254dalPath/something?badparam=%rce_payload%
  • (4th iteration of the loop):
    /appname/%4dalPath/something?badparam=%rce_payload%

When the WAF rule quits the loop, the request’s URI is set to “/appname/%4dalPath/something?badparam=%rce_payload%“. Again, as in the previous case, the string “/malpath/” (line 17 of the WAF rule) does not match with the one in the URI “/%4dalpath/”, so the WAF let the request pass through. The control is bypassed once more and the effect is always the execution of “%rce_payload%”. GAME OVER for the second consecutive time.

One could think: “Ok, but this is a stupid mistake! Just fire these monkeys preteding to be security guys!”. This is not totally true as the “-normalized” option, that undoubtedly simplifies the life of network engineers, can be adopted only with the most recent versions of iRules, leaving as a unique option the creation and deployment of a custom rule which is notoriously an error-prone process. However, it is still surprising how many sources out there are publicly advertising these flawed examples, even in the official F5 documentation (watch the screenshot below).

For copy & paste supporters this kind of “code snippets” are like honey. Don’t get us wrong. We have nothing against copy & paste, except when this practice is performed in cybersecurity without understanding what is being done. In such circumstances copy&paste has been a countless number of times the root cause of many serious security vulnerabilities.

But let’s switch back to our main story! After the identification of the new issue the fix was quick (see the added part below).

Basically, following the “while” cycle (lines 9-13 in the picture at the beginning of this paragraph), the content of the WAF rule was updated with an “if” branch. It checks, at the end of the loop, if the value of the counter variable “cnt” is equal to the value of the “max” variable. In case it is, this means that the “while” cycle was interrupted while there were still characters to decode left in the URI. As we don’t want to process a request still containing encoded characters, the WAF rule responds with a 404 error, preventing the activation of the malicious payload.

Wrong assumptions … any gumption?

Most misconfigurations and bypass techniques at WAF level are not linked to technical problems or bugs afflicting the device itself, but wrong assumptions in the logic of its rules.

Our story may have had a happy ending at this point. Two revalidations had already revealed some flaws in the web application firewall’s configuration and finally a pretty tough WAF rule had been implemented, decently decoding the URI. To make it short, in a certain moment somebody realizes that the same vulnerability could have been exploited with a HTTP POST request.  The WAF rule provided protection only against GET requests and a new modification to it was needed. So, after the umpteenth change we were called back again to verify that this time everything was working as expected.

To cover both exploitation cases (GET and POST) the network engineers thought to apply a fix by deleting the line 16 of the WAF rule:

if ([HTTP::method] equals “GET”) [...]

Without that line the check on the URI would have been performed on any HTTP request, regardless of the method specified. It could have been an elegant solution if had worked…but can you spot the problem here? Take yourself few seconds before to answer. Well, when the URL below is browsed:

https://victim.com/appname/MalPath/something?badparam=%rce_payload%”

the following HTTP request is issued:

GET /appname/MalPath/something?badparam=%rce_payload% HTTP/1.1
[...]

If after discovering that the GET method is blocked at WAF level an attacker attempted to circumvent it via POST, the following request would be instead issued:

POST /appname/MalPath/something HTTP/1.1
[other HTTPheaders]

badparam=%rce_payload%

Would the “%rce_payload%” be triggered this time, given all the premises above? Yes, it would. Why? The answer is in the lines 17 and 18 of the WAF rule, shown below once more.

Let’s compare the content of the attacker’s POST request with the logic of the custom rule. In this case, at line 17 the fully decoded and lowercase-converted URI contains the string “/malpath/”. True. However the line 18 stems from an incorrect assumption. The parameters of a POST request reside in the HTTP body and not in the query string! The line 18 of the WAF rule is instead checking for the presence of the string “badparam=” in the “$decodedUri” variable which in the case of the attacker’s POST request returns the string “/appname/MalPath/something”. For sake of clarity, the WAF rule is not checking the string “badparam=” where it should, so the rule is bypassed.

Officially the revalidation of the revalidation of the revalidation was last round of check to be confident enough that the WAF rule implemented was providing the level of security desired against this web vulnerability. It took 1 week to tune up the custom WAF rule and then, suddenly, few more days later, it was removed because in the meantime the application code had been patched 😀

If it helps to repeat that once more, WAF virtual patching is ok if you need a bit of extra time before fixing a serious vulnerability, but it must not be abused. One week of exposure to RCE is a huge period of time for motivated attackers. The only way to sleep peacefully is to solve your problems at code level and patch it, patch it, patch it as soon as possible! Probably would have been worth from the beginning to dedicate all the resources to patch the application code than struggling to implement a working WAF rule by trial and error.

That’s all for today. Other stories like this will be published in future. So stay tuned and continue to follow us!

Hey! Red Timmy Security will be at Blackhat Las Vegas this year too! Check out our trainings!

Practical Web Application Hacking Advanced 1-2 August and 3-4 August 2020.

JMX RMI – Multiple Applications (RCE)

After achieving a foothold inside the targeted organization, an attacker surely will search for vulnerabilities giving him/her the ability to compromise other machines and move laterally into the network. Especially for companies adopting Java-based software solutions, one of the most abused services to achieve Remote Command Execution is JMX/RMI. It has revealed to be very pervasive in the business LAN/DMZ contexts. Indeed around one year ago we performed a random analysis of both open source and business tools, checking for the presence of JMX/RMI ports. We ended up discovering (and sometimes directly reporting) some new vulnerabilities. Few CVE(s) were registered as well. This whitepaper includes main highlights of our findings.


WHAT IS JMX

JMX (Java Management Extension) is a documental specification for remote management and monitoring of Java applications. Its main unit is the MBean (management bean), a java object exposing some attributes that can be read/written through the network, and most importantly a series of functions or operations invokable from remote. A so-called “MBeanServer” (or more simply JMX Server) keeps track of all registered MBeans inside a kind of searchable register/archive. It is the component puts in charge of managing the communication between clients that want access to one or more exposed functions/attributes of an MBean and the MBean itself.

JMX was not built with the security principle in mind. Therefore, whoever is able to reach the network port it is listening to can also invoke the exposed methods anonymously, without going through a formal authentication process.

WHAT IS RMI

The RMI (Remote Method Invocation) protocol is the most common mechanism (as well as the only one that the JMX standard expressly requires to be supported by default) through which the methods and functions the MBeans remotely expose (made available by means of JMX server) are invoked by clients.

WHERE IS THE PROBLEM WITH JMX/RMI?

By default no authentication is enabled for JMX/RMI. Furthermore the authentication, when rarely adopted, is only restricted to a couple of options:

  • File-based: insecure as passwords are left in clear-text in the filesystem (also transmitted in clear-text over the network);
  • TLS mutual authentication: difficult to set up and maintain with the growth of the numbers of clients and nodes, as it requires the generation of digital keys and certificates for each of them.

The Oracle Java documentation is self-explanatory when it comes to determine where the problem stems from: (https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html)

Caution – […] any remote user who knows (or guesses) your port number and host name will be able to monitor and control your Java applications and platform. Furthermore, possible harm is not limited to the operations you define in your MBeans. A remote client could create a “javax.management.loading.MLet” MBean and use it to create new MBeans from arbitrary URLs, at least if there is no security manager. In other words, a rogue remote client could make your Java application execute arbitrary code”.

AFFECTED SOFTWARE

Below follows the list of components and software solutions that we found affected by this specific issue during an analysis conducted in the period February-March 2018.

CISCO UNIFIED CUSTOMER VOICE PORTAL <= 11.x

Cisco Unified CVP is an intelligent IVR (Interactive Voice Response) and call control solution. In its default configuration, until version <= 11.x and potentially above, an unauthenticated JMX/RMI interface is bound to a wildcard address on TCP ports 2098 and 2099. An attacker establishing a network connection to one of these affected ports and sending the malicious payload could easily trigger the RCE.

The vulnerability was first discovered on February 2018. The vendor was made aware almost immediately. After multiple meetings and discussions occurred between April and July 2018, Cisco agreed to document two new security procedures in to its CVP configuration guide:

  • Secure JMX Communication between CVP Components
  • Secure JMX Communication between OAMP and Call Server using Mutual Authentication.

We reopen the dialogue with Cisco on February 2019 when a CVE has been asked but not assigned, as the vendor considers the flaw as a configuration issue.

Anyway Cisco has published a security bulletin for the flaw we reported at the URL https://quickview.cloudapps.cisco.com/quickview/bug/CSCvi31075

NASDAQ BWISE <= 5.x

This is a commercial GRC (Governance, Risk and Compliance Management) solution for risk handling and management. Branch 5.x of Nasdaq BWise is vulnerable because by default the SAP BO Component enables JMX/RMI on TCP port 81 without authentication.

We discovered the vulnerability and contacted the vendor on March 2018. After discussing with them, the release of Service Pack (SP02) has been announced to solve the problem.

A CVE number has not been requested until February 2019, when we realized that in the meantime another security researcher had registered CVE-2018-11247 for the same issue. As indicated in their published security bulletin https://packetstormsecurity.com/files/148918/Nasdaq-BWise-5.0-JMX-RMI-Interface-Remote-Code-Execution.html, the researcher had discovered the vulnerability 2 months after us (May 2018).

However as a CVE already existed, we did not requested a new one.

NICE ENGAGE PLATFORM <= 6.5

NICE Engage is an interaction recording platform. Versions <= 6.5 (and potentially above) open up the TCP port 6338 where a JMX/RMI service listens to without authentication. Of course this may be abused to launch remote commands by deploying a malicious MBean.

On March 4th 2018 we contacted the vendor and on 7th same month they have recognized the vulnerability. They also declared that no specific fix would have been released because enabling the JMX file-based authentication was considered enough to mitigate the finding. Anyway, this change is not reflected in the default configuration and must be applied manually, leaving at risk the companies using the product and are not aware of the problem. Only very recently, February 2019, we have registered CVE-2019-7727 for this vulnerability

APACHE CASSANDRA 3.8 through 3.11.1 and CLOUDERA ZOOKEEPER/CDH 5.X/6.X

On February 2018 we discovered that the Apache Software Foundation project dubbed Cassandra (release between 3.8 and 3.11) exposed the TCP port 7199 on which JMX/RMI was running. We did not report the finding immediately. Soon after someone else did it and registered CVE-2018-8016. All details are perfectly explain here:

https://lists.apache.org/thread.html/bafb9060bbdf958a1c15ba66c68531116fba4a83858a2796254da066@%3Cuser.cassandra.apache.org%3E

During a contextual security investigation on March 2018 we also managed to spot multiple instances of Cloudera Zookeeper/CDH (versions 5.x and 6.x) affected. In this case the TCP port 9010 was exposing a JMX/RMI service. The vendor is aware of the problem at least since June 2018. In one of their release notes they wrote:

A successful attack may leak data, cause denial of service, or even allow arbitrary code execution on the Java process that exposes a JMX port. Beginning in Cloudera Manager 6.1.0, it is possible to configure mutual TLS authentication on ZooKeeper’s JMX port”.

The possibility to configure mutual TLS authentication for previous product versions is unknown instead. We did not register a CVE for this vulnerability.

EXPLOIT CODE

Working tools to exploit JMX/RMI vulnerabilities exist out there. Some good examples are sjet and mjet. When we have started our first investigations in this field did not manage to find one fitting all requirements (we have had problems to target specific contexts and configurations) and have decided to develop our own. This tool is not going to be publicly shared for now. It probably will in future, so visit our blog (https://redtimmysec.wordpress.com).

Moreover, if you want to know more about JMX/RMI exploitation and mitigation, check out our Blackhat Las Vegas courses on 3-4 and 5-6 August 2019, because this will be one of the topics covered there.

Stay tuned!

FlexPaper <= 2.3.6 RCE

Around one year ago we discovered a Remote Command Execution vulnerability on FlexPaper (https://www.flowpaper.com). The vendor was immediately contacted and a CVE registered (2018-11686). However the vulnerability itself has remained undisclosed until now, regardless the fact that a patch has been issued with the release 2.3.7 of the project.

What is Flexpaper

FlexPaper is an open source project, released under GPL license, quite widespread over the internet.  It provides document viewing functionalities to web clients, mobile and tablet devices. At least until 2014 the component has been actively used by WikiLeaks, when it was discovered to be affected by a XSS vulnerability subsequently patched. The remote command execution vulnerability hereby descripted has remained 0day until being reported to vendor in April 2018.

Setup.php

The “php/setup.php” script is used to initialize the Flexpaper configuration file in the “config/” folder. The function “pdf2swfEnabled()” passes the user input unsafely to “exec()” that leads straight to arbitrary command execution.

However, this entry point can be only reached in case Flexpaper has not been initialized (i.e. there is no configuration file in the “config/” folder) which is the main reason to have the software downloaded and installed.

Therefore, after the configuration process is completed, the “exec()” function cannot be hit with arbitrary user input.

File removal via change_config.php

FlexPaper <= 2.3.6 also suffers from an unrestricted file overwrite vulnerability in “php/change_config.php“. The component exposes a functionality to update the configuration file. However, access to this file is improperly guarded, as can be seen in the code snippet below

The “SAVE_CONFIG” is done before the “FLEXPAPER_AUTH” authorization check is performed. Therefore, a not authenticated user can send a POST request and have the configuration file updated. Even more interesting is the fact that after the configuration file is updated, the script will remove all files in the directory that is configured under “path.swf”. As this path was just updated by the attacker, he is in full control of the directory in which he wants to delete files. 

An example of a HTTP request, which results in deletion of the configuration file, is depicted below.

Back to Setup.php

Once the Flexpaper configuration file is deleted, the vulnerable entry point at “setup.php” becomes reachable again. Now the attacker is one GET request far from triggering RCE. The malicious payload is provided to the “PDF2SWF_PATH” parameter. The command is injected just prepending a semicolon “;” character as shown below

In the example above the server was forced to base64 decode a PHP web shell (see following image) and write that in to a file named “tiger_shell.php” in the webserver’s document root.

The web shell

The web shell adopted in this case takes as an input a key and an arbitrary base64 encoded command submitted via GET request. It base64 decode and execute the command only if the key matches with the hardcoded one. From now on, the attacker can launch any arbitrary command like this:

The command injected through “cmd” parameter, for this specific example, is the base64 encoded representation of “id;uname -a;pwd”. Needless to say it can be whatever command the attacker wants to run.

Red Timmy Sec will be at Blackhat Las Vegas this year! Check out our trainings

Some additional notes

During last year we have reported this vulnerability to several broadcasting and media companies that were using the affected component. Some government websites as well.

Exploit code

The exploit has been published here.