~ubuntu-branches/ubuntu/wily/bandit/wily-proposed

« back to all changes in this revision

Viewing changes to docs/yaml.md

  • Committer: Package Import Robot
  • Author(s): Dave Walker (Daviey)
  • Date: 2015-07-22 09:01:39 UTC
  • Revision ID: package-import@ubuntu.com-20150722090139-fl0nluy0x8m9ctx4
Tags: upstream-0.12.0
ImportĀ upstreamĀ versionĀ 0.12.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
yaml.load()
 
2
=====================
 
3
The [PyYaml docs](http://pyyaml.org/wiki/PyYAMLDocumentation#LoadingYAML) have
 
4
details on why using ```yaml.load()``` with untrusted user data is very scary.
 
5
 
 
6
```yaml.load()``` can lead to remote code execution - ```yaml.safe_load()```
 
7
should be used whenever parsing untrusted YAML.
 
8
 
 
9
 
 
10
### Incorrect
 
11
We'll use [Paul McMillan's
 
12
gist](https://gist.github.com/PaulMcMillan/c4d560471dd529fdf9f3) to demonstrate
 
13
why ```yaml.load()``` is scary. We start by defining a few things for our
 
14
exploit, starting with ```exploit.py``` will look like:
 
15
```python
 
16
print 'WINNA WINNA'
 
17
```
 
18
 
 
19
In order to get ```yaml.load()``` to properly execute our Python, we have to do
 
20
some careful encoding.
 
21
```python
 
22
encoded = ("eval(compile('%s'.decode('base64'), '<string>', 'exec'))" % exploit.encode('base64').replace('\n', ''))
 
23
```
 
24
 
 
25
After executing the above, our ```encoded``` variable looks like:
 
26
```"eval(compile('cHJpbnQgIldJTk5BIFdJTk5BIgo='.decode('base64'), '<string>', 'exec'))"```
 
27
 
 
28
Next, we build the actual YAML object:
 
29
```python
 
30
yaml_object = ('\nupgrade_helper: !!python/object/apply:eval ["%s",]\n' % encoded)
 
31
```
 
32
 
 
33
This results in ```yaml_object``` looking like:
 
34
```
 
35
'\nupgrade_helper: !!python/object/apply:eval ["eval(compile(\'cHJpbnQgIldJTk5BIFdJTk5BIgo=\'.decode(\'base64\'), \'<string>\', \'exec\'))",]\n'
 
36
```
 
37
 
 
38
We then take that ```yaml_object``` and print it to a file ```exploit.yaml```, it will look like:
 
39
```
 
40
upgrade_helper: !!python/object/apply:eval ["eval(compile('cHJpbnQgIldJTk5BIFdJTk5BIgo='.decode('base64'), '<string>', 'exec'))",]
 
41
```
 
42
 
 
43
Ok, all the setup is done. All we need to do now is
 
44
```yaml.load(open("exploit.yaml").read())```. We can see from the output that
 
45
our ```print``` was executed and ```WINNA WINNA``` was printed to STDOUT:
 
46
```console
 
47
>>> yaml.load(open("exploit.yaml"))
 
48
    WINNA WINNA
 
49
```
 
50
 
 
51
### Correct
 
52
Use ```yaml.safe_load()``` instead of ```yaml.load()```. In the PoC above, if we
 
53
try to load ```exploit.yaml``` via ```safe_load()``` we get the following error:
 
54
```
 
55
>>> yaml.safe_load(open("exploit.yaml"))
 
56
Traceback (most recent call last):
 
57
...
 
58
yaml.constructor.ConstructorError: could not determine a constructor for the tag 'tag:yaml.org,2002:python/object/apply:eval'
 
59
  in "exploit.yaml", line 1, column 17
 
60
```
 
61
 
 
62
## Consequences
 
63
* Remote code execution
 
64
 
 
65
## References
 
66
* http://pyyaml.org/wiki/PyYAMLDocumentation#LoadingYAML
 
67
* https://gist.github.com/PaulMcMillan/c4d560471dd529fdf9f3