Уязвимость Ruby в GitLab: Alpine пересобрала сервер после атаки

Безопасность

Уязвимость Ruby в GitLab: Alpine пересобрала сервер после атаки

Уязвимость Ruby в GitLab: Alpine Linux пересобрала сервер после атаки через CVE-2026-97229

28 октября Alpine Linux сообщила, что неизвестный пытался взломать её GitLab через CVE-2026-97229 (CVSS 10,0), а обновление до GitLab 19.3.6 сервер не защитило. Ошибка находится в Onigmo, движке регулярных выражений Ruby, поэтому уязвимость Ruby в GitLab остаётся у всех, кто собирает GitLab сам или берёт сторонний образ. Следов взлома Alpine не нашла, но признала сервер скомпрометированным и пересобрала его с нуля.

Коротко

  • Номер версии GitLab не гарантирует защиты от CVE-2026-97229: самосборным установкам и сторонним образам нужен Ruby 3.3.13, 3.4.12, 4.0.9 или новее.
  • Бюллетень GitLab задним числом сменил исправленный релиз ветки 19.3 с 19.3.4 на 19.3.5; оставшимся на 19.3.4 пора переходить на 19.3.6.
  • Чистые журналы и отсутствие падений не доказывают, что взлома не было, поэтому Alpine признала сервер скомпрометированным и пересобрала его.
  • Публичный эксплойт требует записи в публичный проект, а открытая регистрация этот барьер снимает.
  • Пакеты и minirootfs Alpine сверены с записями вне GitLab, ключи подписи на сервере не хранились.

Коротко

  • 28 октября Alpine Linux сообщила о попытке взлома gitlab.alpinelinux.org, сервера с репозиторием aports, из которого собирают пакеты дистрибутива.
  • Атакующий пытался эксплуатировать CVE-2026-97229 (CVSS 10,0) с недавно зарегистрированной учётной записи.
  • 20 и 21 октября его CI-конфигурации восемь раз уронили рабочие процессы GitLab.
  • Обновление до 19.3.6 не помогло: ошибка в Onigmo внутри Ruby, а образ Alpine работал на стандартном Ruby без исправления.
  • Признаков выполнения кода нет, но успешная атака следов не оставляет. Сервер пересобрали, ключи GitLab заменили, пароли всех учётных записей сбросили.
  • Пакеты Alpine сверили с записями вне GitLab, ключи подписи на сервере никогда не хранились.

Как развивалась атака на gitlab.alpinelinux.org

Речь о сервере gitlab.alpinelinux.org, где размещён aports: репозиторий со сборочными рецептами, из которого получаются пакеты дистрибутива. На Alpine построено огромное число контейнерных образов, так что цель выбрана со смыслом. Здесь код превращается в артефакты.

  • 12 октября. GitLab выпускает исправление CVE-2026-97229 и в бюллетене безопасности называет исправленной для ветки 19.3 версию 19.3.4.
  • 13 октября. Rapid7 публикует действующий эксплойт, продемонстрированный в лабораторных условиях. В тот же день Alpine обновляет сервер до 19.3.4, а не до 19.3.5. Позже сама Alpine назовёт это ошибкой.
  • 20 октября. Недавно зарегистрированная учётная запись начинает прощупывать сервер и присылает специально составленные CI-конфигурации.
  • 21 октября. Основная атака: пользователи видят ошибки 502, CI-задания срываются. За два дня рабочие процессы GitLab (воркеры) падают восемь раз.
  • 22 октября. Alpine переходит на 19.3.6. Уязвимость остаётся.
  • 23 октября. Учётную запись блокируют.
  • 25 и 26 октября. Атакующий пытается вернуться, оба раза безуспешно. 26-го Alpine обнаруживает, что сервер всё ещё уязвим.
  • 27 октября. GitLab закрывают для всех, кроме разработчиков и сборочных серверов, и начинают пересборку.
  • 28 октября. Пересборка закончена, Alpine публикует сообщение об инциденте. Перед открытием для всех сервер тестируют.

Между бюллетенем GitLab и публичным эксплойтом прошли сутки, первое прощупывание Alpine зафиксировала на восьмой день. Неделя, в течение которой эксплойт уже лежит в открытом доступе, для атакующего срок вполне достаточный. Для небольшой инфраструктурной команды open-source проекта это впритык.

Атакующий действовал прицельно. По данным Alpine, в четырёх из восьми падений он проходил часть цепочки эксплуатации и спотыкался на следующем шаге. Он также скачал образ GitLab, который публикует Alpine, и это, по словам проекта, «говорит о том, что эксплойт подгоняли под нашу конкретную сборку».

Снаружи всё выглядело как обычная нестабильность: 502, сорванные задания, перезапуски процессов. Такие сбои легко списать на нагрузку или неудачный коммит, поэтому их нужно разбирать, а не просто перезапускать сервис.

Уязвимость Ruby в GitLab: почему 19.3.6 не помог

Ошибка находится не в коде GitLab, а в Onigmo, движке регулярных выражений, встроенном в Ruby. GitLab компилирует регулярные выражения из CI-конфигураций средствами Ruby, так что специально составленное выражение попадает прямо в уязвимый движок. Патчем к приложению такое не лечится, менять нужно рантайм.

12 октября GitLab закрыл уязвимость, пропатчив Ruby внутри своего Linux-пакета и официальных образов. Исправленные версии самого Ruby вышли только 22 и 23 октября. Alpine запускала GitLab на собственном образе со стандартным Ruby: номер GitLab рос, а интерпретатор оставался прежним, с уязвимым Onigmo внутри.

Сверху наложилась путаница с номерами. Бюллетень от 12 октября называл исправлением для ветки 19.3 версию 19.3.4. Сейчас там значится 19.3.5, а 19.3.4 перечислена среди уязвимых. Вчерашний «безопасный» релиз задним числом стал опасным, и серверам, которые на нём остались, нужно переходить на 19.3.6.

Документация тоже не подсказывает. По состоянию на 29 октября ни одна из публикаций GitLab о патч-релизах 12 и 21 октября не просит администраторов самосборных установок обновить Ruby. Руководство по установке из исходников по-прежнему называет минимальной версию Ruby 3.2, хотя эта ветка в апреле дошла до конца поддержки и исправления не получит.

Из практики: в таблице активов у заказчиков почти всегда стоит «GitLab 19.x» и ни слова о Ruby или OpenSSL внутри образа. Сканер, который сверяет с базой CVE только версию приложения, покажет зелёный статус. Если вы собираете GitLab сами или берёте образ сообщества, за рантайм отвечаете вы, и эту обязанность нужно закрепить за конкретным человеком.

Почему сервер признали скомпрометированным без следов взлома?

Потому что следов могло и не остаться. Alpine не нашла признаков выполнения кода: замеры времени ответа не показали задержек, неожиданных файлов, ключей, хуков и процессов нет, через видимые проекту веб- и API-запросы приватные данные к атакующему не уходили. Но, по словам Alpine, «успешный эксплойт не оставил бы ни падения, ни следа в журналах веб-сервера».

Восемь падений воркеров говорят о сбоях, а не о выполнении кода. Верно и обратное: отсутствие сбоя не значит, что атака провалилась. Успешная попытка, если она была, в статистику падений просто не попала.

Проверить это уже нечем. Пропала часть журналов за день атаки, включая журналы фоновых задач, и файловая система старого контейнера. Исходящие сетевые соединения не журналировали ни Alpine, ни её хостинг-провайдер.

Оставалось два пути: считать сервер чистым, раз взлом не доказан, или скомпрометированным, раз не доказано обратное. Alpine выбрала второе. Сервер пересобрали с нуля, секретные ключи GitLab заменили, пароли всех учётных записей сбросили. Новый сервер работает на Ruby 3.3.13 или новее.

При успешной атаке под угрозой оказались бы:

  • email, имена и IP-адреса всех пользователей;
  • хеши паролей;
  • секреты 2FA, переменные CI/CD и токены runner;
  • учётные данные webhook, интеграций и push-зеркал;
  • персональные токены доступа (хранятся в виде хешей);
  • приватные репозитории, конфиденциальные issues и merge requests.

Заменила ли Alpine токены runner, переменные CI/CD, токены доступа и учётные данные интеграций, проект пока не сообщил. Сброс паролей токены не отзывает, а именно токен злоумышленник оставил бы себе для тихого возвращения.

Для сборочной инфраструктуры решение о пересборке верное: она стоит времени, а пропущенный бэкдор может стоить доверия ко всему дистрибутиву. Регулярная оценка уязвимостей, которая учитывает версии рантайма, а не только приложений, показала бы проблему раньше атакующего.

Кто стоит за атакой, неизвестно: Alpine ни с какой группой атакующего не связывает. Удалось ли ему выполнить код хотя бы раз, с пропавшими журналами, скорее всего, уже не выяснить.

Затронуты ли пакеты и Docker-образы Alpine?

Доказательств этого нет, и Alpine это проверила. Каждое изменение aports с 20 по 22 октября пришло через зафиксированный push известного разработчика. Сверяли с записями, до которых атакующий, по оценке Alpine, добраться не мог: копией репозитория у одного из разработчиков и всеми 45 сборочными серверами.

Все пакеты, собранные с 20 по 24 октября, и релизы от 21 октября получены из изменений aports в том виде, в каком они опубликованы на git.alpinelinux.org. В релизы 21 октября входят файлы minirootfs, из которых собирают официальные Docker-образы Alpine. Проверку проект расширяет до 27 октября, когда сервер закрыли. Ключи подписи пакетов и релизов на GitLab-сервере никогда не хранились.

Если вы только потребляете пакеты Alpine или используете Alpine как базовый образ, массово менять теги смысла нет. Полезнее проверить обычные вещи: пакеты приходят из официальных зеркал, проверку подписей в apk никто не отключил ради быстрой сборки, базовые образы закреплены по digest.

Сценарий всё равно показательный. Компрометация системы, из которой собирают артефакты, опаснее взлома отдельного продукта: заражённый результат расходится по всем потребителям. Такую механику мы разбирали на примере атаки на цепочку поставок инструментов сборки, где пострадали Trivy, Checkmarx KICS и LiteLLM. У Alpine, по имеющимся данным, до этого не дошло.

Пользователям Alpine проект советует:

  • после открытия gitlab.alpinelinux.org задать новый пароль через «Forgot your password?» на странице входа;
  • сменить пароль на других сайтах, если он совпадал с паролем от gitlab.alpinelinux.org: Alpine видит автоматические попытки входа с паролями из чужих утечек;
  • заново скачать alpinelinux/gitlab:19.3.6. В образе от 27 октября стоит Ruby 3.3.13, а скачанный раньше 19.3.6 и все старые теги уязвимы;
  • выполнить apk upgrade: в репозиториях уже лежит Ruby 3.4.12 для Alpine 3.22, 3.23, 3.24 и edge и Ruby 3.3.13 для 3.21.

Насколько опасна уязвимость для вашего GitLab?

Зависит от того, может ли атакующий писать в публичный проект. Бюллетень GitLab говорит, что «при определённых условиях» уязвимость позволяла выполнить код на сервере без аутентификации. GitLab.com и GitLab Dedicated уже исправлены, так что вопрос касается self-managed серверов.

Эксплойт Rapid7 выполнял команды от имени пользователя git на неисправленном официальном образе 19.4.1, а на исправленном 19.4.2 тот же ввод не сработал. Но сначала нужно закоммитить вредоносный .gitlab-ci.yml и файл с UTF-8 символами в пути в ветку публичного проекта. По словам Rapid7, без аутентификации проходит только финальный запрос к CI lint.

На сервере, где любой может зарегистрироваться и создать проект, этот барьер исчезает: атакующий сам становится владельцем репозитория. Как CI-конфигурации попали на сервер Alpine и могли ли новые учётные записи создавать там публичные проекты, Alpine не уточнила. Rapid7 пока не знает, как обходится без шага записи атака из исходного отчёта в GitLab, который остаётся закрытым.

Реально ли воспользоваться этой уязвимостью на вашем сервере, зависит от условий эксплойта и настроек регистрации. Open-source проекту открытая регистрация нужна, на внутреннем GitLab компании она чаще остаётся по недосмотру после установки. На пентестах мы регулярно видим доступные из интернета серверы, где новый пользователь сразу может создавать проекты.

На 28 октября CVE-2026-97229 в каталоге CISA KEV нет, но это слабое утешение. Прошлая «десятка» в GitLab, CVE-2021-22205, давала выполнить код без авторизации при обработке загруженных изображений (компонент ExifTool), применялась в реальных атаках и в KEV попала.

Для бизнеса ставки выше, чем кажется. Через GitLab проходят секреты CI, ключи деплоя, доступы к облакам и реестрам. У аутсорс-команд на одном сервере лежат репозитории десятков заказчиков, а дампы с персональными данными в репозиториях или переменных CI тянут за собой 152-ФЗ и уведомление Роскомнадзора об утечке.

Руководителю нужно принять три решения: назначить владельца рантайма для каждого самосборного образа, решить, нужен ли GitLab доступ из интернета и открытая регистрация, и заранее договориться, при каких признаках сервер считают скомпрометированным. Первую задачу мы закрываем, когда ведём сопровождение CI и инфраструктуры сборки.

Как проверить и закрыть свой GitLab

Начните с типа установки: от него зависит, где искать исправление. Релизы от 21 октября самые свежие в каждой ветке и заодно закрывают CVE-2026-110264, отдельную ошибку в регулярных выражениях.

ВеткаИсправляет CVE-2026-97229 (по бюллетеню GitLab)Свежий релиз (21 октября)
19.5В списке уязвимых нет19.5.1
19.419.4.219.4.3
19.319.3.519.3.6
19.219.2.819.2.9
19.1, 19.0 и 18.11В списке уязвимых нет19.1.9, 19.0.10 и 18.11.13
  • Официальный Linux-пакет или образ GitLab. Обновитесь до свежего релиза своей ветки. Фактическую версию Ruby покажет gitlab-rake gitlab:env:info.
  • Установка из исходников или образ со своим Ruby. Обновите GitLab и Ruby до 3.3.13, 3.4.12, 4.0.9 или новее. Ruby 3.2 исправления не получит, с него нужно уходить. У мейнтейнера стороннего образа спрашивайте версию Ruby, а не новый тег.
  • Если обновиться сразу нельзя. Rapid7 советует ограничить анонимный доступ к публичным проектам, запретить недоверенным пользователям создавать публичные проекты и писать в них или заблокировать маршрут CI lint. Это перекрывает только путь эксплойта Rapid7, уязвимый компилятор регулярных выражений остаётся.
  • Регистрация. Если внешние участники не нужны, отключите её, если нужны, включите подтверждение администратором. GitLab отдельно советует ограничить, кто может создавать учётные записи и персональные токены.

Мейнтейнеры Ruby уточняют: ошибка опасна для программ, которые компилируют регулярное выражение из текста под контролем атакующего, а настройка Regexp.timeout от неё не защищает. GitLab, разбирающий чужие CI-конфигурации, как раз такая программа.

Как искать следы

GitLab опубликовал шесть правил обнаружения в формате Sigma для self-managed серверов. Четыре из них помечены как экспериментальные. Они ищут враждебные шаблоны в предупреждениях о CI-регулярках и в GraphQL-запросах ciLint, необычно длинные пути к файлам с не-ASCII символами и отчёты о падениях Ruby, указывающие на Onigmo.

Сопроводительные рекомендации предупреждают, что успешный эксплойт может обойтись без падения, и предлагают проверить:

  • файлы пользователя git в /tmp;
  • неожиданные дочерние процессы Puma и Sidekiq;
  • исходящие соединения;
  • изменённые crontab и SSH-ключи пользователя git.

При признаках выполнения кода сервер считают скомпрометированным и ротируют секреты из gitlab-secrets.json, все переменные CI/CD, токены runner и токены доступа. С gitlab-secrets.json нужна аккуратность: им шифруются данные в базе, включая переменные CI и секреты 2FA. Ротацию проводят по документации для своей версии и только после резервной копии.

Порядок важен. Сначала снимите образ контейнера или VM, файловую систему и журналы Puma, Sidekiq, nginx и Rails: после удаления контейнера разбирать будет нечего, и вы окажетесь в положении Alpine. Затем поднимите новый сервер на исправленном рантайме и только потом меняйте секреты, иначе бэкдор на старом сервере увидит и новые.

Когда к нам приходят со «странными падениями CI», первое желание команды: перезапустить сервис и закрыть тикет. Пока картина не сохранена, делать этого не надо. Перезапуск стирает ровно то, что понадобится для ответа на главный вопрос: был ли взлом.

Частые вопросы

Почему GitLab 19.3.6 остался уязвим к CVE-2026-97229?

Ошибка находится в Onigmo, движке регулярных выражений Ruby. GitLab исправил её, пропатчив Ruby в своих официальных пакетах и образах. Alpine запускала GitLab на стандартном Ruby, поэтому обновление номера GitLab уязвимость не закрыло: нужен Ruby 3.3.13, 3.4.12, 4.0.9 или новее.

Чем GitLab 19.3.4 отличается от 19.3.5 в истории CVE-2026-97229?

Первоначальный бюллетень от 12 октября называл исправленной для ветки 19.3 версию 19.3.4. Сейчас исправлением названа 19.3.5, а 19.3.4 значится уязвимой. Серверам на 19.3.4 нужно переходить на 19.3.6, а самосборным установкам ещё и обновлять Ruby.

Почему падения воркеров не равны успешному выполнению кода?

Падение процесса и ошибка 502 говорят о сбое, а не о выполнении кода. По словам Alpine, успешная эксплуатация как раз не вызвала бы падения и не оставила бы следов в журналах веб-сервера, поэтому чистая картина ничего не доказывает.

Нужно ли отключать саморегистрацию на внутреннем GitLab?

Если внешние участники не нужны, да. Если нужны, включите подтверждение администратором и запретите новым пользователям создавать публичные проекты: эксплойт Rapid7 требует права записи в публичный проект, а открытая регистрация даёт его любому.

Затронуты ли пакеты и Docker-образы Alpine?

Доказательств этого нет. Alpine сверила изменения aports с копией репозитория разработчика и 45 сборочными серверами, пакеты за 20-24 октября и релизы 21 октября, включая minirootfs, соответствуют опубликованному на git.alpinelinux.org. Ключи подписи на GitLab-сервере не хранились.

Какие секреты менять после подозрительных падений CI?

Если есть признаки выполнения кода, GitLab советует ротировать секреты из gitlab-secrets.json, все переменные CI/CD, токены runner и токены доступа. Сначала сохраните доказательства и поднимите сервер на исправленном рантайме, иначе возможный бэкдор увидит и новые секреты.

Если ваш GitLab собран не из официального пакета или смотрит в интернет, проверьте его раньше атакующих. HirexTech проводит пентест DevOps-инфраструктуры: от настроек регистрации и CI до рантайма внутри образов.

Прокрутить вверх