Блог / Разборы CVE

CVE-2026-97229: оговорка в «неаутентифицированном» RCE GitLab

CVSS 10.0 от GitLab и запись CISA KEV называют это неаутентифицированным. Два человека, заставившие эксплойт работать, сначала нуждались в учётной записи GitLab. Вот почему это важно.

Аспирантура по информационной безопасности завершенаВнутривузовская защита пройдена · Итоговая защита диссертации впереди

Ноль учётных данных на бумаге. Одна учётная запись на практике.

Ноль. Именно столько учётных данных требует собственный вектор CVSS GitLab для CVE-2026-97229: AV:N/AC:L/PR:N/UI:N, идеальные 10.0, выполнение произвольного кода без каких-либо привилегий. Один. Именно столько учётных записей понадобилось единственным двум людям, которые реально заставили это работать, независимо друг от друга, прежде чем они смогли отправить запрос, который хоть что-то делал. Обе цифры верны. Они описывают два разных шага одной и той же атаки.

CVE-2026-97229 — критическое целочисленное переполнение в компиляторе регулярных выражений CI/CD GitLab, исправленное 12 октября 2026 года в ветках 19.2, 19.3 и 19.4.[1] Собственный бюллетень GitLab описывает именно то, что подразумевает эта оценка: специально сформированное регулярное выражение в конфигурации CI/CD, отправленное неаутентифицированным пользователем, превращается в выполнение произвольного кода на сервере при определённых условиях.[1] Менее чем через три недели CISA добавила уязвимость в каталог известных эксплуатируемых уязвимостей, подтвердив, что её используют против реальных целей.[2] Всё это верно. Просто упускается одна деталь, которая на самом деле показывает, насколько вы уязвимы: чтобы добраться до неаутентифицированного шага, сначала нужна учётная запись.

Что именно выпустила GitLab и когда

GitLab датировала релиз с исправлением 12 октября 2026 года, выпустив 19.2.8, 19.3.5 и 19.4.2 для трёх затронутых веток — 19.2 до 19.2.8, 19.3 до 19.3.5, 19.4 до 19.4.2.[1] На GitLab.com уже работала исправленная сборка до публикации бюллетеня, а клиентам GitLab Dedicated вообще ничего не требовалось делать.[1] Исследователь, сообщивший об уязвимости, использовал псевдоним joaxcar на HackerOne и отдельно сообщил о баге открытого редиректа средней степени серьёзности, CVE-2026-7767, исправленном в том же релизе.[1]

Одна деталь изменилась постфактум, и об этом стоит знать, прежде чем проверять номер своей версии. Исходные примечания к релизу GitLab указывали для ветки 19.3 версию 19.3.4; на следующий день в прессе сообщили, что эта сборка на деле содержала несвязанное исправление бага страниц merge request, а настоящим исправленным релизом для этой CVE является 19.3.5 — именно это теперь отражено в текущем бюллетене.[8] После этого события развивались быстро: Rapid7 сообщила о независимом работающем воспроизведении примерно через день после раскрытия, публичный PoC с указанием автора появился примерно через четыре дня после этого, а запись CISA о подтверждённой эксплуатации появилась примерно через семнадцать дней после исходного патча.[1][2][4][7] Это не главная тема этого материала. Но об этом стоит задуматься: CVSS 10.0 в продукте уровня GitLab вооружается быстро, как только сам патч подсказывает атакующим, куда смотреть.

Движок регулярных выражений, который выполняет вашу команду вместо сравнения со строкой

Я не воспроизводил это сам. Всё ниже основано на PoC, который Джона Берджесс (GitHub: jburgess-r7) опубликовал примерно через четыре дня после патча, явно обозначив его как «первоначальный релиз PoC», на отдельно сообщённом воспроизведении Rapid7 и на собственном бюллетене GitLab — все источники прочитаны по состоянию на 2 ноября 2026 года.[1][3][4] Триггер находится за эндпоинтом CI-lint GitLab — тем же самым, который GitLab использует для проверки файла .gitlab-ci.yml перед запуском любого пайплайна. Подайте туда специально сформированное регулярное выражение в конфигурации CI/CD вместе с веткой репозитория, путь которой использует определённый трюк с UTF-8-именем, и собственный учёт длины в компиляторе регулярных выражений переполняется.[3]

Это переполнение не просто обрушивает воркер, хотя часто делает и это. Оно перенаправляет выполнение движка регулярных выражений дальше — прямо в функцию, которая запускает произвольную команду.[3] Команда выполняется от имени низкопривилегированной служебной учётной записи GitLab «git», а не администратора и не root на хосте.[3] Это реальный предел для масштаба ущерба, и об этом стоит сказать прямо: это выполнение кода на стороне сервера, а не мгновенный путь к полному администраторскому контролю. Но из-под шелла «git» атакующему уже есть что взять — начиная с содержимого репозиториев и любых секретов CI, которые доступны этой учётной записи.

// The CI-lint trigger, reconstructed from Burgess's public PoC. Paraphrased
// shape of the chain -- not the working payload itself.

1. Attacker-controlled branch is pushed to a public project, with a
   specially-named (UTF-8) path and a crafted regex in .gitlab-ci.yml.
2. An unauthenticated request hits GitLab's CI-lint endpoint, which
   validates that regex before any pipeline actually runs.
3. The regex compiler's internal length accounting overflows -- an
   integer wraps where it shouldn't.
4. Execution inside the regex engine is redirected into a function that
   runs an OS command, as the low-privilege "git" service account.
5. On a vulnerable build, the worker process frequently crashes right
   after (a bare HTTP 502 is a side effect, not proof by itself).

Шаг, который происходит до неаутентифицированного

Вот деталь, которую упускает каждый заголовок про CVSS 10. Собственный README Берджесса прямо называет предварительные условия: обычная учётная запись, способная запушить ветку в публичный проект на целевом инстансе; цель, работающая на точно официальной сборке gitlab/gitlab-ce:19.4.1-ce.0 (для любой другой нужны пересобранные бинарные смещения); примерно 9 ГиБ свободной памяти; и сетевая доступность до слушателя, если нужен вывод команды.[3] Сам запрос к CI-lint, который и вызывает переполнение, не требует ни токена, ни куки, ни заголовка авторизации — вот эта часть действительно неаутентифицирована.[3][4] Просто это не первый шаг.

По данным Help Net Security, Rapid7 независимо воспроизвела рабочее выполнение кода на неисправленном официальном образе GitLab CE 19.4.1 примерно через день после раскрытия (тот же самый ввод не сработал на исправленном образе 19.4.2) и описала тот же барьер: «обычная учётная запись», пушащая сформированную ветку, применимо «на самоуправляемых инстансах, которые разрешают создание публичных проектов».[4] Две независимые стороны, работавшие отдельно, пришли к одному и тому же предварительному условию. Отмечу то, чего запись на самом деле не утверждает, потому что объединить их соблазнительно: у GitHub-аккаунта автора PoC суффикс «-r7», а собственный README ссылается на «технический анализ Rapid7» этой же уязвимости. Ни один источник не утверждает эту связь напрямую, поэтому я рассматриваю PoC Берджесса и воспроизведение Rapid7 так, как это фактически показывает запись: как два независимо полученных подтверждения одного и того же условия, а не одно.

Автор PoC также честно говорит об ограничениях: 10 из 10 попыток успешны на той одной протестированной сборке с включённым ASLR, тот же самый ввод возвращает false на исправленном компиляторе 19.4.2, и автор прямо утверждает, что «анализ других затронутых конфигураций и достижимых путей продолжается», а сам PoC «не доказывает реальную эксплуатацию во вторжениях».[3] Успешный триггер часто возвращает просто HTTP 502, потому что воркер обрушивается сразу после выполнения команды — так что один лишь 502, без остального паттерна, ничего не доказывает.[3] Держите это различие в уме. Оно важно для того, что вы на самом деле можете понять из собственных логов.

Насколько далеко на самом деле простирается «неаутентифицированность»

Так что же это: неаутентифицированная уязвимость или нет? И то, и другое — зависит от того, о какой границе вы спрашиваете. Вектор CVSS от GitLab оценивает уязвимый компонент как PR:N, привилегии не требуются, и это точно для того, что он действительно оценивает: сам запрос к CI-lint не проверяет никаких учётных данных.[1] Но «компонент не требует аутентификации» и «цепочка атаки не требует аутентификации» — это два разных утверждения, и единственные две названные стороны, которые реально заставили это работать на практике, Берджесс и Rapid7, независимо сходятся на том, что перед этим запросом в цепочке есть шаг: учётная запись, способная запушить ветку в публичный проект.[3][4] Оценка серьёзности от GitLab не ошибочна. Она просто отвечает на более узкий вопрос, чем подразумевает заголовок.

Насколько это предварительное условие тривиально или является настоящей стеной, зависит от настроек, о которых большинство администраторов не думают как о мерах защиты против этой CVE. Собственная документация GitLab говорит, что саморегистрация по умолчанию открыта для любого посетителя на самоуправляемом инстансе, и рекомендует отключать её на всём, что обращено в интернет и не предполагает открытой регистрации.[5] Также там сказано, что одобрение администратором новых регистраций является настройкой по умолчанию для новых инстансов начиная с GitLab 13.6 — то есть достаточно современный и правильно настроенный инстанс уже имеет человека в цепочке, прежде чем незнакомец получит учётную запись.[5] Видимость проектов и то, кому разрешено создавать публичный проект, отдельно настраивается администратором.[5] Ничто из этого не патч. Но всё это меняет, сколько незнакомцев вообще могут добраться до неаутентифицированной половины этой уязвимости.

// For a stranger to turn this into RCE against your instance,
// three things have to be true at once:

1. Your instance allows self-registration, OR the attacker already
   holds any ordinary account on it.
2. That account (or self-registration) can create, or push a branch
   to, a PUBLIC project -- not just a private one.
3. The instance is still running an unpatched build
   (before 19.2.8 / 19.3.5 / 19.4.2) and is reachable from wherever
   the attacker is.

Close registration, restrict who can make projects public, or patch --
any one of the three breaks the chain. Patching is still the only fix
that doesn't depend on a setting staying the way you left it.

Эксплуатация подтверждена. Почти всё остальное — нет.

CISA добавила CVE-2026-97229 в каталог известных эксплуатируемых уязвимостей 29 октября 2026 года под названием «GitLab Community Edition and Enterprise Edition Integer Overflow or Wraparound Vulnerability», с обязательным сроком устранения 1 ноября по директиве BOD 26-04.[2] Эта дата добавления — не утверждение о том, когда эксплуатация реально началась. Записи KEV регулярно отстают от реального первого использования, и ни один источник здесь не говорит, когда атаки против этой уязвимости действительно начались. Читайте «29 октября» как дату, когда CISA подтвердила это, а не как отметку времени на самих атаках.

Вот честный масштаб того, что означает «подтверждённая эксплуатация» в этой записи, и здесь стоит быть точным, потому что запись тоньше, чем предполагает заголовок: ни один источник, рассмотренный для этого материала — ни собственная запись CISA, ни бюллетень GitLab, ни материал BleepingComputer об этом — не публикует индикаторы компрометации, названную жертву или какое-либо описание техники наблюдаемых атак на уровне методов.[1][2][6] Материал BleepingComputer ссылается на собственные релизы обнаружения угроз от GitLab и подсчёт Shadowserver — примерно 2800 доступных из интернета инстансов GitLab CE — и это весь доступный публичный уровень детализации.[6] «Подтверждённая эксплуатация» здесь опирается на запись CISA как на названный авторитетный источник, а не на доказательства атак, которые этот материал мог бы проверить самостоятельно. Это законное основание для такого определения. Но это и предел того, что я могу честно сказать вам о самих атаках.

Патч, правила и что проверить на этой неделе

Исправление имеет точные версии: 19.2.8, 19.3.5 и 19.4.2, все выпущены 12 октября 2026 года.[1] Если ваша ветка 19.3 показывает 19.3.4 — эта сборка исправила несвязанный баг страниц merge request, а не эту CVE; нужна именно 19.3.5.[8] Вместе с патчем GitLab выпустила шесть новых правил обнаружения угроз для самоуправляемых инстансов, названных именно для этой проблемы: Onigmo native worker crash, GraphQL cilint hostile regexp, ci regexp oversized non-ascii path, ci regexp guard rejection, ci lint workhorse worker crash и ci lint scripted client error burst.[1] Если ваш собственный мониторинг или обнаружения GitLab покажут что-то из этого списка — считайте это сигналом, который стоит отследить, а не шумом.

Собственная политика раскрытия информации GitLab публикует полные детали в трекере задач для исправленной уязвимости через 90 дней после релиза с исправлением, что по простой арифметике выводит на примерно середину января 2027 года всё, что выходит за рамки текста бюллетеня — это не дата, которую называет сама GitLab, а просто то, что получается из их собственного правила 90 дней.[1] До этого момента вот что реально поддерживает публичная запись для проверки.

Patch and verify, in order:
  [ ] Confirm the build actually RUNNING, not just what the changelog
      says: 19.2.8 / 19.3.5 / 19.4.2 or later.
  [ ] If self-managed, review sign-up restrictions: is self-registration
      open, and is admin approval of new accounts actually enabled?
  [ ] Review who can create or push to a PUBLIC project on your instance
      -- that setting is the other half of this bug's real exposure.
  [ ] Enable and watch GitLab's six named threat detections for this CVE,
      especially "ci lint workhorse worker crash" and "ci regexp guard
      rejection".
  [ ] Check CI-lint access logs for requests with no auth header paired
      with recently-pushed branches on public projects -- that pairing
      is this chain's fingerprint, a bare HTTP 502 is not.

Оценка CVSS оценивает уязвимость. Она не оценивает ваш инстанс.

CVE-2026-97229 — настоящая критическая уязвимость, и оценки серьёзности от GitLab и CISA обе верны. Чего одна цифра сама по себе не скажет — так это открыт ли ваш инстанс настолько, что эта цепочка атаки была построена именно под него, или это инстанс, где регистрация закрыта, а видимость проектов заблокирована. Второй вариант инстанса — заметно меньшая цель, даже для уязвимости с CVSS 10.0, которая активно эксплуатируется. Оценка серьёзности оценивает компонент. Экспозиция — это свойство вашей конфигурации, и никто не оценивает её за вас.

Именно этот разрыв я ищу при разборе веб-приложения или пайплайна CI/CD: не просто «пропатчено ли это», а «учитывая, как инстанс реально настроен, что неаутентифицированная половина конкретной уязвимости на самом деле даёт атакующему здесь». Если вы используете самоуправляемый GitLab или любую поверхность CI/CD с публично доступными частями, и предпочли бы, чтобы кто-то прошёл эту границу вместе с вами, а не просто доверились слову оценки CVSS, — это именно та проверка, которую я делаю. Расскажите, как реально настроен ваш инстанс, и мы определим объём работ.

Источники

Нужна такая проверка для вашего продукта?

Пентест веб-приложений и API

Хотите сами научиться находить такие уязвимости?

Курс пентеста веб-приложений, индивидуально
Все статьи

08Контакт

Выпускаете продукт с данными пользователей?

Пришлите короткое описание. Я отвечу в течение трёх дней вопросами или наброском предложения, а стоимость согласуем письменно до начала работ.