flawopen.com/Incidents/gitlab-commits-api-path-traversal-september-2026
CVE-2026-85706: раскрытие файлов через GitLab
Парсер загрузок GitLab позволял без аутентификации выбирать файлы сервера; неверные escape-последовательности раскрывали фрагменты в ошибках.[1], [3]
Представьте сотрудника приёмной: вместо документа из лотка он принимает номер шкафа, написанный посетителем. Не проверив пропуск, сотрудник открывает шкаф; если строка не читается, он произносит её вслух в объяснении ошибки. Открыть документ — одна ошибка, повторить его содержимое — другая.[1]
Ключевые понятия и термины
Upload metadata- Переданные клиентом file.path и metadata.path не должны подменять буфер, зарегистрированный доверенным обработчиком загрузок.[1]
Percent decoding- При разборе формы знак % с двумя шестнадцатеричными цифрами декодируется; неверная последовательность может вызвать ошибку с проблемным фрагментом.[1]
CWE-22 / CVSS- Регистратор CNA относит
CVE-2026-85706к обходу пути, CVSS 10.0, критическая тяжесть. Нарушенная граница — выбор серверного файла по воле клиента.[3]
Связи со стандартами
- Ссылка CWE для слабого места, описанного в этом руководстве: CWE-22
- CISA KEV — добавлено 2026-09-11 — CVE-2026-85706
Хронология инцидента
GitLab выпустил исправления 19.1.8, 19.2.6 и 19.3.2.[2]
В бюллетень добавили обратные порты 18.11.12 и 19.0.9.[2]
Пошаговый сценарий атаки
Выбор до проверки доступа
Метаданные загрузки могли заменить ожидаемый временный буфер путём к файлу ещё до аутентификации.[1]
Файл как параметры формы
При application/x-www-form-urlencoded содержимое выбранного файла разбиралось как форма, а не возвращалось обычным ответом на commit.[1]
Фрагмент в ошибке
Неверное процентное кодирование могло попасть в текст ошибки. Разделители & и = ограничивали фрагмент; само чтение не доказывает раскрытие.[1]
Расследование
Свяжите записи Rails API и Workhorse по correlation_id; сравните api_error с written_bytes, чтобы определить, были ли байты отправлены в ответе.[1]
Исходный код: Уязвимый vs Защищённый вариант
Учебная модель на Python, не исходный код и не патч GitLab. Уязвимый пример использует только буферы в памяти и считает любой % ошибкой; точная проверка и выбор фрагмента здесь не воспроизводятся. Исправленная модель сначала аутентифицирует, отклоняет метаданные пути, принимает только байты доверенной загрузки, ограничивает размер и выдаёт общие ошибки. Установите обновление производителя: проверка внутри маршрута запоздала, если файл уже прочитан промежуточным компонентом.[1]
from urllib.parse import unquote_to_bytes
def parse_upload(authenticated, metadata, buffers):
# Безопасные фикстуры в памяти; доступа к файловой системе нет.
body = buffers.get(metadata.get("path"), b"")
if b"%" in body:
return 400, body.decode("ascii", errors="replace")
if not authenticated:
return 401, "Unauthorized"
return 200, unquote_to_bytes(body)
from urllib.parse import unquote_to_bytes
MAX_BYTES = 4096
HEX = frozenset(b"0123456789abcdefABCDEF")
def parse_upload(authenticated, metadata, registered_body):
# registered_body поступает из доверенного middleware загрузки,
# а не из пути, выбранного параметрами запроса.
if not authenticated:
return 401, "Unauthorized"
if metadata or type(registered_body) is not bytes:
return 400, "Invalid upload"
if len(registered_body) > MAX_BYTES:
return 413, "Upload too large"
for i, byte in enumerate(registered_body):
if byte == 37 and (i + 2 >= len(registered_body) or
registered_body[i+1] not in HEX or
registered_body[i+2] not in HEX):
return 400, "Invalid upload"
return 200, unquote_to_bytes(registered_body)
Чек-лист по защите системы для инженеров
- ✓Обновитесь до поддерживаемой исправленной версии. Пороговые версии веток: 18.11.12, 19.0.9, 19.1.8, 19.2.6 и 19.3.2; затронуты выпуски начиная с 18.7. Сверьте установленную версию с бюллетенем.[2], [3]
- ✓Проверяйте анонимные запросы безвредными маркерами в памяти: до аутентификации нет обращения к буферу, а маркер не появляется в ответах и обычных журналах. Отклоняйте метаданные пути до разбора.[1]
- ✓Сохраните журналы Rails API и Workhorse; связывайте записи по
correlation_id, затем сравнивайтеapi_errorиwritten_bytes. Проверяйте декодированные file.path и metadata.path, не удаляя всю строку из-за безобидного пути загрузки. Само чтение не доказывает утечку: по данным GitLab, отражённый фрагмент в api_error, подтверждённый полем Workhorse written_bytes, указывает на отправленные клиенту байты; одного HTTP-статуса недостаточно.[1] - ✓Меняйте только учётные данные, которые были раскрыты или могли быть раскрыты с достаточными основаниями. Если могли быть затронуты
db_key_baseилиotp_key_base, перед ротацией обратитесь в поддержку GitLab: GitLab указывает, что дляdb_key_baseсейчас нет поддерживаемого способа ротации, а неподдерживаемая смена ключей может привести к потере данных. Отсутствие журналов не доказывает отсутствие раскрытия.[1]