
Forgejo 16.0.5 - Alta - Remota - IDOR - GET /api/v1/user/keys/{id} devuelve las claves SSH y de despliegue de otros usuarios
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · forgejo-keys-idor
Clase: IDOR Alcance: Remoto
Forgejo 16.0.5 (8d24b7d) - Forgejo
GET /api/v1/user/keys/{id} se encuentra en el grupo del usuario actual (reqToken, ámbito de usuario). El manejador aún carga public_key por ID numérico sin verificación de propietario. La misma tabla contiene claves de usuario, claves de despliegue y principales. GPG en esta versión lo hace correctamente: GetGPGKeyForUserByID(ctx, ctx.Doer().ID, id). Los campos privados adicionales están protegidos. El material de la clave pública, el título y la huella digital no lo están. Los IDs son secuenciales.
Un token read:user recorre 1, 2, 3 e inventaría las claves SSH y de despliegue de otras personas.
| ID | sin CVE aún |
| Clase | IDOR |
| Alcance | Remoto (autenticado read:user / sesión) |
| CWE | CWE-639 |
| CVSS | Alto: 7.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
| Producto | Forgejo |
| Afectado | hasta v16.0.5 (8d24b7d) |
| Autenticación | autenticado (token read:user o sesión) |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1 |
Registrar o usar cualquier cuenta, generar un token read:user (o usar una cookie de sesión), recorrer /api/v1/user/keys/{id}. Obtienen títulos, huellas digitales y material de clave pública de claves que no les pertenecen. No obtienen la clave privada. No obtienen una shell. Inventarían quién tiene acceso a qué.
| Privilegio | Método / sink | Reconocimiento / integridad / disponibilidad | Probado en laboratorio o inferido |
|---|---|---|---|
| Leer el título, la huella digital y el material de la clave pública SSH de otro usuario | GET /api/v1/user/keys/{id} como propietario ajeno | Reconocimiento (confidencialidad del inventario de claves públicas) | Probado en laboratorio. Título testigo FORGEJO-KEYS-IDOR-WITNESS en id 1; la lista del atacante es solo id 2 |
| Inventariar claves de despliegue de la misma manera | Mismo manejador, filas KeyTypeDeploy en public_key (OwnerID 0) | Reconocimiento | Inferido de la tabla. El laboratorio plantó una clave SSH de usuario, no una clave de despliegue |
| Leer principales de certificados SSH | Mismo manejador, filas KeyTypePrincipal. ListPublicKeys excluye este tipo | Reconocimiento | Inferido de la tabla |
| Ver claves que pertenecen a usuarios limitados o privados | Sin verificación de visibilidad en esta búsqueda | Reconocimiento | Inferido del manejador |
JSON adicional (user, key_type, read_only) | appendPrivateInformation solo si es admin o propietario | Reconocimiento | Probado en laboratorio cerrado: la respuesta de no propietario no tenía campo user |
Cerrado en esta ruta: material de clave privada, una shell, GET /users/{username}/keys (lista pública con ámbito de nombre de usuario), GPG GET /api/v1/user/gpg_keys/{id} (404 en un id ajeno), DeletePublicKey (propietario/admin 403). Cross-repo GET /repos/{owner}/{repo}/keys/{id} devuelve 404 cuando RepoID no coincide.
Los parches de seguridad de v16.0.5 (#14370) fueron RCE por git-apply y CSRF de OpenID. No cerraron esta búsqueda. Misma clase que gitea-user-keys-idor, independientemente aún abierta aquí.
Fijé Forgejo v16.0.5 después de la oleada de 2026-09. Gitea 1.27.3 aún tenía esta búsqueda. Forgejo ha divergido, así que releí el manejador de claves del usuario actual junto a GPG en lugar de copiar el paquete de Gitea. GPG devuelve 404 en un id ajeno. SSH no.
Caminos equivocados ya registrados: tratar la propia lista del atacante como la fuga (no lo es - la lista tiene ámbito, GET-by-id no); tratar DISABLE_REGISTRATION=true en el instalador como un cierre (cualquier cuenta existente es suficiente); una reverse shell. Teatro.
python3 forgejo-keys-idor-Abraxas-Labs.py
o:
cd lab
./run.sh
Objetivo solo http://127.0.0.1:19310. Imagen codeberg.org/forgejo/forgejo:16.0.5. Dos cuentas. Testigo 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 a menos que key.OwnerID == ctx.Doer().ID (o administrador del sitio). Rechazar KeyTypeDeploy / KeyTypePrincipal en esta ruta. Coincidir con GetGPGKeyForUserByID.
GetPublicKeyGNU Affero GPL v3.0. Ver LICENSE. Solo laboratorio en loopback. Sin garantía.