Attacking Python's pickle
Asked Answered
O

3

33

I'm writing a web app that stores user input in an object. This object will be pickled.

Is it possible for a user to craft malicious input that could do something egregious when the object is unpickled?

Here's a really basic code example that ignores wonderful principles such as encapsulation but epitomizes what I'm looking at:

import pickle

class X(object):
    some_attribute = None

x = X()
x.some_attribute = 'insert some user input that could possibly be bad'

p = pickle.dumps(x)

# Can bad things happen here if the object, before being picked, contained
# potentially bad data in some_attribute?
x = pickle.loads(p)
Oporto answered 23/4, 2012 at 14:8 Comment(5)
Is it only a string? No, it's secure. Is it an arbitrary object? You betcha, it can do bad stuff.Lung
+1 this is an excellent questionRoyalroyalist
pickle.loads(p) treats a string as an arbitrary object, thoughRoyalroyalist
@spinning_plate I'm not too worried about that as my code will only unpickle data it has previously pickled, it's just a concern whether data in the object that has been passed in by a malicious user through the public web interface can somehow trick pickle.loads() to act like an evil eval() and execute arbitrary code or do some other badness.Oporto
yeah, I want to say @Algin is right, but you want to be very careful here. There's plenty of room for trickeryRoyalroyalist
A
15

Yes and no...

No - unless there's a bug with the interpreter or the pickle module, you can't run arbitrary code via pickled text, or something like that. unless the pickled text is evaled later, or you're doing stuff like creating a new object with a type mentioned in this data.

Yes - depending on what you plan to do with the information in the object later, a user can do all sorts of things. From SQL injection attempts, to changing credentials, brute force password cracking, or anything that should be considered when you're validating user input. But you are probably checking for all this.


Edit:

The python documentation states this:

Warning The pickle module is not intended to be secure against erroneous or maliciously constructed data. Never unpickle data received from an untrusted or unauthenticated source.

However this is not your case - you accept the input, put it through the regular validation, and then pickle it.

Algin answered 23/4, 2012 at 14:14 Comment(8)
A person could write an object, that when called, would do stupid things, but the question of whether just unpickling could do something bad might be more complexRoyalroyalist
here's an example, but you have to protect your code from user input in any case, so I hardly see this as a real threat, unless you are actually letting the user send a pickled object to you securityfocus.com/bid/5257/exploitAlgin
but p in his case is an object constructed by OP from user input, not just pickled data sent from the user if I'm reading this correctly. the data itself is escaped, so unless the constructor of the object is doing some weird shit without checking the data, it should be fine.Algin
@Algin you must assume the attacker has the algorithm.Peterson
@Peterson even if they have it, if it's just input (strings/numbers/booleans) that's put in an object constructed on the server side and then pickled, how would you run arbitrary code with it?Algin
@Algin If i know how your object is constructed, I would have to get an object instantiated that upon serialization (load) would parse out the block of text that is representative of an attack. I apologize for not having a working exploit for this, but the explanation does serve as a loose guide as to how it works.Peterson
@Peterson given this simple class here: pastebin.com/zMqsVqp6 is there an attack input that when not validated would execute arbitrary code if the object is pickled and then unpickled?Algin
@Algin When I return home from work I will look at exploit code for the example provided.Peterson
P
7

Well according to the documentation

Warning: The pickle module is not intended to be secure against erroneous or maliciously constructed data. Never unpickle data received from an untrusted or unauthenticated source.

It would imply that it is possible to attack this functionality just by invoking it if the structure of the data existed in such a state that the pickle algorithm would enter into a state where program behavior was not guaranteed.

According to this site

import pickle
pickle.loads(b"cos\nsystem\n(S'ls ~'\ntR.") # This will run: ls ~

is all that is required to execute arbitrary code. There are other examples there as well as an "improvement" to pickling for security purposes.

Peterson answered 23/4, 2012 at 14:23 Comment(8)
I saw that site. Only objects that have been pickled by my code would be unpickled. The concern is whether the object, before being pickled, could contain some string that would trick the pickle.loads() function to execute arbitrary code.Oporto
@Oporto it appears to me that I can pass you some string i.e. the one above and you will pickle it just fine and then unpickle it as that string. Which would result in you executing my statement.Peterson
@Oporto also assume that the attacker knows all the inner workings of your system and how text will be escaped / unescaped.Peterson
Pickling the string in the example and then unpickling it will not run ls.Plectognath
@MichaelJ.Barber I did not write the tutorial.Peterson
@Peterson You did, however, claim that "you will pickle it just fine and then unpickle it as that string. Which would result in you executing my statement." This is not true. While nothing you've said in your answer is incorrect, I'm not sure that it addresses the question, where you're not dealing with an untrusted pickle, but a pickle of some form of untrusted data. That is, can we construct a string s that will cause problems in pickle.loads(pickle.dumps(s))?Plectognath
ON ubuntu 16 and python 3.6 this code gives the error : pickle.loads("cos\nsystem\n(S'ls ~'\ntR.") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: a bytes-like object is required, not 'str'Tribe
@Tribe I got the same error; the code runs as discussed on Ubuntu 20.04 and Python 3.8.10 after prefixing the string literal with a b to make it a bytes literal (i.e. pickle.loads(b"cos\nsystem\n(S'ls ~'\ntR."))Bamboozle
R
0

I found this in the documentation of multiprocessing module which I think answers the question:

Warning

The Connection.recv() method automatically unpickles the data it receives, which can be a security risk unless you can trust the process which sent the message.

Therefore, unless the connection object was produced using Pipe() you should only use the recv() and send() methods after performing some sort of authentication. See Authentication keys.

(emphasis mine)

Conclusion is that if the connection object is produced using a trusted Pipe, i.e. a trusted pickle, then it can be safely unpickled.

Roundhouse answered 26/1, 2017 at 1:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.