
Proof-of-concept и лабораторная работа для аутентифицированного SSRF в эндпоинте миграции репозиториев Gitea, где git следует HTTP-редиректам в loopback git HTTP и утекает внутренние объекты.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-git-redir-ssrf
Разбор: https://abraxaslabs.tech/research/gitea-git-redir-ssrf
Gitea 1.27.3 - Gitea
IsMigrateURLAllowed — это однократный вызов net.LookupIP для введённого вами URL. Затем байты проходят через git clone --mirror. HandleGitCmdHTTPRedirection должен мешать git следовать этому переходу. Он не делает ничего. Комментарий и есть находка: они не включили http.followRedirects=false, потому что GitLab отвечает 301 без .git. Загрузка pull-mirror вызывает ту же пустую функцию.
Аутентифицированный пользователь может выполнить миграцию с разрешённого bait-хоста, и git проследует по 302 в loopback git HTTP. Внутренние объекты попадают в репозиторий, которым он владеет.
| 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) |
| Аутентификация | аутентифицированный (открытая регистрация по умолчанию) |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1 |
Зарегистрироваться, выполнить миграцию с внешне выглядящего URL, который отвечает 302 на http://127.0.0.1:.../internal.git. Прямая миграция на loopback запрещена. Клонирование с переходом по редиректу — нет. Файл-свидетель попадает в его репозиторий.
Не чтение диска через file://. Не X-Gitea-Internal-Auth на /api/internal (git не установит этот заголовок). Не неаутентифицированный RCE. Не RCE через metadata, если отдельно не показать, что git сохранил тело.
Остатки того же тега: keys IDOR, hostmatcher 0.0.0.0/8.
v1.27.3 закрыла волну GHSA 2026 года: CVE-2026-60004 (RCE через хук diffpatch, CISA KEV), CVE-2026-59774 (неаутентифицированный #+INCLUDE в Org-mode), ограничения Actions для fork-PR, значение по умолчанию X-WEBAUTH-USER для reverse-proxy. Для всего этого оставайтесь на 1.27.3+. Я всё равно задержался на этом теге.
CVE-2026-59765 исправила Go uri.Open. CVE-2026-22874 исправила Go reservedIPNets. Редиректор git по-прежнему FIXME. Запись в changelog «disable HTTP redirects on pull mirror sync» — не про эту функцию.
Лаборатория размещает Gitea на 8.8.4.2, чтобы bait-хост на 8.8.4.10 выглядел внешним для hostmatcher. Внутренний git HTTP слушает только loopback в netns Gitea (network_mode: service:gitea, порт 9418). Прямая миграция на 127.0.0.1 должна завершиться неудачей. Клонирование с переходом по редиректу должно удаться. redir.py отвечает 302 на /bait.git с переходом на http://127.0.0.1:9418/internal.git. Файл-свидетель WITNESS содержит GHSA-GITEA-GIT-REDIR-SSRF.
Уже зафиксированные неверные пути: ALLOW_LOCALNETWORKS true (тогда allow-list исчезает); file://; X-Gitea-Internal-Auth; reverse shell. Театр. Контроль — это запрет прямой миграции на loopback.
Ошибки клиента выглядят как ошибки продукта. Хелпер, затеняющий http.client, вообще не отправляет пакет. .recv() у HTTPResponse — это не .read(). Упоминаю это один раз, потому что это стоило времени и на минуту показалось, будто 1.27.3 довела дело до конца.
cd lab
./run.sh
Цель — только http://127.0.0.1:18103. ALLOW_LOCALNETWORKS=false.
negative-loopback status=422 You can not import from disallowed hosts
migrate-bait status=201 clone_addr=http://8.8.4.10/bait.git
fetch raw/WITNESS status=200 GHSA-GITEA-GIT-REDIR-SSRF
SUCCESS Gitea #2 git HTTP redirect SSRF
Не рассматривайте DNS-разрешение на этапе отправки как границу безопасности. Принудительно задайте http.followRedirects=false (и нормализуйте URL клонирования до .git, чтобы GitLab продолжал работать), либо проксируйте git HTTP через dialer, который повторно запускает hostmatcher на каждом переходе.
HandleGitCmdHTTPRedirection · IsMigrateURLAllowedGNU Affero GPL v3.0. См. LICENSE. Только для лабораторных условий с loopback. Без гарантий.