
재현 가능한 CVE-2026-10053 (GitLab npm 패키지 레지스트리 경로 탐색 -> git 권한으로 임의 파일 쓰기) 실습 환경. 취약한 19.2.1 vs 패치된 19.2.2, 결정론적 오라클.
| 취약점 | npm 패키지 레지스트리의 CWE-22 경로 탐색 (TOCTOU: 검사가 before :cache에서는 실행되었으나 before :store에서는 실행되지 않음) |
| CVSS | 8.5 높음 — AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H |
| 영향받는 버전 | GitLab CE/EE 18.8 → <19.0.6, 19.1 → <19.1.4, 19.2 → <19.2.2 |
| 수정 버전 | 19.0.6 / 19.1.4 / 19.2.2 — 커밋 435cf863 "저장 전 업로드 경로 탐색 재검증" |
python3, git, curl 필요../run.sh # up + provision + exploit + verify BOTH; exits 0 iff vuln writes AND patched blocks
예상 결과:
================ 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.
============================================================
기타 하위 명령:
./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
취약 서버와 패치된 서버 모두 악의적인 게시에 대해 다음으로 응답합니다.
HTTP 200 {"status":"processing"} — 파일은 나중에 finalize 작업자에서 기록됩니다. 따라서 HTTP
상태는 오라클이 아닙니다. exploit/poc.py --verify-docker <container>는 대신 컨테이너
내부에서 탐색된 경로의 제어된 파일을 폴링하고 SHA-1을 비교합니다:
/var/tmp/CVE_2026_10053_PROOF-1.0.0.tgz 파일이 나타남 (소유자 git:git, 우리가 보낸 바이트) → 종료 코드 0.Gitlab::PathTraversal::PathTraversalAttackError: Invalid path를 기록 → 종료 코드 2.익스플로잇 자체는 인증된 PUT …/packages/npm/:pkg 요청에 불과합니다; 경로 탐색은
전적으로 JSON 본문의 name에 있습니다 (file_name = "#{name}-#{version}.tgz", 공백만 검사됨).
docker exec 호출은 검증 및 설정일 뿐이며, 공격자의 능력의 일부가 아닙니다.
git이 쓸 수 있는 경로에 기록합니다. 파일은 0644로 저장됩니다.-<semver>.tgz로 끝나야 합니다 (NUL은 커널이 거부하고,
개행 문자는 잘리지 않습니다). 따라서 정확한 이름의 대상(secrets.yml, authorized_keys,
.rb 파일, gitaly 바이너리)을 덮어쓸 수 없습니다. 유일한 파일 이름 무관 실행기인
custom_hooks/<hook>.d/는 모든 파일 이름을 실행하지만 실행 가능한 경우에만 실행됩니다 — 그리고 이 쓰기는 0644입니다.
기존 0755 파일을 덮어써도 도움이 되지 않습니다: 저장소가 inode를 교체하여
0644로 재설정합니다. 따라서 RCE에는 이 PoC가 제공하지 않는 별도의 실행 비트 / 실행 프리미티브가 필요합니다../run.sh rce-gate는 바로 이것을 정직하게 보여줍니다: 경로 탐색을 통해 pre-receive.d 훅을 설치하고,
푸시한 다음 0644 훅이 실행되지 않음을 보여줍니다. ./rce_gate_demo.sh --illustrate-gate는
추가적으로 docker exec chmod를 통해 +x를 설정합니다 (대역 외 docker-root 작업이며, 공격자
능력이 아님) 순전히 gitaly가 실행했을 것임을 보여주기 위한 것입니다 — 누락된 지렛대가 실행 비트임을 강조합니다.
전체 분석은 ANALYSIS.md를 참조하십시오. 요약하면: app/uploaders/gitlab_uploader.rb는
저장 경로를 before :cache에서만 검증했습니다. before :store에서는 모델에서 파생된 file_name이
(공격자가 제어하며 검증되지 않음) 그대로 기록되었습니다. 수정은 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