Brewing RCEs by validating Java Beans
This article is about a vulnerability that I learnt recently. A tale on how input validation allowed me to hack your server :P
Before exploiting the vulnerability and hacking into servers, let us take a minute to understand what this vulnerability is all about. I won't cover much detail, as many researchers have already done it.
[ TLDWR ] If you are here only for the Demo, please skip to the final section of this blog, where I have explained how to find and exploit the vulnerable code.
Not this Bean !! In Java, beans are objects with different properties or attributes.
Imagine you have a box and want to ensure that whatever you put inside the box follows certain rules. Bean validation in Java is like having a special magic power that automatically checks if the things you put inside the box meet those rules.
For example, imagine you have a bean called "Person" with properties like "name," "age," and "email."
Now, let's say you want to ensure that every time you create a new "Person" object, the name is not empty, the age is a positive number, and the email follows a specific format. You can use bean validation instead of manually checking these rules every time.
Bean validation provides a set of rules or constraints that you can apply to the properties of your beans. These rules define what is valid and what is not. So, when you create a new "Person" object, the bean validation magic power automatically checks if the name is not empty, the age is positive, and the email is in the correct format.
If everything follows the rules, you can feel confident that the object you put inside the box is valid. But if something doesn't meet the rules, the bean validation magic power will raise an error or give you a message saying what's wrong so that you can fix it.
Overall, bean validation ensures that the objects you create in Java follow specific rules, making your programs more reliable and less prone to errors.
Practical use case:
And frameworks like Springboot use this technology; for example, let us assume that a web application written in Java expects an IP address from the user.
But we cannot assume that users of our application will always provide us with the right values. We must ensure that the values we get from users are validated and in the expected format.
The following image shows that the web server validated user input and returned an error message saying evil.com is not an IP address.
You must note here that the user input (evil.com) is reflected in the response error message, and the server uses a vulnerable Hibernate Validator implementation. This means nothing but a Remote Code Execution via Expression Language Injection.
We can send Expression Language payloads to the remote server in the IP address field, and the server will run the command for us without even a second thought.
In Java, we often use variables to store values. For example, we might have a variable called "age" that stores a person's age. With Expression Language, we can directly refer to that variable and use it in our code without having to write lengthy code to access it.
EL allows us to perform operations on variables, like adding or subtracting numbers, comparing values, or even accessing properties of objects. For example, we can use Expression Language to calculate someone's birth year based on age or check if a certain value is greater than another.
Many expression languages are available such as JSP EL, OGNL, MVEL, SpEL and JBoss EL.
For more info: https://docs.oracle.com/javaee/6/tutorial/doc/bnahq.html
There are different approaches to remediate the issue:
Do not include validated bean properties in the custom error message.
Use parameterized messages instead of string concatenation.
Disable the EL interpolation and only use
Replace Hibernate Validator with Apache BVal, which does not interpolate EL expressions by default in its latest version. Note that this replacement may not be a simple drop-in replacement.
DEMO TIME !!!
Talking alone is boring, so let's get our hands dirty and pop some reverse shells. I initially thought of writing a demo application, but what fun is there in exploiting an intentionally vulnerable hello world application? I did some research and found a vulnerable application on Github, but they have patched it.
The vulnerable code is still there for us to play with,
Download the zip file from here https://github.com/browserup/browserup-proxy/releases/download/v2.0.1/browserup-proxy-2.0.1.zip
And we will also need the code to perform static code analysis; this will help us understand the vulnerability. Follow the steps to identify the vulnerable function:
v2.0.1tag using the git command to get the vulnerable code.
Now search for this function in VSCode
buildConstraintViolationWithTemplateto identify the vulnerable validator function and constraints. We have around six validators using a vulnerable function.
Let's start the browser-proxy server and play with it. Unzip the file we have downloaded from the release page. And run the
binfolder to start the server.
The previous image shows that a query param (MILLISECONDS) in the
/assertResponseTimeLessThanOrEqual endpoint is vulnerable to attack. Let us try and send a curl request. Before pasting it into the curl, it is advised to URL encode the data that is sent to the server.
A challenge for you!
The application we downloaded to practice this vulnerability also has a few other endpoints you could exploit. If you have found those endpoints, figured out the functions, and need help, please ping me.
You can find my social handles on my profile page. Cheers to hacking 🥂