针对 PyYAML 中 CVE-2020-14343 的受控漏洞研究与复现实验环境
本项目在受控且隔离的 Docker 环境中演示 CVE-2020-14343。
该实验包含两个环境:
yaml.safe_load() 的修复环境该实验演示了完整的漏洞生命周期:
本实验中使用的复现载荷是故意无害的,仅打印一个测试标记。
| 属性 | 详情 |
|---|
| CVE | CVE-2020-14343 |
| 产品 | PyYAML |
| 受影响版本 | 5.4 之前的版本 |
| 修复版本 | 5.4 |
| 漏洞类型 | 输入验证不当 (CWE-20) |
| 严重性 | 严重 |
| 攻击面 | 使用存在漏洞的加载功能处理的 YAML 输入 |
当使用存在漏洞的 PyYAML 加载功能处理不受信任的 YAML 输入时,就会出现该漏洞。
5.4 之前的 PyYAML 版本允许特制的 YAML 标签,包括 Python 特定的标签,例如:
!!python/object/new
在使用 FullLoader 时,这些标签可以到达对象构造功能。
因此,恶意 YAML 文档可能导致意外的 Python 代码执行。
该问题与早期 PyYAML 漏洞的不完整修复有关。
PyYAML 5.4 通过将任意 Python 标签移至 UnsafeLoader 修复了 CVE-2020-14343。
对于处理不受信任 YAML 的应用程序,本实验还使用 yaml.safe_load() 作为安全的加载方式。
该实验包含两个隔离的 Docker 服务:
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
| 服务 | 主机端口 | 容器端口 | PyYAML | 用途 |
|---|---|---|---|---|
| vulnerable | 5000 | 5000 | 5.3.1 | 漏洞环境 |
| patched | 5001 | 5000 | 5.4 | 修复环境 |
cve-2020-14343-lab/
│
├── vulnerable/
│ ├── app.py
│ ├── Dockerfile
│ └── requirements.txt
│
├── patched/
│ ├── app.py
│ ├── Dockerfile
│ └── requirements.txt
│
├── exploit/
│ └── reproduce.py
│
├── detection/
│ └── detect.py
│
├── docker-compose.yml
└── README.md
| 文件/目录 | 用途 |
|---|---|
vulnerable/app.py | 使用存在漏洞的 YAML 加载方式的 Flask 应用程序 |
vulnerable/Dockerfile | 构建存在漏洞的 Docker 镜像 |
vulnerable/requirements.txt | 将 PyYAML 固定为 5.3.1 |
patched/app.py | 使用安全 YAML 加载方式的 Flask 应用程序 |
patched/Dockerfile | 构建已修复的 Docker 镜像 |
patched/requirements.txt | 将 PyYAML 固定为 5.4 |
exploit/reproduce.py | 使用受控载荷复现漏洞 |
detection/detect.py | 检查 Docker 容器内的 PyYAML 版本 |
docker-compose.yml | 构建并运行两个环境 |
需要以下软件:
在启动实验之前,Docker Desktop 必须正在运行。
存在漏洞的应用程序通过 Docker 端口映射故意仅暴露在本地机器上。
从项目根目录运行:
docker compose up --build -d
运行:
docker compose ps
两个服务都应显示 Up。
打开:
http://127.0.0.1:5000
预期响应:
{
"message": "CVE-2020-14343 vulnerable YAML parser"
}
打开:
http://127.0.0.1:5001
预期响应:
{
"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
存在漏洞的应用程序暴露了一个接受 YAML 输入的 /parse 端点。
漏洞环境使用:
yaml.FullLoader复现脚本位于:
exploit/reproduce.py
存在漏洞的应用程序可通过以下地址访问:
http://127.0.0.1:5000/parse
运行:
python exploit/reproduce.py
该脚本发送一个包含 Python 特定 YAML 标签的受控 YAML 载荷。
在漏洞环境中,载荷被接受,应用程序返回 HTTP 200 响应。
受控测试标记:
CVE-2020-14343-TEST
在存在漏洞的应用程序容器内执行。
可以使用以下命令验证执行情况:
docker logs cve-vulnerable
预期日志输出包括:
CVE-2020-14343-TEST
这演示了通过存在漏洞的 YAML 加载行为实现代码执行。
复现载荷是故意无害的。它仅打印一个测试标记,不会修改主机系统、访问凭据、访问敏感数据、与外部系统交互或执行破坏性操作。
检测脚本位于:
detection/detect.py
该脚本检查指定 Docker 容器内安装的 PyYAML 版本。
修复版本为:
5.4
运行:
python detection/detect.py cve-vulnerable
预期结果:
PyYAML installed version: 5.3.1
Fixed version: 5.4
STATUS: VULNERABLE
Reason: PyYAML version is older than 5.4.
运行:
python detection/detect.py cve-patched
预期结果:
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
该脚本从目标容器查询已安装的 PyYAML 版本。
漏洞环境使用:
PyYAML==5.3.1
已修复环境使用:
PyYAML==5.4
已修复的应用程序还使用:
yaml.safe_load(yaml_input)
而不是:
yaml.load(yaml_input, Loader=yaml.FullLoader)
yaml.safe_load()。针对已修复的应用程序测试相同的漏洞利用载荷。
已修复的应用程序运行在:
http://127.0.0.1:5001/parse
运行:
python exploit/reproduce.py http://127.0.0.1:5001/parse
本实验中观察到的结果:
HTTP Status: 400
Server Response:
{"message":"could not determine a constructor for the tag 'tag:yaml.org,2002:python/object/new:tuple' ...","status":"error"}
Python 特定的 YAML 标签被已修复的应用程序拒绝。
受控测试标记未在已修复的应用程序中执行。
| 测试 | 漏洞环境 | 已修复环境 |
|---|---|---|
| PyYAML 版本 | 5.3.1 | 5.4 |
| 检测 | VULNERABLE | PATCHED |
| 漏洞利用请求 | HTTP 200 | HTTP 400 |
| Python 特定 YAML 标签 | 接受 | 拒绝 |
| 测试标记执行 | 在容器日志中观察到 | 未执行 |
确保 Docker Desktop 正在运行并验证:
docker version
检查:
docker ps -a
如有需要,删除旧容器:
docker rm cve-vulnerable cve-patched
然后:
docker compose up --build -d
docker compose ps
docker logs cve-vulnerable
docker logs cve-patched
docker compose up --build -d
进行干净重建:
docker compose build --no-cache
docker compose up -d
本项目是一个受控的概念验证实验,并不代表生产部署。
局限性包括:
应为项目提交收集以下证据:
STATUS: VULNERABLE。CVE-2020-14343-TEST 标记。STATUS: PATCHED。截图可以存储在:
screenshots/
本仓库中的所有复现代码和文档均为本实验专门开发。
本实验演示了从识别到修复的 CVE-2020-14343。
漏洞环境使用 PyYAML 5.3.1,并通过特制的 YAML 载荷演示了受控的代码执行。
检测脚本识别出存在漏洞的依赖版本。
修复后的环境将 PyYAML 升级到 5.4,并使用 yaml.safe_load() 处理不受信任的 YAML。
随后,相同的复现载荷被已修复的应用程序拒绝,提供了修复前后的证据。
整个环境可以使用 Docker Compose 复现。