IDOR, mass assignment y APIs: fallos de control de acceso que los escáneres no ven
Enumerar una API (Swagger, rutas ocultas, versiones), encontrar IDOR cambiando identificadores y hacerte administrador con mass assignment.
Ninguna inyección, ningún CVE: solo una aplicación que no comprueba si lo que pides es tuyo. Son los fallos de lógica más frecuentes en auditorías reales y aparecen en máquinas como Cap, TwoMillion o Academy. Un escáner no los encuentra porque no sabe qué datos deberías poder ver.
1. Enumerar la API
Antes de probar permisos, necesitas el mapa completo (enumeración de APIs):
- Documentación olvidada:
/swagger,/swagger-ui.html,/api-docs,/openapi.json,/v2/api-docs,/graphql(con introspection). - El JavaScript del cliente: las rutas de la API suelen estar en los
.js. Busca"/api/en las fuentes del navegador o congrepsobre lo descargado. - Fuerza bruta de rutas y versiones:
/api/v1/,/api/v2/,/api/internal/. Una versión vieja puede no tener los controles nuevos. - Métodos: lo que no deja hacer
GET, quizá lo dejaPUT,PATCHoDELETE. PruebaOPTIONSpara ver cuáles acepta.
ffuf -u http://objetivo.htb/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -mc all -fc 404
ffuf -u http://objetivo.htb/api/v1/user/FUZZ -w acciones.txt -X POST -H 'Content-Type: application/json' -d '{}' -mc all -fc 404
En TwoMillion, la propia API te enseña todas sus rutas si pides /api/v1, incluidas las de administración.
2. IDOR: cambiar el identificador
Una referencia insegura a objetos (IDOR) es cuando la aplicación usa un identificador que tú controlas y no comprueba que el objeto sea tuyo:
GET /api/facturas/1043 → tu factura
GET /api/facturas/1042 → ¿la de otro?
GET /download/5 → en Cap, /data/0 es la captura de red de otro usuario
Dónde buscarlo: en la URL, en el cuerpo JSON, en cabeceras y cookies, y en los nombres de fichero (/uploads/usuario_17.pdf). Si los identificadores no son números, mira si son predecibles: un base64 de un número, un MD5 del correo o un UUID versión 1 (lleva la hora dentro).
# Recorre identificadores y filtra por tamaño de respuesta
ffuf -u http://objetivo.htb/api/facturas/FUZZ -w <(seq 0 2000) -H 'Cookie: session=...' -fs 23
La forma sistemática: dos usuarios
Crea dos cuentas (o una normal y una de administrador, si tienes ambas) y usa la extensión Autorize de Burp: navegas como el usuario con más permisos y la extensión repite cada petición con la cookie del otro. Si la respuesta es la misma, hay un fallo de control de acceso.
3. Mass assignment
El servidor convierte el JSON que mandas directamente en un objeto y lo guarda. Si el objeto tiene campos que no deberías tocar (role, is_admin, balance), añádelos tú (mass assignment):
POST /api/register HTTP/1.1
Content-Type: application/json
{"username":"pepe","password":"pepe123","email":"[email protected]","role":"admin","isAdmin":true}
Cómo adivinar los nombres de los campos:
- Mira lo que devuelve la API: si
GET /api/meresponde con"role":"user", el campo se llamarole. - Busca en el JavaScript y en la documentación.
- Prueba variantes (
admin,is_admin,isAdmin,roleid,role_id,type,group).
En Academy, el formulario de registro tiene un campo oculto roleid=0; ponerlo a 1 te da una cuenta de administrador. En TwoMillion, PUT /api/v1/admin/settings/update acepta {"is_admin":1} de cualquier usuario autenticado.
4. Otros fallos de control de acceso
- Rutas de administración sin proteger: si
/adminte redirige al login, mira si/admin/userso/api/admin/usersresponden igualmente. - Comprobación solo en el cliente: un botón oculto con CSS o un rol que se lee de
localStorage. Haz la petición a mano. - Cabeceras de confianza:
X-Forwarded-For: 127.0.0.1,X-Original-URL: /adminoX-Custom-IP-Authorizationa veces saltan restricciones por IP o por ruta. - Tokens manipulables: si la sesión es un JWT o una cookie firmada con un secreto débil (falsificación de cookies), el rol va dentro.
Errores típicos
- Todas las respuestas tienen el mismo tamaño: la aplicación devuelve
200con un error dentro. Filtra por palabras (-fr "not found") en vez de por código. - El IDOR funciona en
GETpero no enPUT: prueba también el formato: si la API acepta JSON y formulario, uno de los dos caminos puede no tener la comprobación. - El campo extra se ignora: puede ir anidado (
{"user":{"role":"admin"}}) o con otra forma (role[]=admin).
Y después
Como administrador, la aplicación suele ofrecer funciones peligrosas: subir plantillas o plugins, ejecutar diagnósticos, generar informes. Repasa la checklist web con los permisos nuevos: lo que no era alcanzable antes puede tener una inyección de comandos o una SSTI.