Gitea 28.0.0 закрывает 20 уязвимостей, включая вход под чужим аккаунтом и запуск кода без согласования

Gitea

Платформа Gitea для совместной работы с кодом получила версию 28.0.0. В ней закрыты двадцать уязвимостей, причём пять помечены как критические. Самые неприятные последствия - вход в чужой аккаунт через встроенный SSH-сервер и запуск чужого кода на собственных серверах сборки без согласования. Обновление вышло 30 сентября 2026 года, и заодно разработчики отказались от исторической приставки "1." в номерах версий.

Детали уязвимостей

Критическая проблема со SSH касается CVE-2026-103059. Встроенный SSH-сервер искал предъявленный клиентом открытый ключ запросом с регистронезависимым сравнением строк. На SQLite такое сравнение не различает регистр, поэтому подделанный ключ, отличающийся от настоящего только регистром букв, опознавался как ключ другого пользователя. Открытые ключи Gitea публикует свободно, так что злоумышленник забирает ключ жертвы и меняет регистр символов в его теле до тех пор, пока модуль ключа не окажется простым числом. Для ключа длиной 3072 бита подходящий вариант находится примерно за 1100 попыток. Проверка подписи RSA не требует, чтобы модуль был составным числом, поэтому собранного таким образом ключа хватает для аутентификации. Дальше атакующий получает полный доступ по SSH ко всем репозиториям жертвы, включая закрытые, с правом записи. Условие эксплуатации узкое, но распространённое: включён встроенный SSH-сервер и используется SQLite. Официальный rootless-образ контейнера сочетает оба параметра по умолчанию. Установки, работающие за OpenSSH, базы на PostgreSQL, вход по SSH-сертификатам и подпись HTTP уязвимости не подвержены.

В Gitea Actions нашли сразу несколько обходов согласования. Первый, CVE-2026-104632, опирается на обычные действия сопровождающего. Первый вкладчик открывает запрос на слияние из форка, и задание помечается как ожидающее одобрения. Отмена такого запуска переводит его в завершённое состояние, но флаг ожидания остаётся в базе. Последующий перезапуск создаёт задание уже в очереди, и сервер сборки забирает его вместе со сценарием из форка. Отдельного нажатия "одобрить" не требуется, достаточно отменить шумную проверку и перезапустить её. Секреты репозитория при этом по-прежнему не выдаются, речь идёт именно о допуске к исполнению чужого сценария. Второй обход, CVE-2026-94205, возник из-за того, что проверка согласования смотрела на инициатора события, а не на автора запроса. Метка, поставленная сопровождающим при обычном разборе задач, запускала сценарий форка, потому что права проверялись у него. Теперь Gitea сверяет и инициатора, и автора. Ещё одна ошибка возвращала к жизни уже отменённые задания при одобрении, другая позволяла неодобренному запуску из форка срывать доверенные сборки в общей группе параллелизма. Отдельно закрыт путь исчерпания памяти: большой статический набор комбинаций параметров разворачивался в момент создания запуска, и теперь наборы длиннее 256 комбинаций отклоняются до развёртывания.

Реестр контейнеров отдавал блоки данных с тем типом содержимого, который указал автор образа. Пуш образа с типом HTML позволял выполнить скрипт на домене Gitea, а оттуда оформить API-токен с полными правами для вошедшего пользователя. Теперь блоки отдаются как набор байтов, а типы содержимого манифестов ограничены. Похожая проблема с подменой касается дерева Git: коммит с двумя записями под одним именем файла Gitea принимал. Интерфейс проверки показывал первый файл, а выгрузка кода и конвейер сборки использовали второй. Вкладчик мог получить одобрение безобидного кода и подсунуть свой на исполнение под тем же хешем коммита. Теперь входящие объекты проходят проверку целостности. Ключи развёртывания, которые задуманы как малоправные учётные данные для конвейеров, выполняли перехватчики от имени владельца репозитория. Из-за этого обходилась защита тегов, а приватный репозиторий можно было сделать публичным одной командой.

Шесть уязвимостей связаны с исходящими соединениями. Проверка имени хоста при миграции репозитория выполнялась отдельно от самого соединения, поэтому подмена DNS-ответа между двумя обращениями открывала путь к внутренним узлам. Совпадение имени с настройкой разрешённых домены отменяло проверку адреса, а несколько DNS-ответов позволяли обратиться к внутреннему хосту через внешний. Зеркала на запись проверялись только при создании и позже могли принудительно переписать содержимое внутренних репозиториев. Перенаправления HTTP в операциях Git обходили политику исходящих соединений. Теперь сетевые операции Git идут через внутренний прокси, который проверяет адрес в момент подключения, а не полагается на ранние проверки имён.

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

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

Основная тяжесть исправлений пришлась на автоматизацию и конвейеры сборки, а не на разбор данных или ошибки в разметке. Платформа хранит исходный код и права доступа, поэтому сценарии сборки из чужих копий репозитория превратились в удобную точку входа. Закрытие двух десятков уязвимостей за один выпуск показывает, насколько плотно сам инструмент разработки стал частью периметра защиты.

Ссылки

Комментарии: 0