Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set your subtitle preferences in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please refer to our video player troubleshooting guide for assistance.

Security Concerns

00:00 A couple of lessons ago you saw how you can use the .__setstate__() method to reinitialize things that can’t be saved in a pickled object. This was a cool workaround, but remember how you executed that print() statement that had little to do with the object itself? In this lesson, you’re going to see how pickle is not secure and why you should never unpickle untrusted data.

00:23 Let’s take a look at an example where you’ll be an attacker going after a service that transfers data in a pickled format. In this scenario, imagine that the client is sending a pickled stream of data to the server.

00:35 What the service is probably expecting is that users have some sort of Python application on their machines which is collecting data, pickling it, and then sending it over the network to the service, which could be a Flask web application that’s storing that data in a database.

00:51 Unfortunately for the service, you’ve realized this process by monitoring network traffic out of your machine. You decide to make your own custom class that will compromise the service running the application using pickle.

01:04 I’ve made a new Python script called pickle_attack.py. Go ahead and import pickle, and you’re also going to import os. Now define your new custom class, which you can call foobar.

01:18 Define a constructor .__init__() method that will take in self, and it can just be pass. And now, to work with the .__getstate__() and .__setstate__() methods, go ahead and define .__getstate__(), which will take in self, and will return self and then the .__dict__ property.

01:40 And now the interesting part is the .__setstate__() method, which will take in self and state.

01:49 Because this example is going to take place on my Mac and would also work on a Linux machine, the attack is going to come from localhost, and you will also expect the attack is listening on port 8080.

02:06 So now using os, you’re going to make a system() call, and this is going to be a little bit complex, so let me copy and paste this in.

02:15 And if I zoom out a little bit, you can see that there’s a Bash script here, which is actually a pretty cool one-liner, but we won’t get too far into the details of how this works.

02:26 Basically, what this line is going to do is it’s going to open up an interactive Bash session, which is actually a reverse shell that’s going to point back to localhost at port 8080.

02:38 How this works is well beyond the scope of this lesson, but it’s a cool example to see how pickle can be used in less-than-expected ways. Because we’re only going to be working inside of one script, go ahead and make an instance of foobar.

02:56 And then my_pickle is going to be equal to pickle and then dump this into a string, and pass in my_foobar.

03:06 And then now to actually call this .__setstate__() method, you’re going to say my_unpickle is equal to pickle and then load from the string and pass in my_pickle.

03:17 So here you make an instance of your custom attack class, you then pickle it to get the serialized data stream, and this is what would be sent over the network.

03:27 And then here is what the server would do, where it would take that pickled data stream, unpickle it, which would then call this .__setstate__() method, which unfortunately for the server, instead of returning what the properties of the expected object should be it’s actually going to run this code here, which it does not want to do.

03:50 So, save this script, open up a terminal, and this is going to happen in two steps. I’m opening up a second terminal, and this is what would be the attacker’s machine—so that localhost at port 8080.

04:05 Use the nc command for Ncat, and then you’re going to listen with a -l at port 8080. So by running this, it looks like nothing’s happening, and that’s because this terminal is now listening on port 8080 for some type of communication to come back through.

04:24 So now I’m back in my original terminal where the other Python scripts have run, and this is what the server would be, basically. So now here, go ahead and run the pickle_attack.py,

04:41 and you’ll see that this hangs up. So go back to the attacker terminal, and if I expand this out, you can see it’s saying that now this is an interactive shell.

04:52 So now let’s see what we can do. We can print the working directory, and this is the working directory that I ran the script from. You can do all sorts of stuff, like run a whoami and see, “Oh, well there’s the user account that’s running that script.” So obviously, this is not intended behavior and you definitely don’t want to expose any services that you have to somebody to be able to run a reverse shell this easily. I mean, looking at the actual exploit itself, it’s just a couple of lines of code and really just one significant line.

05:26 The biggest takeaway from here is that if you’re going to pickle data and then unpickle the data, make sure you trust where that unpickled data is coming from because it can be very easy for an attacker to execute code on your machine that you’re not intending to run. And with that, you should be pretty comfortable using pickle and knowing some of its limitations and how to get around those limitations. In the next video, we’re going to take a few minutes to wrap up and cover everything that you learned.

Become a Member to join the conversation.