How programming languages can hurt your application’s security

Posted on

The token is base64-decoded, unpacked, and then checked. However, what is to prevent a malicious user from creating his or her own pickle and then base64-encoding it? Nothing. That is the danger of pickles; the data can come from untrusted sources.

The exploit can be constructed like so:

import cPickle
import subprocess
import base64
class RunBinSh(object):
def __reduce__(self):
return (subprocess.Popen, (('/bin/sh',),))
print base64.b64encode(cPickle.dumps(RunBinSh()))

This was covered in a 2011 blog by Nelson Elhage of Stripe (the source of these code snippets). And Django, arguably the most popular web framework for Python, had a similar pickle bug for sessions up until version 1.6, which was two years after the blog posting on Twisted! A mantra often heard after such discoveries is, “Don’t use pickles!” Some people react by doing more JSON and YAML, but, again, you hit unexpected problems. YAML, as it turns out, has a nifty feature, unknown to many developers, to instantiate native object types.

Note this excerpt from the official spec concerning “Other Schemas”:

None of the above recommended schemas preclude the use of arbitrary explicit tags. Hence YAML processors for a particular programming language typically provide some form of local tags that map directly to the language’s native data structures (e.g., !ruby/object:Set).

That’s essentially what a pickle provides: language-native data structures.

Hence, a line such as:

print(yaml.load(exploit)['user_input'])

can be compromised by including a field value of something such as:

exploit = '''\
name: Boris Chen
user_input: !!python/object/apply:subprocess.check_output
args: [ cat ~/.ssh/id_rsa ]
kwds: {shell:true}
'''
Prev2 of 6Next

Leave a Reply

Your email address will not be published. Required fields are marked *