Insecure Deserialization | Aditya Chaudhary

Huh, well this sounds like fun. Let’s see if it’s really that fun…

This story began not so long ago. As always some hackers found a way to exploit the deserialization process. This so called insecure deserialization vulnerability was so severe that it nailed its name in the OWASP Top 10 in 2017 as A8:2017 Insecure Deserialization. This vulnerability allows attackers to transfer a payload using serialized objects. This happens when integrity checks are not in place and deserialized data is not sanitized or validated.

What is Serialization and Deserialization?

Serialization is the process of converting an object into a stream of bytes to store the object or transmit it to memory, a database, or a file. Its main purpose is to save the state of an object in order to be able to recreate it when needed. The reverse process is called deserialization.

Dealing with Node.js

Untrusted data passed into unserialize() function in node-serialize module can be exploited to achieve arbitrary code execution by passing a serialized JavaScript Object with an Immediately invoked function expression (IIFE).

Let’s first create a vulnerable server…

server.js
  • Start the Node.js server
  • Load the page in the browser and inspect the cookies
  • The cookie set by the server is a base64 encoded JSON object

If we change the values of this JSON object and base64 encode the object and replace our current cookie’s value then the changed value is reflected on the webpage. Yessssss…..

Now the game begins…

The Payload

As we observed, the value is reflected on the webpage when we modify the cookie and reload the webpage, let’s see if we can inject some malicious code in the ‘username’ parameter.

First let’s create a Node.js script to serialize our code.

If we replace username parameter’s value with the value of test parameter(from above), the server will not execute this function. We need to make this function self invoking by appending () after the function.

_$$ND_FUNC$$_function (){ return 'hi'; }()

So, the object in the cookie looks like:

{"username": "_$$ND_FUNC$$_function (){ return 'hi'; }()" ,"country":"india","city":"Delhi"}

Let’s base64 encode it and replace it with the cookie’s value.

eyJ1c2VybmFtZSI6ICJfJCRORF9GVU5DJCRfZnVuY3Rpb24gKCl7IHJldHVybiAnaGknOyB9KCkiICwiY291bnRyeSI6ImluZGlhIiwiY2l0eSI6IkRlbGhpIn0=

Now let’s send this request and observe that “hi” string is reflected in the response. And wahlah! our code is executed on the server.

Now, let’s see if we can get a shell on the server. Let’s modify serialize.js so that our function now gives us a reverse shell by connecting to a listening port on our machine.

serialize_shell.js

Now, add this to the cookie’s JSON object and base64 encode it.

Start a listener on port 4444 in terminal on your machine (in mac remove ‘p’ option from the command).

$ nc -nvlp 4444

Replace this payload with cookie’s value in request and wahlah!! We got a reverse shell on the listener.


I guess we have reached the end of this story. This story was not entirely mine. Ajin Abraham’s blog on OpSecX Exploiting Node.js deserialization bug for Remote Code Execution has been a stepping stone for me. Lots of appreciation and thanks to him.