
Proof-of-concept лаборатория, демонстрирующая аутентифицированный SSRF в allow-list hostmatcher Gitea, где webhook на http://0.0.0.1 достигает loopback-сервисов, недоступных для 127.0.0.1.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-hostmatcher-0000-ssrf
Gitea 1.27.3 - Gitea
CVE-2026-22874 добавил reservedIPNets. CGNAT, TEST-NET, NAT64, Teredo, Azure WireServer. Реальные диапазоны. Не RFC 6890 0.0.0.0/8, старый блок "this network". IANA всё ещё его публикует. Go IsGlobalUnicast возвращает true для 0.0.0.1. Это не IsLoopback (127.0.0.0/8). Это не IsPrivate. Внешний allow-list по умолчанию считает его публичным интернетом. Linux доставит 0.0.0.1 на локальный сокет, если этот адрес есть на lo. Один и тот же слушатель, два имени.
Аутентифицированный пользователь может отправить webhook на http://0.0.0.1 и прочитать тело HTTP-ответа из истории хуков. 127.0.0.1 на том же порту отклоняется.
| ID | CVE пока нет |
| CWE | CWE-918 |
| CVSS | High: 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N |
| Продукт | Gitea |
| Затронуто | вплоть до v1.27.3 (146cc3e); остаток от CVE-2026-22874 |
| Аутентификация | аутентифицированный пользователь, который может создать webhook репозитория |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1 |
Зарегистрироваться (открытая регистрация по умолчанию), создать репозиторий, направить webhook на http://0.0.0.1:<local-port>/, протестировать его и прочитать тело ответа из истории хуков. Это loopback HTTP, до которого 127.0.0.1 на том же порту добраться не может: всё, что слушает в netns Gitea и привязано шире, чем loopback, включая сервисы, которые оператор считал "только для localhost", потому что они блокировали 127.0.0.0/8.
Это не устанавливает X-Gitea-Internal-Auth. Это само по себе не RCE. Это полное тело HTTP-ответа с адреса, который allow-list всё ещё называет внешним.
v1.27.3 — это сборка, закрывшая волну GHSA 2026 года. Я всё равно задержался на этом теге. Патчи, добавляющие allow-list или deny-list, — это подарок: читаешь список, а затем спрашиваешь, что у IANA всё ещё есть такого, что список забыл. У двух остатков уже были лаборатории на этом дереве (keys IDOR и git-redirect SSRF). Таблица остатков в том посте перечисляла webhook на http://0.0.0.1/ как карту. Это SUCCESS.
Доставка webhook сохраняет тело HTTP-ответа в истории хуков. Это лучший оракул, чем git clone, который в основном отвечает на вопрос "сработал ли fetch". Я поместил оракул в netns Gitea на порт 8080, добавил 0.0.0.1/8 на lo с NET_ADMIN и создал два webhook. Сначала негативный контроль.
Уже зафиксированные неверные повороты: считать HTTP 201 при создании webhook оракулом (оракул — это история); успешная доставка на 127.0.0.1 (это был бы другой баг — лаборатория требует, чтобы она отклонялась); обращение к /api/internal с X-Gitea-Internal-Auth; reverse shell. Театр. Свидетель — GITEA-0000-SSRF в истории хуков.
cd lab
./run.sh
Цель только http://127.0.0.1:18131. Оракул делит netns Gitea на порту 8080. run.sh добавляет 0.0.0.1/8 на lo.
create-hook name=neg-loopback status=201
negative-control 127.0.0.1 blocked
create-hook name=hit-0000 status=201
poll id=2 witness=True
SUCCESS GITEA-HOSTMATCHER-0000
Добавьте 0.0.0.0/8 в reservedIPNets или перестаньте считать IsGlobalUnicast признаком "безопасно набирать". 0.0.0.1 должен отклоняться так же, как 127.0.0.1.
hostmatcher.goGNU Affero GPL v3.0. См. LICENSE. Только loopback-лаборатория. Без гарантий.