
Laboratoire reproductible pour CVE-2026-10053 (GitLab npm package-registry traversée de chemin -> écriture arbitraire de fichier en tant que git). Version vulnérable 19.2.1 vs corrigée 19.2.2, oracle déterministe.
Laboratoire autonome qui prouve le path traversal du registre de paquets npm de GitLab (CVE-2026-10053) et montre qu'il est corrigé, à l'aide d'un oracle déterministe sur disque — pas du statut HTTP.
Ce qui est prouvé et ce qui ne l'est pas. Ce laboratoire prouve l'écriture arbitraire de fichiers authentifiée en tant qu'utilisateur OS
gitsur une instance GitLab vulnérable, et que la version corrigée la bloque. Ce n'est pas un PoC autonome d'exécution de code à distance — voir Portée. Ne le citez pas comme une RCE.
| Vulnérabilité | CWE-22 path traversal dans le registre de paquets npm (TOCTOU : la vérification s'exécutait before :cache, pas before :store) |
| CVSS | 8.5 High — AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H |
| Affecté | GitLab CE/EE 18.8 → <19.0.6, 19.1 → <19.1.4, 19.2 → <19.2.2 |
| Corrigé | 19.0.6 / 19.1.4 / 19.2.2 — commit 435cf863 « Re-valider le path traversal du téléversement avant le store » |
python3, git, curl sur la machine hôte../run.sh # up + provision + exploit + verify BOTH; exits 0 iff vuln writes AND patched blocks
Résultat attendu :
================ CVE-2026-10053 verification ================
INSTANCE EXPECT RESULT STATUS
gitlab-vuln (19.2.1) written written PASS
gitlab-patched (19.2.2) blocked blocked PASS
------------------------------------------------------------
PROVEN: authenticated arbitrary file write as git on 19.2.1.
NOT a standalone RCE — see README.md 'Scope' and ./run.sh rce-gate.
============================================================
Autres sous-commandes :
./run.sh up # just start + wait until healthy
./run.sh rce-gate # honest exec-bit-gate demo (below)
./run.sh down # docker compose down -v
Les serveurs vulnérable et corrigé répondent tous deux à la publication malveillante par
HTTP 200 {"status":"processing"} — le fichier est écrit plus tard, dans le worker de finalisation. Le statut HTTP
n'est donc pas un oracle. exploit/poc.py --verify-docker <container> interroge à la place, à l'intérieur
du conteneur, l'apparition du fichier contrôlé au chemin traversé et compare son SHA-1 :
/var/tmp/CVE_2026_10053_PROOF-1.0.0.tgz apparaît (propriétaire git:git, nos octets) → code de sortie 0.Gitlab::PathTraversal::PathTraversalAttackError: Invalid path → code de sortie 2.L'exploit lui-même n'est rien d'autre que la requête authentifiée PUT …/packages/npm/:pkg ; le path traversal
réside entièrement dans le corps JSON name (file_name = "#{name}-#{version}.tgz", uniquement contrôlé non vide).
Les appels docker exec relèvent de la vérification et de la mise en place, jamais de la capacité de l'attaquant.
git. Les fichiers sont créés en 0644.-<semver>.tgz (le NUL est
rejeté par le noyau, le retour à la ligne ne tronque pas), donc aucune cible à nom exact (secrets.yml,
authorized_keys, un .rb, binaires gitaly) ne peut être écrasée. Le seul exécuteur indépendant du nom,
custom_hooks/<hook>.d/, exécute n'importe quel nom de fichier mais seulement s'il est exécutable — et cette
écriture est en 0644. Écraser un fichier 0755 préexistant n'aide pas : le store remplace l'inode et le
réinitialise à 0644. La RCE nécessite donc une primitive distincte de bit exécutable / d'exécution que ce PoC
ne fournit pas../run.sh rce-gate démontre exactement cela de manière honnête : il place un hook pre-receive.d via le
path traversal, pousse, et montre que le hook 0644 ne s'exécute pas. ./rce_gate_demo.sh --illustrate-gate
définit en plus +x via docker exec chmod (une action docker-root hors bande, pas une capacité de
l'attaquant) uniquement pour montrer que gitaly l'exécuterait — soulignant que le levier manquant est le bit
exécutable.
Voir ANALYSIS.md pour l'analyse complète. En bref : app/uploaders/gitlab_uploader.rb
ne validait le chemin de stockage que before :cache ; au before :store, le file_name dérivé du modèle
(contrôlé par l'attaquant, non validé) était écrit tel quel. Le correctif ajoute before :store, :protect_from_path_traversal!.
docker-compose.yml vuln (19.2.1) + patched (19.2.2) GitLab CE
run.sh one-command harness with deterministic file oracle + PASS/FAIL
provision.rb lab setup: mint root PAT + create project (NOT part of the exploit)
exploit/poc.py the PoC sender + --verify-docker oracle
rce_gate_demo.sh honest exec-bit-gate demonstration