
Laboratório reproduzível para CVE-2026-10053 (path traversal no registro de pacotes npm do GitLab -> escrita arbitrária de arquivos como git). Versão vulnerável 19.2.1 vs corrigida 19.2.2, oráculo determinístico.
Laboratório autocontido que prova o path traversal do registro de pacotes npm do GitLab (CVE-2026-10053) e mostra que está corrigido, usando um oráculo determinístico em disco — não o status HTTP.
O que é e o que não é provado. Este laboratório prova escrita arbitrária de arquivos autenticada como o usuário do sistema
gitem GitLab vulnerável, e que a versão corrigida a bloqueia. Não é um PoC autônomo de execução remota de código — veja Escopo. Não o cite como RCE.
| Vulnerabilidade | CWE-22 path traversal no registro de pacotes npm (TOCTOU: a verificação rodava before :cache, não before :store) |
| CVSS | 8.5 Alto — AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H |
| Afetados | GitLab CE/EE 18.8 → <19.0.6, 19.1 → <19.1.4, 19.2 → <19.2.2 |
| Corrigido | 19.0.6 / 19.1.4 / 19.2.2 — commit 435cf863 "Revalidar path traversal do upload antes do store" |
python3, git, curl no host../run.sh # up + provision + exploit + verify BOTH; exits 0 iff vuln writes AND patched blocks
Resultado esperado:
================ 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.
============================================================
Outros subcomandos:
./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
Tanto o servidor vulnerável quanto o corrigido respondem à publicação maliciosa com
HTTP 200 {"status":"processing"} — o arquivo é gravado depois, no worker finalize. Portanto, o status
HTTP não é um oráculo. exploit/poc.py --verify-docker <container> em vez disso faz polling dentro
do contêiner pelo arquivo controlado no caminho atravessado e compara seu SHA-1:
/var/tmp/CVE_2026_10053_PROOF-1.0.0.tgz aparece (dono git:git, nossos bytes) → exit 0.Gitlab::PathTraversal::PathTraversalAttackError: Invalid path → exit 2.O exploit em si nada mais é do que a requisição autenticada PUT …/packages/npm/:pkg; o traversal
vive inteiramente no campo name do corpo JSON (file_name = "#{name}-#{version}.tgz", somente
verificado se está em branco). As chamadas docker exec são de verificação e configuração, nunca
parte da capacidade do atacante.
git. Os arquivos ficam com 0644.-<semver>.tgz (NUL é
rejeitado pelo kernel, quebra de linha não trunca), então nenhum alvo de nome exato (secrets.yml,
authorized_keys, um .rb, binários do gitaly) pode ser sobrescrito. O único executor que não
depende do nome do arquivo, custom_hooks/<hook>.d/, executa qualquer nome de arquivo mas somente
se for executável — e esta gravação é 0644. Sobrescrever um arquivo 0755 preexistente não
ajuda: o store substitui o inode, redefinindo-o para 0644. Então RCE exige uma primitiva separada
de bit de execução / execução que este PoC não fornece../run.sh rce-gate demonstra exatamente isso de forma honesta: planta um hook pre-receive.d via o
traversal, faz push e mostra que o hook 0644 não executa. ./rce_gate_demo.sh --illustrate-gate
adicionalmente define +x via docker exec chmod (uma ação docker-root fora de banda, não uma
capacidade do atacante) apenas para mostrar que o gitaly executaria — reforçando que a alavanca que
falta é o bit de execução.
Veja ANALYSIS.md para a análise completa. Em resumo: app/uploaders/gitlab_uploader.rb
validava o caminho de armazenamento apenas before :cache; em before :store, o file_name derivado
do modelo (controlado pelo atacante, não validado) era gravado literalmente. A correção adiciona
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