Prueba de concepto y laboratorio para un IDOR de Gitea (CWE-639) en GET /api/v1/user/keys/{id}, que permite a un token autenticado read:user enumerar las claves SSH y de despliegue de otros usuarios.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-user-keys-idor
Gitea 1.27.3 - Gitea
GET /api/v1/user/keys/{id} se encuentra en el grupo del usuario actual (reqToken, rejectPublicOnly). El handler aún carga la fila por ID numérico sin ninguna comprobación de propietario. La misma tabla contiene claves de usuario, claves de despliegue y principals. El equivalente de GPG lo hace bien: GetGPGKeyForUserByID(ctx, ctx.Doer.ID, id). Los campos privados están protegidos. El material de la clave pública, el título y la huella 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. El registro abierto convierte eso en un bug de cuenta nueva.
| ID | sin CVE todavía |
| 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 | Gitea |
| Afectado | hasta v1.27.3 (146cc3e) |
| Auth | autenticado (token read:user) |
| Licencia | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Registrarse (registro abierto por defecto), generar un token read:user, recorrer /api/v1/user/keys/{id}. Obtienen títulos, huellas y material de clave pública de claves que no poseen, incluidas las claves de despliegue. No obtienen la clave privada. No obtienen una shell. Inventarían quién tiene acceso a qué.
El GHSA de claves de la 1.27.3 estaba acotado por nombre de usuario más individualPermsChecker. No era esta consulta.
Mismo tag, resto: SSRF por redirección HTTP de git.
La v1.27.3 cerró la oleada de GHSA de 2026. Aun así me quedé en ese tag y leí el handler de claves del usuario actual junto al de GPG. GPG devuelve 404 para un id ajeno. SSH no.
seed.sh planta el título de clave de la víctima GHSA-GITEA-KEY-IDOR-WITNESS y una clave del atacante. La lista propia del atacante no contiene el testigo. GET /user/keys/1 como el atacante sí. GPG con un id ajeno devuelve 404. Ese es todo el argumento.
Caminos equivocados ya registrados: tratar la lista propia del atacante como la fuga (no lo es - la lista está acotada, el GET por id no); una reverse shell. Teatro.
Los bugs de cliente parecen bugs de producto. Un helper que ensombrece http.client nunca envía un paquete. .recv() sobre un HTTPResponse no es .read(). Lo menciono una vez porque costó tiempo y pareció, por un minuto, que la 1.27.3 había terminado el trabajo.
cd lab
./run.sh
Objetivo solo http://127.0.0.1:18101.
add-key user=victim status=201 title=GHSA-GITEA-KEY-IDOR-WITNESS id=1
add-key user=attacker status=201 id=2
attacker-list status=200 ids=[2] (no witness)
get id=1 status=200 snippet contains GHSA-GITEA-KEY-IDOR-WITNESS
SUCCESS Gitea #1 user/keys IDOR
404 a menos que key.OwnerID == ctx.Doer.ID (o administrador del sitio). Rechazar los tipos deploy/principal. Igualar a GPG.
GetPublicKeyGNU Affero GPL v3.0. Ver LICENSE. Solo lab en loopback. Sin garantía.