
Lab riproducibile per la CVE-2026-10053 (path traversal del package-registry npm di GitLab -> scrittura arbitraria di file come git). 19.2.1 vulnerabile vs 19.2.2 patchata, oracolo deterministico.
Laboratorio autocontenuto che dimostra il path traversal del registro pacchetti npm di GitLab (CVE-2026-10053) e ne mostra la correzione, usando un oracolo deterministico basato su disco — non sullo stato HTTP.
Cosa è e cosa non è dimostrato. Questo laboratorio dimostra scrittura arbitraria di file autenticata come utente di sistema
gitsu GitLab vulnerabile e che la release corretta la blocca. Non è un proof-of-concept autonomo di esecuzione remota di codice — vedi Scope. Non citarlo come RCE.
| Vulnerabilità | CWE-22 path traversal nel registro pacchetti npm (TOCTOU: il controllo veniva eseguito before :cache, non before :store) |
| CVSS | 8.5 High — AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H |
| Versioni affette | GitLab CE/EE 18.8 → <19.0.6, 19.1 → <19.1.4, 19.2 → <19.2.2 |
| Corretta | 19.0.6 / 19.1.4 / 19.2.2 — commit 435cf863 "Re-validate upload path traversal before store" |
python3, git, curl sull'host../run.sh # up + provision + exploit + verify ENTRAMBI; esce 0 sse la scrittura vulnerabile avviene E la patch blocca
Risultato atteso:
================ 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.
============================================================
Altri sottocomandi:
./run.sh up # avvia e attendi che sia healthy
./run.sh rce-gate # demo onesta del gate sul bit di esecuzione (sotto)
./run.sh down # docker compose down -v
Sia il server vulnerabile sia quello corretto rispondono alla pubblicazione malevola con
HTTP 200 {"status":"processing"} — il file viene scritto dopo, nel worker di finalizzazione. Quindi lo stato
HTTP non è un oracolo. exploit/poc.py --verify-docker <container> invece interroga dentro il
container per verificare la presenza del file controllato nel percorso attraversato e ne confronta lo SHA-1:
/var/tmp/CVE_2026_10053_PROOF-1.0.0.tgz appare (proprietario git:git, i nostri byte) → exit 0.Gitlab::PathTraversal::PathTraversalAttackError: Invalid path → exit 2.L'exploit in sé non è altro che la richiesta autenticata PUT …/packages/npm/:pkg; la traversal
vive interamente nel campo name del body JSON (file_name = "#{name}-#{version}.tgz", sottoposto solo a controllo di vuotezza).
Le chiamate docker exec sono verifica e setup, mai parte delle capacità dell'attaccante.
git. I file finiscono con 0644.-<semver>.tgz (NUL è rifiutato dal
kernel, la newline non tronca), quindi nessun target con nome esatto (secrets.yml, authorized_keys,
un .rb, i binari di gitaly) può essere sovrascritto. L'unico esecutore indipendente dal nome,
custom_hooks/<hook>.d/, esegue qualsiasi file ma solo se eseguibile — e questa scrittura è 0644.
Sovrascrivere un file preesistente 0755 non aiuta: lo store sostituisce l'inode, riportandolo
a 0644. Quindi la RCE richiede una primitiva separata di bit di esecuzione / esecuzione che questo PoC non fornisce../run.sh rce-gate dimostra esattamente questo in modo onesto: pianta un hook pre-receive.d tramite la
traversal, fa push e mostra che l'hook 0644 non viene eseguito. ./rce_gate_demo.sh --illustrate-gate
imposta inoltre +x tramite docker exec chmod (un'azione docker-root fuori banda, non una capacità
dell'attaccante) solo per mostrare che gitaly lo eseguirebbe — sottolineando che la leva mancante è il bit di esecuzione.
Vedi ANALYSIS.md per l'analisi completa. In breve: app/uploaders/gitlab_uploader.rb
validava il percorso di storage solo before :cache; in before :store il file_name derivato dal modello
(controllato dall'attaccante, non validato) veniva scritto letteralmente. La correzione aggiunge before :store, :protect_from_path_traversal!.
docker-compose.yml GitLab CE vulnerabile (19.2.1) + corretto (19.2.2)
run.sh harness one-command con oracolo file deterministico + PASS/FAIL
provision.rb setup del laboratorio: genera root PAT + crea progetto (NON parte dell'exploit)
exploit/poc.py il sender del PoC + oracolo --verify-docker
rce_gate_demo.sh dimostrazione onesta del gate sul bit di esecuzione