Metodología IDOR y control de acceso
Dominar la categoría de vulnerabilidad más rentable y menos saturada en programas de bug bounty maduros: fallos de control de acceso, con foco en IDOR (Insecure Direct Object Reference).
📋 Índice de contenidos
01Objetivo
Dominar la categoría de vulnerabilidad más rentable y menos saturada en programas de bug bounty maduros: fallos de control de acceso, con foco en IDOR (Insecure Direct Object Reference).
02Teoría
Por qué IDOR es la categoría más rentable para un hunter
A diferencia de XSS o SQLi, un IDOR no requiere inyectar nada raro: solo requiere cambiar un identificador en una petición legítima y observar si el servidor comprueba que el usuario autenticado tiene permiso sobre ese recurso. Esto significa:
- No lo detectan los escáneres automáticos de la empresa (no hay payload "malicioso" que firmar).
- No requiere herramientas sofisticadas: un proxy y dos cuentas de prueba bastan.
- El impacto es fácil de demostrar de forma inequívoca (acceso a datos de otro usuario), lo que reduce el riesgo de que triage lo cierre como "teórico".
Las tres preguntas que detectan un candidato a IDOR
Para cada endpoint mapeado en el módulo 4-6:
- ¿El identificador del recurso es predecible o enumerable? IDs secuenciales (1, 2, 3...) son el caso obvio. UUIDs no son inmunes: si un UUID de otro usuario se filtra en otra respuesta (ej. en el HTML de una página pública, en una respuesta de API que lista "amigos" o "posts recientes"), el hecho de que sea difícil de adivinar no importa — ya lo tienes.
- ¿El servidor valida la pertenencia del recurso al usuario autenticado, o solo valida que el usuario esté autenticado? Esta es la distinción real entre autenticación (quién eres) y autorización (qué puedes hacer). Un fallo de IDOR es casi siempre un fallo de autorización sobre una autenticación que funciona perfectamente.
- ¿El mismo recurso es accesible por distintos verbos HTTP o rutas (API v1 vs v2, endpoint "admin" vs endpoint "user") con validación inconsistente entre ellos? Es habitual que la validación se implemente en un controlador y se olvide en otro que accede al mismo dato.
Control de acceso horizontal vs vertical
- Horizontal: usuario A accede a datos o acciones de usuario B, ambos con el mismo rol (el caso clásico de IDOR).
- Vertical: un usuario de rol bajo accede a funcionalidad reservada a un rol superior (usuario normal ejecuta una acción de admin). Se detecta con la misma metodología: probar cada endpoint con el rol mínimo que lo acepte y comprobar si rechaza o acepta a un rol inferior.
Metodología de prueba paso a paso
- Con la cuenta A, realiza la acción legítima y captura la petición completa en el proxy.
- Repite la petición exacta sustituyendo solo el identificador del recurso por el de la cuenta B (o de un recurso que no te pertenece).
- Si la respuesta HTTP es 200 con datos reales de B (no un mensaje de error genérico camuflado en 200), hay IDOR. Si la respuesta es 403/404 de forma consistente para recursos ajenos, el control está bien implementado en ese endpoint concreto — pero prueba el mismo recurso por otras rutas (paso 3 de las tres preguntas).
- Prueba también operaciones de escritura, no solo lectura: modificar o borrar un recurso ajeno es un impacto mucho mayor que solo leerlo, y a menudo el control de lectura está mejor hecho que el de escritura porque recibe menos atención en revisión de código.
03Caso real — dos IDOR confirmados en el VDP de la NASA
Continuación directa del caso del módulo 3. Tras descartar la API pública, el trabajo se movió a las aplicaciones con sesión del dominio de ciencia ciudadana. *(Igual que en el módulo 3: endpoint, parámetro y dominio exactos siguen redactados por el embargo de confidencialidad del programa; lo que sigue es la metodología real, completa.)*
Hallazgo 1 — un parámetro que anulaba la sesión
En la pantalla de "mis observaciones" apareció el olor clásico de un IDOR: un identificador de usuario viajando en la propia URL. Siguiendo la metodología de este módulo — capturar la petición legítima con la cuenta propia A y repetirla sustituyendo solo el identificador por el de la cuenta propia B — el resultado fue inequívoco: el servidor devolvía la ficha de la cuenta B. El parámetro anulaba la sesión en vez de solo complementarla. Una comprobación adicional subió la gravedad: la misma petición funcionaba sin ninguna cookie de sesión, de forma completamente anónima. Y los identificadores de usuario eran casi correlativos — enumerables uno a uno sin autenticar.
Antes de reportarlo se hizo la pregunta honesta que el propio triage iba a hacer: ¿esto es una vulnerabilidad real, o son "datos públicos por diseño" en una plataforma de ciencia ciudadana? La respuesta fue que no: la aplicación solo exponía la ficha propia a través de la sesión en su uso normal; el parámetro era un bypass de ese control, funcionaba sin autenticar, y permitía enumerar a toda la base de usuarios — incluyendo una marca que identificaba cuentas de estudiantes (potencialmente menores) junto a su organización. Impacto medido con honestidad: no exponía contraseñas ni email, así que se reportó con prioridad baja-media, ni más ni menos de lo que era.
Resultado: el informe fue aceptado como válido técnicamente, pero cerrado como duplicado — otro investigador lo había reportado antes. En un VDP sin recompensa, un duplicado no da Letter of Recognition. Pero un duplicado no es un fracaso: confirma que el criterio de búsqueda y la metodología eran correctos. Simplemente llegó segundo.
Pivote tras el duplicado: una cuenta cebo para demostrar impacto sin tocar a nadie
En vez de abandonar el programa, el pivote fue de vector dentro del mismo scope: de la API de consulta de usuario a un portal de gestión de equipos/invitados construido sobre un CMS empresarial (Liferay), donde los educadores gestionan cuentas de estudiante. La primera pista fue la misma de siempre: un identificador de usuario viajando como parámetro, esta vez codificado en base64 en la URL y en claro en la petición AJAX que cargaba la tabla de miembros.
Aquí apareció un matiz importante que el módulo no cubre con las tres preguntas básicas: que el servidor acepte un identificador ajeno con HTTP 200 no basta como prueba si la cuenta objetivo no tiene datos que filtrar. Con la cuenta B vacía, la respuesta para su identificador salía vacía — indistinguible de "no autorizado" y de "no hay nada que mostrar". La solución fue construir una cuenta cebo controlada: usar la función legítima de la aplicación para crear, bajo la cuenta propia B, una cuenta de invitado (el tipo de sub-cuenta que un educador crea para un estudiante). B pasó a tener exactamente un objeto que A no debía poder ver.
Repetida la petición logueado como A pero apuntando al identificador de B, el servidor devolvió el invitado de B completo: su identificador interno y la organización/colegio asociado a B. El detalle que cerró la prueba sin ambigüedad: *antes* de crear el invitado, esa misma petición para B devolvía cero registros; *después*, la sesión de A lo recuperaba. La respuesta se decidía por el parámetro, no por la sesión — control de acceso roto, de manual.
Durante estas pruebas el WAF del sitio bloqueó temporalmente la IP por exceso de recargas con el parámetro manipulado. La respuesta correcta no fue intentar evadirlo, sino parar, esperar a que se levantara el bloqueo y retomar a ritmo humano — las reglas del programa excluyen explícitamente cualquier cosa parecida a DoS o fuerza bruta de rate-limiting.
Resultado: este segundo informe, pese a un walkthrough detallado que el propio equipo de triage pidió antes de resolver, también se cerró como duplicado. Dos hallazgos reales y técnicamente válidos sobre la misma clase de fallo (IDOR por identificador de usuario), dos duplicados, cero Letter of Recognition. La conclusión estratégica —no técnica— fue que ese programa estaba agotado para esa clase de vulnerabilidad: alguien más ya tenía cazados todos los ángulos obvios de IDOR-por-userid en ese scope. La decisión fue dejar de insistir ahí y reasignar el tiempo a programas con recompensa económica (ver módulo 12), conservando igualmente el aprendizaje de metodología — que es exactamente el argumento del módulo 13.
Lección: la prueba de impacto con datos propios (incluida una cuenta cebo creada para la ocasión, cuando el objetivo real no tiene datos que demostrar el acceso) no es opcional en programas exigentes — es el requisito mínimo de aceptación. Y un resultado técnicamente perfecto puede seguir dando cero recompensa: eso no invalida el método, invalida la expectativa de que acertar siempre paga.
04Resumen operativo
- IDOR es la categoría de mejor relación esfuerzo/recompensa para casi cualquier hunter.
- Las tres preguntas (ID predecible / validación de pertenencia / consistencia entre rutas) cubren el 90% de los casos.
- Prueba siempre con dos cuentas propias, nunca con datos de usuarios reales ajenos.
- Prueba también escritura, no solo lectura: el impacto y el pago son mayores.