To prepare for OSWE I am working through the awesome PortSwigger Academy material and labs. This post contains my notes on Insecure Serialization made while working through the labs.
Why serialize data?
Data serialization converts structured data into a standardised format. This allows the sharing and storage of the data in a form that allows the recovery of it’s original structure and state.
So basically serialization is taking a live object in memory, converting it into a format that allows it to be stored somewhere and then later deserializing the data back into a live object.
Some forms of serialization also compress the data, therefore reducing the file size on disk. In large organisations this can translate into a significant cost reduction.
What does serialized data look like?
It depends on the language but it is either stored in a human-readable string format (PHP) or in a binary format (Java). To identify serialized data look for the PHP methods serialize() and unserialize(). Serialized Java objects always begin with the same bytes ac ed (HEX) and rO0 (B64). Look for the readObject() method within code if whitebox testing as this is used by Java to deserialize data.
Below is an example of some base64 encoded serialized data within the Cookie header:
Here is the decoded output:
Looking at the serialized object we can see that:
- O:4:”User” is an object with the the 4-character class name “User” and has 4 attributes
- s:8:”username” is the key of the first attribute it is a string value (s) and it is 8 characters in length
- s:5:”gregg” is the value of the first attribute it is a string value (s) and it is 5 characters in length
and so on for the rest of the data. So in this case it simply consists of a list of attributes with key:value pairs.
So how is this exploitable?
In the above POST request the serialized data originally contained the key value pair:
I grabbed the base64 serialized data from burp, decoded it using Burp Decoder and modified the avatar_link value to:
Note: you must also update the character length (s:18 to s:23)
After modifying the serialized data I re-encoded it into Base64 using Burp Decoder and pasted the modified data back into the Cookie: header of the above POST request. Making a POST request to the URL /my-account/delete using the unmodified serialized data deletes the user account (I logged into the web application as gregg) and the file at the location /users/gregg/avatar. Sending the modified serialized data instead, deletes a known file on another users account, which in this case is /home/carlos/morale.txt. So by simply modifying the file path we are able to arbitrarily delete files on the server. Pretty neat!
The above example shows how to use a web applications own functionality to exploit insecure deserialization but it can also be used as a simple authentication bypass. Take the below code for example:
Now lets say the serialized data contains the following object:
Looking at the key:value pairs we can see the second attribute isAdmin. The value of :b:0 indicates that this is a boolean value and in this case the value is 0 (False). Simply by changing the value to 1, adding our new serialized data to the appropriate header and making a new request will bypass authentication and allow access to the admin control panel!
Magic methods are special methods that are not directly invoked by the user. The invocation (sounds like magic!) happens internally from the class on a certain action. For instance in Python when you add two numbers using the + operator, internally, the _add_() method will be called. There are alot of magic methods. To check for yourself open up Python and type dir(int) and you will see all of the magic methods defined in the int class. Most magic methods are prefixed or surrounded with double underscores as you can see:
So essentialy magic methods can be added to a class to be executed when a specified event or scenario occurs.
Magic methods are used everywhere and are not an inherent vulnerability. They can become a vulnerability when they handle user controlled data (i.e from a deserialized object).
Serializable classes can also declare their own readObject() methods like so:
Here’s how to exploit a magic method using user controlled serialized data:
First we find some source code that contains deserialization magic methods and then check if the magic method performs an operation on controllable data that we can exploit.
To grab the source code from the website I used this great tip from Portswigger Academy:
You can sometimes read source code by appending a tilde (~) to a filename to retrieve an editor-generated backup file.
In this case it works! The following code was retrieved from the target webserver using Burp:
Looking over the source code we can see that there are two magic methods defined within the CustomTemplate class, __construct and __destruct. __destruct appears to delete the lock file created by the __construct magic method. Lets try and use __destruct to delete an arbritrary file on the webserver.
To accomplish this we are going to have to pass our unserialized data to the __destruct magic method somehow. When a session is initiated with this particular webserver a serialization session mechanism is used. The following serialized data is sent and deserialized on the web-server:
Instead of sending back this serialized session data lets be assholes and send a CustomTemplate object identified from the source code instead of a User object:
I used the the serializededitor website to create the data as it automatically calculates string length.
and encoded the data back to Base64 using Burp Decoder:
After replacing the User object with the CustomTemplate object and sending it an error is returned but this doesn’t matter as our new object is already instantiated and the target file has been deleted!
Java Gadget chains
The example above used only one magic method to exploit the web server. It is also possible to chain together a series of method invocations to exploit a target. This is know as a gadget chain.
It would be very difficult to identify a gadget chain without access to the source code. Luckily there are known gadget chains that have been used successfuly on other web servers that we can try even without knowing the source code. The reason that this is possible is due to web servers that use standard or similar librarys, therefore it stands to reason that if one web server can be exploited using gadget chains from one library then any other web server using the same library should be exploitable also.
So lets exploit it:
Let’s just say that through enumeration we have discovered that the target is using the Apache Commons Library.
Lets run ysoserial and take a look the usage:
So it appears that all we need to do to generate a payload is specify the desired payload and the command to execute. As we have identified that the target is using the Apache Commons library we will try the CommonsCollection4 (yes I tried 1,2 and 3) payload and attempt to delete an arbritrary file from the web server.
To generate the payload I used the following command:
The command does the following:
- ysoserial generates a serialized payload to execute the command ‘rm /home/carlos/morale.txt’
- sends standard error to /dev/null
- encodes the payload using into base64 format
- removes new lines from the output so that it can be pasted directly into Burp
- redirects the output into the file ‘payload.bin’
All that remains is to copy the payload into the appropriate header value and send it to the web server. To do this I used Burp Suite to intercept a request containing the serialized session data, removed the old data and pasted in the generated payload. Prior to sending the payload remember to URL encode the payload. To do this highlight your payload -> right click -> convert selection -> URL -> URL-encode key characters.
After sending the payload using Burp the file on the web server was successfully deleted!
PHP gadget chains
For PHP the PHP Generic Gadget Chains library can be used.
First you need to identify the framework in use by the server. In this case an error divulges that the server is running Symfony 4.3.6. Viewing the page source also shows that the file /cgi-bin/phpinfo.php exists on the server. Viewing the output from phpinfo.php we can see under the PHP Variables section that the variable $_SERVER[‘SECRET_KEY’] exists.
Using PHPGGC a payload is generated to delete the file ‘/home/carlos/morale.txt’ using the following command:
A request is then intercepted within Burp and it is apparant that the cookie is signed due to the ‘sig_hmac_sha1’ key being present:
To generate a valid signed cookie the following PHP code can be used:
Note: The secret used to sign the cookie is the value from the SECRET_KEY php variable discovered earlier.
running our new script generates the serialized url encoded cookie:
All that remains is to paste the output into the “Cookie: session=” header/variable in an intercepted Burp request and then send the request to the web server!
After sending the request to the web server the file is deleted and RCE is achieved. Since we are now able to use PHP functions such as exec and system we would be able to escalate the RCE to a shell.
Ruby gadget chains
For the technical side of this published exploit refer to these links:
The below code is a universal gadget chain to achieve arbitrary command execution for Ruby 2.x:
The above code will generate a base64 encoded serialized payload and verify it working locally (using a local ruby install). To exploit a remote web server copy the base64 encoded output and replce the original serialized data within your captured request with the payload.