root@sparrow:~/writeups$ cat vulnerability-pattern-idor-bac.md
// bug-bounty
IDOR & Broken Access Control
23 Jul 2026
· 3 min read
What it is
IDOR: you access another user's data just by changing an identifier —
GET /api/invoices/1001 becomes GET /api/invoices/1002 and it's not your invoice. Broken Access Control: same idea one level up — you can call an action your role shouldn't be allowed to perform at all, like a normal user successfully hitting POST /api/admin/delete-user.Why it matters
It's the single most common high-impact class in bug bounty, and it needs no exploit — just a browser, two accounts, and the willingness to change a number. That combination of low effort and high impact is exactly why programs take it seriously.
Where to look
- Any endpoint that addresses a resource by ID: invoices, orders, files, tickets, projects, messages, organizations
- Admin panels, team/workspace management, billing, invitations, role settings
How I test it
- Create two accounts — A and B. Non-negotiable.
- With A, create or touch a resource; capture the request in Burp.
- Note the identifier — numeric ID, UUID, slug.
- Switch to B's session, replay the exact same request but with A's identifier.
- If B can read, edit or delete A's resource: IDOR. If a normal account successfully performs an admin-only action: Broken Access Control.
- Repeat across every resource type and every verb (GET/PUT/PATCH/DELETE), not just the first one that worked.
Hunting checklist
- Second test account created
- Resource created and request captured
- Identifier replayed under a different account
- Tested read AND write/delete, not just read
- Tested at least one admin-only action with a normal account
- Checked whether the response is 200 with real data, not just 200 with an empty body
Evidence to collect
- Request as account A
- Same request replayed as account B
- Response proving B could read/modify/delete A's resource
- For BAC: the privileged request and the unprivileged replay side by side
Recommended fix
- Check ownership/role server-side on every request, not just on the ones the UI exposes
- Use non-guessable identifiers where feasible, but never rely on that alone
- Return 403/404 consistently instead of leaking existence via different error codes
Qué es
IDOR: accedes a los datos de otro usuario cambiando un identificador —
GET /api/invoices/1001 pasa a GET /api/invoices/1002 y no es tu factura. Broken Access Control: la misma idea un nivel más arriba — puedes llamar a una acción que tu rol no debería permitir en absoluto, como un usuario normal ejecutando con éxito POST /api/admin/delete-user.Por qué importa
Es, con diferencia, la familia de más impacto y más frecuente en bug bounty, y no requiere ningún exploit — solo un navegador, dos cuentas y las ganas de cambiar un número. Esa combinación de poco esfuerzo y mucho impacto es exactamente por lo que los programas se lo toman en serio.
Dónde buscar
- Cualquier endpoint que referencia un recurso por ID: facturas, pedidos, archivos, tickets, proyectos, mensajes, organizaciones
- Paneles de admin, gestión de equipos/workspaces, facturación, invitaciones, configuración de roles
Cómo lo pruebo
- Crea dos cuentas — A y B. No es opcional.
- Con A, crea o toca un recurso; captura la petición en Burp.
- Anota el identificador — ID numérico, UUID, slug.
- Cambia a la sesión de B, repite exactamente la misma petición pero con el identificador de A.
- Si B puede leer, editar o borrar el recurso de A: IDOR. Si una cuenta normal ejecuta con éxito una acción exclusiva de admin: Broken Access Control.
- Repite con cada tipo de recurso y cada verbo (GET/PUT/PATCH/DELETE), no solo con el primero que funcionó.
Checklist de hunting
- Segunda cuenta de prueba creada
- Recurso creado y petición capturada
- Identificador reenviado con otra cuenta
- Probado lectura Y escritura/borrado, no solo lectura
- Probada al menos una acción exclusiva de admin con cuenta normal
- Comprobado si la respuesta es 200 con datos reales, no solo 200 con cuerpo vacío
Evidencias para el reporte
- Petición como cuenta A
- Misma petición reenviada como cuenta B
- Respuesta que prueba que B pudo leer/modificar/borrar el recurso de A
- Para BAC: la petición privilegiada y la reenviada sin privilegios, una junto a la otra
Mitigación
- Comprobar propiedad/rol en servidor en cada petición, no solo en las que expone la UI
- Usar identificadores no adivinables donde sea viable, pero nunca depender solo de eso
- Devolver 403/404 de forma consistente en vez de filtrar existencia con códigos de error distintos