Controlled vulnerability research and reproduction lab for CVE-2020-14343 in PyYAML
This project demonstrates CVE-2020-14343 in a controlled and isolated Docker environment.
The lab contains two environments:
yaml.safe_load()The lab demonstrates the complete vulnerability lifecycle:
The reproduction payload used in this lab is intentionally harmless and only prints a test marker.
| Property | Details |
|---|---|
| CVE | CVE-2020-14343 |
| Product | PyYAML |
| Affected versions | Versions before 5.4 |
| Fixed version | 5.4 |
| Vulnerability type | Improper Input Validation (CWE-20) |
| Severity | Critical |
| Attack surface | YAML input processed using vulnerable loading functionality |
| CVSS | 9.3 (CVSS v4, GitHub Advisory) |
The vulnerability occurs when untrusted YAML input is processed using vulnerable PyYAML loading functionality.
PyYAML versions before 5.4 allowed specially crafted YAML tags, including Python-specific tags such as:
!!python/object/new
to reach object construction functionality when FullLoader was used.
A malicious YAML document can therefore cause unintended Python code execution.
The issue was related to an incomplete fix for an earlier PyYAML vulnerability.
PyYAML 5.4 fixed CVE-2020-14343 by moving arbitrary Python tags to UnsafeLoader.
For applications processing untrusted YAML, this lab also uses yaml.safe_load() as a secure loading approach.
The lab contains two isolated Docker services:
Host Machine
|
+------------+------------+
| |
v v
Vulnerable App Patched App
localhost:5000 localhost:5001
| |
PyYAML 5.3.1 PyYAML 5.4
FullLoader SafeLoader
| |
v v
Payload executes Payload rejected
| Service | Host Port | Container Port | PyYAML | Purpose |
|---|---|---|---|---|
| vulnerable | 5000 | 5000 | 5.3.1 | Vulnerable environment |
| patched | 5001 | 5000 | 5.4 | Remediated environment |
cve-2020-14343-lab/
├── vulnerable/
│ ├── app.py
│ ├── Dockerfile
│ └── requirements.txt
├── patched/
│ ├── app.py
│ ├── Dockerfile
│ └── requirements.txt
├── exploit/
│ └── reproduce.py
├── detection/
│ └── detect.py
├── screenshots/
│ ├── 01-docker-compose-running.png
│ ├── 02-vulnerable-detection.png
│ ├── 03-patched-detection.png
│ ├── 04a-vulnerable-request.png
│ ├── 04b-vulnerable-execution-log.png
│ └── 05-patched-exploit-blocked.png
├── blog/
│ └── CVE-2020-14343-Technical-Blog.pdf
├── docker-compose.yml
├── README.md
└── .gitignore
| File/Directory | Purpose |
|---|---|
vulnerable/app.py | Flask application using vulnerable YAML loading |
vulnerable/Dockerfile | Builds the vulnerable Docker image |
vulnerable/requirements.txt | Pins PyYAML to 5.3.1 |
patched/app.py | Flask application using secure YAML loading |
patched/Dockerfile | Builds the patched Docker image |
patched/requirements.txt | Pins PyYAML to 5.4 |
exploit/reproduce.py | Reproduces the vulnerability using a controlled payload |
detection/detect.py | Checks the PyYAML version inside a Docker container |
docker-compose.yml | Builds and runs both environments |
screenshots/ | Contains evidence captured during the lab |
blog/ | Contains the 800–1200 word technical blog PDF |
The following software is required:
Docker Desktop must be running before starting the lab.
The vulnerable application is intentionally exposed only on the local machine through Docker port mappings.
From the project root directory, run:
docker compose up --build -d
Run:
docker compose ps
Both services should show Up.
Open:
http://127.0.0.1:5000
Expected response:
{
"message": "CVE-2020-14343 vulnerable YAML parser"
}
Open:
http://127.0.0.1:5001
Expected response:
{
"message": "CVE-2020-14343 patched YAML parser"
}
docker compose up -d
docker compose down
docker compose up --build -d
docker compose ps
docker logs cve-vulnerable
docker logs cve-patched
The vulnerable application exposes a /parse endpoint that accepts YAML input.
The vulnerable environment uses:
yaml.FullLoaderThe reproduction script is located at:
exploit/reproduce.py
The vulnerable application is available at:
http://127.0.0.1:5000/parse
Run:
python exploit/reproduce.py
The script sends a controlled YAML payload containing a Python-specific YAML tag.
In the vulnerable environment, the payload is accepted and the application returns an HTTP 200 response.
The controlled test marker:
CVE-2020-14343-TEST
is executed inside the vulnerable application container.
The execution can be verified using:
docker logs cve-vulnerable
Expected log output includes:
CVE-2020-14343-TEST
This demonstrates code execution through the vulnerable YAML loading behavior.
The reproduction payload is intentionally harmless. It only prints a test marker and does not modify the host system, access credentials, access sensitive data, interact with external systems, or perform destructive actions.
The detection script is located at:
detection/detect.py
The script checks the PyYAML version installed inside the specified Docker container.
The fixed version is:
5.4
Run:
python detection/detect.py cve-vulnerable
Expected result:
PyYAML installed version: 5.3.1
Fixed version: 5.4
STATUS: VULNERABLE
Reason: PyYAML version is older than 5.4.
Run:
python detection/detect.py cve-patched
Expected result:
PyYAML installed version: 5.4
Fixed version: 5.4
STATUS: PATCHED
Reason: PyYAML version is 5.4 or newer.
Docker Container
|
v
docker exec
|
v
Import PyYAML
|
v
Read yaml.__version__
|
v
Compare with 5.4
|
+----------------------+
| |
< 5.4 >= 5.4
| |
v v
VULNERABLE PATCHED
The script queries the installed PyYAML version from the target container.
The vulnerable environment uses:
PyYAML==5.3.1
The patched environment uses:
PyYAML==5.4