A Day In The Life Of A Pentester – Case Study



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? 

Two major types were available. One made it possible to create decision tables from a set list of conditions. The second one allowed to write arbitrary javascript code.

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

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.

Another quick search and it appears Camunda supports JavaScript, Groovy, Python, Ruby and JUEL languages. The only ones allowed in this case were javascript and JUEL as rest was not recognizable.

Unable to find script engine for expression language ‘python’

After initial research, it seemed that JUEL is a simple expression language and there was not much that could be done besides simple expressions and math operations in the context given in the target application. The Javascript one was far more interesting. 

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.

It was necessary to know what Javascript API was available and if it could be abused in any way. The application contained example javascript codes among which there existed load() function which loaded target .js file from classpath. Now by using all that information and googling around it was pretty easy to pinpoint the exact engine used – it was Nashorn, javascript engine in Java. 

All that was left was to dig through documentation and see what kind of code we could execute.

Unexpected problems

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 info@smarttech247.com