Insecure Deserialization with JSON .NET
by Nairuz Abulhul
Today, I will go over one of the OWASP’s top 10 vulnerabilities, Insecure Deserialization focusing on exploiting basic .NET applications.
Serialization is the process of turning data objects into a stream of bytes that can be stored in files, memories, and databases or sent over a network, between different components of an application, and in API calls.
Deserialization is the opposite process; it restores the stream bytes into their original state of objects before they were serialized.
Insecure deserialization is passing manipulated serialized objects that can be interpreted by the application leading to its control. The impact of this vulnerability ranges from denial-of-service attacks, bypass authentications to arbitrary code execution.
As with any security vulnerability out there, the core issue is the lack of validation of user inputs from the application side that blindly trusts data sent to it. No proper checks are implemented before parsing the data and acting on it.
I chose the JSON machine from Hack The Box to demonstrate the insecure deserialization of a vulnerable .NET application.
💭 My testing methodology with web applications starts with splitting requests into three parts:
- Manually fuzz the application and observe the responses carefully with Burp Suite. I usually focus on testing each parameter sent to the application by changing, removing, or modifying the values.
- Intentionally generate error messages to see if the application will show visible errors disclosing some back-end information helping me understand the application’s structure.
- Automate the discovery of directories and endpoints.
I started inspecting the application by sending requests through Burp Suite Proxy. I noticed that the logging requests are sent to the “api/token” route to check for the username and password before granting the access token (Bearer Token).
Also, all the data are sent in the form of JSON. “Interesting”
I clicked on Action and sent the request to Repeater for further investigation. Since I do not have the password, I passed generic values to observe the application’s response. As we see from the below screen, the user does not exist.
Continued the fuzzing process by sending an empty request to the “api/token” to see if I would still get back the same error of “User Not exists” error. In the contrary, the application returned a 500 error response revealing some useful information about the application language and the location where it is deployed; yay!!!!
The username based on the displayed error is admin.
I tried to log in with admin/admin, and it worked. The machine wasn’t focusing on hardening the password as much as demonstrating the information disclosure and Insecure deserialization vulnerabilities.
Back to our demo…
After logging, I noticed two parameters added to the “api/Account” request — the Bearer token and OAuth2 Cookie. Both values are encoded in Base64.
💡 Bearer Token is an access token used with the OAuth 2.0 authorization framework to grant access to protected resourcesrather than using the owner’s credentials directly. — RFC 6750 — The OAuth 2.0 Authorization Framework
💡 One of the primary signs for insecure deserialization is having a serialized object encoded in base64 and put into a cookie. When the Cookie goes back to the application, it deserializes it and executes its content. There no security/safety controls for checking the UNTRUSTED data.
To figure out which parameter gets its content parsed by the application, I passed different values for each parameter. As you see, the application does not check for the Cookie at all. It only looks for the Bearer value.
Another way to confirm the deserialization error is to send a malformed JSON request and see how the application responds to it. I added a second double quote randomly in the request, encoded it, and send it to the application.
As you see, the application returned a deserialization error stating why it failed and what type of serialization is expecting.
- Failed because it could not parse the malformed JSON object
- The serialization type expected is a JSON.NET
Now we played with the application. We understand that the Bearer token is the vulnerable parameter; theoretically, whatever JSON data we send to it should be parsed. To check that, I sent a simple ping request to confirm we have arbitrary code execution on the machine.
Since the application is looking for JSON.NET objects, we can use a great tool called ysoserial.netbyAlvaro Muñoz to create the needed objects. His GitHub repo has excellent examples of using the tool.
Note: This tool works best on a Windows machine.
I encoded the request with Base64 and started the tcpdump to listen for ICMP packets [echo requests]. The moment I passed the encoded payload to the application, I got an immediate response from the machine validating our ability to execute code.
From here, we can use Netcat or Nishang Powershell to drop a reverse shell. I chose Nishang TCP on-liner to get the initial foothold.
As we see, the crafted payload was deserialized and executed with no verification checks from the application to prevent such an attack. The impact was a remote code execution that fully compromised the machine.
– Stop using serialization if possible 😐
– Use digital signatures such HMAC signatures to check if the data passed have not been tampered with.
– ALWAYS validate user input and sanitize the data before it gets serialized and sent to the application
That’s all for this tutorial. Thanks for stopping by !!!
About the Author
I spent 70% of the time reading security stuff and 30% trying to make it work !!! aka Pentester >>Security Researcher