
Forgejo 16.0.5 — высокий уровень — удалённо — IDOR — GET /api/v1/user/keys/{id} возвращает SSH-ключи и deploy-ключи других пользователей
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · forgejo-keys-idor
Класс: IDOR Доступ: Удалённый
Forgejo 16.0.5 (8d24b7d) - Forgejo
GET /api/v1/user/keys/{id} находится в группе текущего пользователя (reqToken, область пользователя). Обработчик по-прежнему загружает public_key по числовому ID без проверки владельца. В той же таблице хранятся пользовательские ключи, deploy-ключи и principals. GPG на этой версии делает всё правильно: GetGPGKeyForUserByID(ctx, ctx.Doer().ID, id). Приватные дополнительные поля защищены. А материал публичного ключа, заголовок и отпечаток — нет. ID последовательные.
Токен read:user перебирает 1, 2, 3 и инвентаризирует чужие SSH- и deploy-ключи.
| ID | CVE пока нет |
| Класс | IDOR |
| Доступ | Удалённый (аутентифицированный read:user / сессия) |
| CWE | CWE-639 |
| CVSS | High: 7.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
| Продукт | Forgejo |
| Затронуто | по v16.0.5 (8d24b7d) |
| Аутентификация | аутентифицированный (токен read:user или сессия) |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1 |
Зарегистрироваться или использовать любой аккаунт, выпустить токен read:user (или использовать сессионную cookie), перебирать /api/v1/user/keys/{id}. Он получает заголовки, отпечатки и материал публичных ключей для ключей, которыми не владеет. Приватный ключ он не получает. Шелл он не получает. Он инвентаризирует, у кого есть доступ к чему.
| Привилегия | Метод / sink | Разведка / целостность / доступность | Доказано в лаборатории или выведено |
|---|---|---|---|
| Чтение заголовка, отпечатка и материала SSH-публичного ключа другого пользователя | GET /api/v1/user/keys/{id} как чужой владелец | Разведка (конфиденциальность инвентаря публичных ключей) | Доказано в лаборатории. Свидетель-заголовок FORGEJO-KEYS-IDOR-WITNESS на id 1; список атакующего содержит только id 2 |
| Инвентаризация deploy-ключей тем же способом | Тот же обработчик, строки KeyTypeDeploy в public_key (OwnerID 0) | Разведка | Выведено из таблицы. В лаборатории был размещён пользовательский SSH-ключ, а не deploy-ключ |
| Чтение principals SSH-сертификатов | Тот же обработчик, строки KeyTypePrincipal. ListPublicKeys исключает этот тип | Разведка | Выведено из таблицы |
| Просмотр ключей, принадлежащих ограниченным или приватным пользователям | Нет проверки видимости при этом поиске | Разведка | Выведено из обработчика |
Дополнительный JSON (user, key_type, read_only) | appendPrivateInformation только если админ или владелец | Разведка | Доказано в лаборатории как закрытое: в ответе не-владельца не было поля user |
Закрыто на этом маршруте: материал приватного ключа, шелл, GET /users/{username}/keys (публичный список в области username), GPG GET /api/v1/user/gpg_keys/{id} (404 на чужой id), DeletePublicKey (403 для владельца/админа). Кросс-репозиторный GET /repos/{owner}/{repo}/keys/{id} возвращает 404, когда RepoID не совпадает.
Патчи безопасности v16.0.5 (#14370) были git-apply RCE и OpenID CSRF. Они не закрыли этот поиск. Тот же класс, что и gitea-user-keys-idor, независимо всё ещё открыт здесь.
Я зафиксировал Forgejo v16.0.5 после волны 2026-09. В Gitea 1.27.3 этот поиск всё ещё был. Forgejo разошёлся, поэтому я перечитал обработчик ключей текущего пользователя рядом с GPG, вместо того чтобы копировать набор для Gitea. GPG возвращает 404 на чужой id. SSH — нет.
Уже зафиксированные неверные пути: считать собственный список атакующего утечкой (это не так — список ограничен областью, GET-by-id — нет); считать DISABLE_REGISTRATION=true на установщике закрытием (достаточно любого существующего аккаунта); reverse shell. Театр.
python3 forgejo-keys-idor-Abraxas-Labs.py
или:
cd lab
./run.sh
Цель только http://127.0.0.1:19310. Образ codeberg.org/forgejo/forgejo:16.0.5. Два аккаунта. Свидетель FORGEJO-KEYS-IDOR-WITNESS.
IOC planted victim-id=1 attacker-id=2
IOC attacker-list status=200 ... title=attacker-own-key
IOC get id=1 status=200 ... FORGEJO-KEYS-IDOR-WITNESS
IOC gpg-neg id=1 status=404
SUCCESS forgejo-keys-idor victim-id=1 get-http=200 list-has-id=no gpg-http=404 FORGEJO-KEYS-IDOR-WITNESS
404, если только key.OwnerID == ctx.Doer().ID (или администратор сайта). Отклонять KeyTypeDeploy / KeyTypePrincipal на этом маршруте. Соответствовать GetGPGKeyForUserByID.
GetPublicKeyGNU Affero GPL v3.0. См. LICENSE. Только loopback-лаборатория. Без гарантий.