bandit/doc/old/yaml.md
Eric Brown 222c0809b3 Use sphinx autodoc to generate docs from docstring
Rather than having separate rst documentation files, this patch auto
generates the docs from the docstrings in the modules. Should
make it easier to maintain.

Also renamed directory docs to doc to be consistent with all other
OpenStack projects.

Change-Id: Iaed77f8358ccb6edaf2627fbabdcc855272b4ea2
2015-12-15 16:57:57 -08:00

2.4 KiB

yaml.load()

The PyYaml docs have details on why using yaml.load() with untrusted user data is very scary.

yaml.load() can lead to remote code execution - yaml.safe_load() should be used whenever parsing untrusted YAML.

Incorrect

We'll use Paul McMillan's gist to demonstrate why yaml.load() is scary. We start by defining a few things for our exploit, starting with exploit.py will look like:

print 'WINNA WINNA'

In order to get yaml.load() to properly execute our Python, we have to do some careful encoding.

encoded = ("eval(compile('%s'.decode('base64'), '<string>', 'exec'))" % exploit.encode('base64').replace('\n', ''))

After executing the above, our encoded variable looks like: "eval(compile('cHJpbnQgIldJTk5BIFdJTk5BIgo='.decode('base64'), '<string>', 'exec'))"

Next, we build the actual YAML object:

yaml_object = ('\nupgrade_helper: !!python/object/apply:eval ["%s",]\n' % encoded)

This results in yaml_object looking like:

'\nupgrade_helper: !!python/object/apply:eval ["eval(compile(\'cHJpbnQgIldJTk5BIFdJTk5BIgo=\'.decode(\'base64\'), \'<string>\', \'exec\'))",]\n'

We then take that yaml_object and print it to a file exploit.yaml, it will look like:

upgrade_helper: !!python/object/apply:eval ["eval(compile('cHJpbnQgIldJTk5BIFdJTk5BIgo='.decode('base64'), '<string>', 'exec'))",]

Ok, all the setup is done. All we need to do now is yaml.load(open("exploit.yaml").read()). We can see from the output that our print was executed and WINNA WINNA was printed to STDOUT:

>>> yaml.load(open("exploit.yaml"))
    WINNA WINNA

Correct

Use yaml.safe_load() instead of yaml.load(). In the PoC above, if we try to load exploit.yaml via safe_load() we get the following error:

>>> yaml.safe_load(open("exploit.yaml"))
Traceback (most recent call last):
...
yaml.constructor.ConstructorError: could not determine a constructor for the tag 'tag:yaml.org,2002:python/object/apply:eval'
  in "exploit.yaml", line 1, column 17

Consequences

  • Remote code execution

References