Labo de recherche et de reproduction contrôlées de vulnérabilités pour CVE-2020-14343 dans PyYAML
Ce projet démontre la CVE-2020-14343 dans un environnement Docker contrôlé et isolé.
Le laboratoire contient deux environnements :
yaml.safe_load()Le laboratoire démontre le cycle de vie complet de la vulnérabilité :
La charge utile de reproduction utilisée dans ce laboratoire est intentionnellement inoffensive et se contente d'afficher un marqueur de test.
| Propriété | Détails |
|---|
| CVE | CVE-2020-14343 |
| Produit | PyYAML |
| Versions affectées | Versions antérieures à 5.4 |
| Version corrigée | 5.4 |
| Type de vulnérabilité | Validation d'entrée incorrecte (CWE-20) |
| Sévérité | Critique |
| Surface d'attaque | Entrée YAML traitée à l'aide d'une fonctionnalité de chargement vulnérable |
La vulnérabilité se produit lorsqu'une entrée YAML non fiable est traitée à l'aide d'une fonctionnalité de chargement PyYAML vulnérable.
Les versions de PyYAML antérieures à 5.4 autorisaient des balises YAML spécialement conçues, notamment des balises spécifiques à Python telles que :
!!python/object/new
à atteindre la fonctionnalité de construction d'objets lorsque FullLoader était utilisé.
Un document YAML malveillant peut donc provoquer une exécution non intentionnelle de code Python.
Le problème était lié à un correctif incomplet d'une vulnérabilité PyYAML antérieure.
PyYAML 5.4 a corrigé la CVE-2020-14343 en déplaçant les balises Python arbitraires vers UnsafeLoader.
Pour les applications traitant du YAML non fiable, ce laboratoire utilise également yaml.safe_load() comme approche de chargement sécurisée.
Le laboratoire contient deux services Docker isolés :
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 | Port hôte | Port conteneur | PyYAML | Objectif |
|---|---|---|---|---|
| vulnerable | 5000 | 5000 | 5.3.1 | Environnement vulnérable |
| patched | 5001 | 5000 | 5.4 | Environnement corrigé |
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
| Fichier/Répertoire | Objectif |
|---|---|
vulnerable/app.py | Application Flask utilisant un chargement YAML vulnérable |
vulnerable/Dockerfile | Construit l'image Docker vulnérable |
vulnerable/requirements.txt | Fixe PyYAML à 5.3.1 |
patched/app.py | Application Flask utilisant un chargement YAML sécurisé |
patched/Dockerfile | Construit l'image Docker corrigée |
patched/requirements.txt | Fixe PyYAML à 5.4 |
exploit/reproduce.py | Reproduit la vulnérabilité à l'aide d'une charge utile contrôlée |
detection/detect.py | Vérifie la version de PyYAML à l'intérieur d'un conteneur Docker |
docker-compose.yml | Construit et exécute les deux environnements |
Les logiciels suivants sont requis :
Docker Desktop doit être en cours d'exécution avant de démarrer le laboratoire.
L'application vulnérable est intentionnellement exposée uniquement sur la machine locale via les mappages de ports Docker.
Depuis le répertoire racine du projet, exécutez :
docker compose up --build -d
Exécutez :
docker compose ps
Les deux services doivent afficher Up.
Ouvrez :
http://127.0.0.1:5000
Réponse attendue :
{
"message": "CVE-2020-14343 vulnerable YAML parser"
}
Ouvrez :
http://127.0.0.1:5001
Réponse attendue :
{
"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
L'application vulnérable expose un point de terminaison /parse qui accepte une entrée YAML.
L'environnement vulnérable utilise :
yaml.FullLoaderLe script de reproduction se trouve à l'emplacement :
exploit/reproduce.py
L'application vulnérable est disponible à l'adresse :
http://127.0.0.1:5000/parse
Exécutez :
python exploit/reproduce.py
Le script envoie une charge utile YAML contrôlée contenant une balise YAML spécifique à Python.
Dans l'environnement vulnérable, la charge utile est acceptée et l'application renvoie une réponse HTTP 200.
Le marqueur de test contrôlé :
CVE-2020-14343-TEST
est exécuté à l'intérieur du conteneur de l'application vulnérable.
L'exécution peut être vérifiée à l'aide de :
docker logs cve-vulnerable
La sortie attendue des journaux comprend :
CVE-2020-14343-TEST
Cela démontre l'exécution de code via le comportement de chargement YAML vulnérable.
La charge utile de reproduction est intentionnellement inoffensive. Elle se contente d'afficher un marqueur de test et ne modifie pas le système hôte, n'accède pas aux identifiants, n'accède pas aux données sensibles, n'interagit pas avec des systèmes externes et n'effectue aucune action destructive.
Le script de détection se trouve à l'emplacement :
detection/detect.py
Le script vérifie la version de PyYAML installée à l'intérieur du conteneur Docker spécifié.
La version corrigée est :
5.4
Exécutez :
python detection/detect.py cve-vulnerable
Résultat attendu :
PyYAML installed version: 5.3.1
Fixed version: 5.4
STATUS: VULNERABLE
Reason: PyYAML version is older than 5.4.
Exécutez :
python detection/detect.py cve-patched
Résultat attendu :
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
Le script interroge la version de PyYAML installée depuis le conteneur cible.
L'environnement vulnérable utilise :
PyYAML==5.3.1
L'environnement corrigé utilise :
PyYAML==5.4
L'application corrigée utilise également :
yaml.safe_load(yaml_input)
au lieu de :
yaml.load(yaml_input, Loader=yaml.FullLoader)
yaml.safe_load() lorsque l'application ne nécessite que des types de données YAML standard.La même charge utile d'exploit est testée contre l'application corrigée.
L'application corrigée s'exécute sur :
http://127.0.0.1:5001/parse
Exécutez :
python exploit/reproduce.py http://127.0.0.1:5001/parse
Résultat observé dans ce laboratoire :
HTTP Status: 400
Server Response:
{"message":"could not determine a constructor for the tag 'tag:yaml.org,2002:python/object/new:tuple' ...","status":"error"}
La balise YAML spécifique à Python est rejetée par l'application corrigée.
Le marqueur de test contrôlé n'est pas exécuté dans l'application corrigée.
| Test | Environnement vulnérable | Environnement corrigé |
|---|---|---|
| Version de PyYAML | 5.3.1 | 5.4 |
| Détection | VULNERABLE | PATCHED |
| Requête d'exploit | HTTP 200 | HTTP 400 |
| Balise YAML spécifique à Python | Acceptée | Rejetée |
| Exécution du marqueur de test | Observée dans les journaux du conteneur | Non exécutée |
Assurez-vous que Docker Desktop est en cours d'exécution et vérifiez :
docker version
Vérifiez :
docker ps -a
Supprimez les anciens conteneurs si nécessaire :
docker rm cve-vulnerable cve-patched
Puis :
docker compose up --build -d
docker compose ps
docker logs cve-vulnerable
docker logs cve-patched
docker compose up --build -d
Pour une reconstruction propre :
docker compose build --no-cache
docker compose up -d
Ce projet est un laboratoire de preuve de concept contrôlé et ne représente pas un déploiement en production.
Les limitations incluent :
Les preuves suivantes doivent être capturées pour la soumission du projet :
STATUS: VULNERABLE.CVE-2020-14343-TEST dans les journaux Docker.STATUS: PATCHED.Les captures d'écran peuvent être stockées dans :
screenshots/
Tout le code de reproduction et la documentation de ce dépôt ont été développés spécifiquement pour ce laboratoire.
Ce laboratoire démontre la CVE-2020-14343 depuis l'identification jusqu'à la remédiation.
L'environnement vulnérable utilise PyYAML 5.3.1 et démontre une exécution de code contrôlée via une charge utile YAML conçue.
Le script de détection identifie la version vulnérable de la dépendance.
L'environnement corrigé met à niveau PyYAML vers 5.4 et utilise yaml.safe_load() pour traiter du YAML non fiable.
La même charge utile de reproduction est ensuite rejetée par l'application corrigée, fournissant des preuves avant/après de la remédiation.
L'ensemble de l'environnement peut être reproduit à l'aide de Docker Compose.