Lab 05: YAML Deserialization RCE

Objective

Exploit PyYAML's yaml.load() with the unsafe Loader=yaml.Loader to achieve Remote Code Execution via crafted YAML payloads:

  1. Load benign YAML to establish a baseline

  2. Inject !!python/object/apply:os.system to execute OS commands

  3. Use subprocess.run to capture command output in the response

  4. Compare safe (yaml.safe_load) vs unsafe (yaml.load) behaviour


Background

PyYAML supports a !!python/object/apply: tag that instructs the parser to call a Python function during deserialisation. With the default or yaml.Loader (unsafe), any callable can be invoked — including os.system.

Real-world examples:

  • 2017 Ansible — Ansible playbooks are YAML; untrusted playbooks with !!python/object/apply allowed lateral movement in CI/CD pipelines. Mandatory use of --check mode added to mitigate.

  • 2018 PyYAML CVE-2017-18342yaml.load() without Loader= argument allows arbitrary code execution. Affected countless Python applications using config-file loading. Fixed by requiring explicit Loader= in PyYAML 6.0.

  • Kubernetes/Helm — Helm chart values.yaml files parsed by Go's YAML library; analogous deserialization issues in Go's encoding/yaml allowed type confusion attacks.

  • CI/CD pipelines — Jenkins, GitLab CI, and GitHub Actions all parse YAML configuration; supply chain attacks inject malicious YAML into dependency build files.

OWASP: A08:2021 Software and Data Integrity Failures


Architecture

Time

40 minutes


Lab Instructions

Step 1: Setup


Step 2: Launch Kali


Step 3: YAML RCE — os.system

📸 Verified Output:

💡 !!python/object/apply:callable [args] is PyYAML's function call syntax. When yaml.load() (unsafe) encounters this tag, it calls the named Python callable with the provided arguments — during parsing, before your application code even sees the result. The attack happens silently inside the YAML parser itself.


Step 4: RCE via Profile Import Endpoint


Step 5: Advanced Payload — Reverse Shell Setup


Step 6: Demonstrate safe_load Protection

📸 Verified Output:


Step 7: Automated Scan


Step 8: Cleanup


Remediation

Loader

!!python/ tags

Safe for untrusted input

yaml.Loader

✅ Executes

❌ Never

yaml.FullLoader

⚠️ Partially

❌ No

yaml.SafeLoader

❌ Blocked

✅ Yes

yaml.safe_load()

❌ Blocked

✅ Yes

Further Reading

Last updated