A Day In The Life Of A Pentester – Case Study
When it comes to performing a penetration test, one of the most important things is understanding how the application you are testing works. A pentester needs to know how it is used, all its functionalities and namely external libraries and what parts are custom-developed. In one of Smarttech247’s penetration testing engagements, we encountered an interesting case that lead our team to find an RCE (Remote Code Execution) vulnerability.
Below is a step by step on how a typical penetration test assessment is performed.
For confidentiality reasons the provided examples will be simplified.
The primary tool used during this assessment was Burp Suite Professional.
Reconnaissance is important because we need to search for all sorts of functionalities, especially those that stand out. Basically, any area where user input is accepted is a potential attack vector. The one that immediately stood out in this case was the functionality to create decision models. In such models, the user can create rules that will be processed on the server. The most important question here is – what kind of rules could be created?
That second one sounded the most promising, but it needed to be tested to make sure.
Test 1: console.log(1)
In response we received:
(...)<eval>:1 ReferenceError: "console" is not defined
Well, it couldn’t be that easy. This looked like we didn’t have access to the same properties and functionalities that existed in a standard browser.
We took a step back and looked for what actually
went to the server. Here we needed to understand what happened under the hood
with our rule, whether it was their custom solution or maybe some third party
library that processed it.
The application communicated through API with JSON messages. When sending our code to the server, the message contained the whole decision model definition in an XML format. Wait, XML?!
We then tested for the XXE (XML External Entity)vulnerability. It allowed to inject local files or even connect to the remote server if XML parser had been misconfigured.
The simplest test consists of appending
<!DOCTYPE replace [<!ENTITY example "Doe"> ]>
definition at the start of XML and then insert &example; entity which should be replaced with text “Doe” in this case. Let’s test it then.
After sending the request we received in response:
DOCTYPE is disallowed when the feature
“http://apache.org/xml/features/disallow-doctype-decl” set to true.
So, this indicated that it was safe since we were not able to define any entities in our XML. But other interesting things we could find inside it are the defined XML namespaces. XML at the beginning contains this type of line:
A quick google search told us that “Camunda BPM is an open-source workflow and decision automation platform”.
It is always worth checking the version if possible and if there are any known vulnerabilities, but nothing came up in this case.
The last interesting part is expression:
It appears right before our code, so maybe it is possible to specify another language.
Unable to find script engine for expression language ‘python’
There were many indications up to this point during the test that backend application is written in Java – Camunda platform is written in it, also some error messages got Java-style package names in them. There were even versions of Java in response to other functionalities’ request so it could be narrowed to Java 8.
All that was left was to dig through documentation and see what kind of code we could execute.
Meanwhile, some Burp Suite’s active scans were running in the background on interesting parts of other functionalities – doing everything manually is ineffective and so automating things is an important part of a pentester’s work.
One of the requests of such scan caused what looked like parsing error on the server side – and every subsequent request to access one specific resource was met with 400 HTTP status code, which effectively disabled most of applications’ UI including decision models’ functionality.
After immediate contact with the client, we got back to work. It was still possible to continue our test by manipulating JSON messages sent to API, but generated XML was horrible to work with.
Thanks to the use of Burp Suite, we could easily retrieve the working response, so the idea was to replace the broken response that contained HTTP 400 status code, with the one that worked before. As long as UI didn’t reference some data that was created there after the crash – it should work.
Unfortunately replacing the whole request proved to be quite difficult with existing tools and extensions. As it is a pretty straightforward functionality, instead of waiting for a client’s fix or wasting time on assembling proper XML we quickly wrote a Burp’s extension that did the work for us, replacing target response in flight so the browser couldn’t notice that something broke. After a quick detour, we could go back to our exploitation.
As mentioned before, there was an example of using load() function to load resources. That function was already worth testing, so the first thing was to see if it could load some internal resources like load(“/etc/passwd“).
<eval>:2 SyntaxError: /etc/passwd:1:8 Expected ; but found : root:x:0:0:root:/root:/bin/bash ^ ::
Result – if there is an error message returned (as
in our case), we can see the first line of any file on the system. That in
itself is already a pretty serious vulnerability, but its impact can be very
situational as we need to know the exact file path and are only able to read
the first line of content.
After further digging through documentation, it is easy to notice that Nashorn exposes Java API through the use of Java.type(className) function. As we assumed, it did not work since it was possible to disable access to Java API with the appropriate switch.
Searching for a way to bypass this restriction led us to an interesting issue in the Nashorn sandbox implementation https://github.com/javadelight/delight-nashorn-sandbox/issues/73
It looked very promising. After running it in our context we successfully got Java object in response! At this stage, we were practically done and all that was left was modifying the code to execute any code we wanted, be it OS command or reverse shell.
As it can be seen, there are plenty of necessary steps to properly identify what we’re dealing with during a penetration test and a lot of research for ways of exploiting it. Usually we are not inventing any “new” ways but rather using existing tools and vulnerabilities in innovative ways and adjusting them to our targets. As a last note, combining manual penetration testing and automated security testing results in a comprehensive and effective approach to security. Although they are different, they are not mutually exclusive.
Do you have any questions for our pentesting team? Feel free to send us an email on firstname.lastname@example.org