
Воспроизводимый лабораторный стенд для CVE-2026-10053 (обход пути в реестре npm-пакетов GitLab -> произвольная запись файлов от имени git). Уязвимая версия 19.2.1 против исправленной 19.2.2, детерминированный оракул.
Автономная лаборатория, которая доказывает обход пути в npm-реестре пакетов GitLab (CVE-2026-10053), и показывает, что исправление его блокирует, используя детерминированный файловый оракул, а не HTTP-статус.
Что доказано, а что нет. Эта лаборатория доказывает аутентифицированную запись произвольных файлов от имени системного пользователя
gitна уязвимой версии GitLab, а также то, что пропатченный релиз блокирует её. Это не самостоятельный PoC удалённого выполнения кода — см. Scope. Не ссылайтесь на неё как на RCE.
| Уязвимость | CWE-22 обход пути в npm-реестре пакетов (TOCTOU: проверка выполнялась в before :cache, а не в before :store) |
| CVSS | 8.5 High — 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 "Re-validate upload path traversal before store" |
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"} — файл записывается позже, в финализирующем воркере. Поэтому HTTP-статус
не является оракулом. Вместо этого exploit/poc.py --verify-docker <container> опрашивает внутри
контейнера наличие управляемого файла по пути обхода и сравнивает его SHA-1:
/var/tmp/CVE_2026_10053_PROOF-1.0.0.tgz (владелец git:git, наши байты) → exit 0.Gitlab::PathTraversal::PathTraversalAttackError: Invalid path → exit 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 через обход пути, делает push и показывает, что хук с правами 0644 не выполняется. ./rce_gate_demo.sh --illustrate-gate дополнительно устанавливает +x через docker exec chmod (внешнее действие 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