Manual de Burp Suite Community Edition

Capítulo 17 · Control de acceso roto (horizontal/vertical)

Serie: Burp Suite desde cero  |  unfantasmaenelsistema.com

Los capítulos 13 y 16 se quedaron en la lectura -ver un dato que no era tuyo. Este capítulo da el paso que de verdad preocupa a quien tiene que responder por una aplicación: ¿y si, además de verlo, se puede modificar o borrar? El control de acceso roto tiene dos caras -horizontal, cuando alguien toca un objeto de otra cuenta con su mismo nivel de privilegio, y vertical, cuando alguien ejecuta una función que debería estar reservada a un rol distinto- y Juice Shop tiene un ejemplo real de cada una, a un par de peticiones de distancia.

Qué aprenderás en este capítulo

La diferencia entre control de acceso horizontal y vertical, con un caso real de cada uno · Cómo una cuenta completamente normal puede ejecutar una acción reservada al panel de administración · Cómo esa misma cuenta puede modificar el contenido del carrito de otra persona · Por qué "puedo leerlo" y "puedo escribirlo" son dos preguntas distintas que hay que probar por separado

1. Vertical: una acción de administrador, ejecutada por un cliente cualquiera

El panel de administración del capítulo 12 tiene un botón para borrar cualquier feedback de la tienda -visible solo si has entrado con una cuenta con `role:"admin"`. La pregunta que importa no es si ese botón existe, es si el endpoint que hay detrás comprueba el rol de quien llama, o solo que haya alguien logueado. Antes de tocar nada, la línea base:

GET /api/Feedbacks con la cuenta alumno (cliente normal, sin ningún privilegio): la lista completa, incluido el feedback id 3

GET /api/Feedbacks con la cuenta alumno (cliente normal, sin ningún privilegio): la lista completa, incluido el feedback id 3

Con esa misma cuenta -sin rastro de `role:"admin"` en ningún sitio- se manda la petición que en la interfaz solo aparece detrás del panel de administración:

DELETE /api/Feedbacks/3 con la cuenta alumno: 200 OK -ninguna comprobación de rol detrás de este endpoint

DELETE /api/Feedbacks/3 con la cuenta alumno: 200 OK -ninguna comprobación de rol detrás de este endpoint

El feedback ya no está: de id 1 salta directamente a id 4 -el borrado es real, no un 200 de cortesía

El feedback ya no está: de id 1 salta directamente a id 4 -el borrado es real, no un 200 de cortesía

Vertical: la interfaz decide quién ve el botón, no quién puede pulsarlo

El servidor confía en que solo una cuenta admin va a llegar a mandar esta petición -porque solo una cuenta admin ve el botón. Pero el botón es una decisión de interfaz, no un control de seguridad: en cuanto alguien manda la petición directamente, sin pasar por el botón, el servidor la ejecuta igual. Esto es exactamente lo que separa el control de acceso roto de un simple fallo de visibilidad -la función en sí no tiene ninguna verificación propia, delega toda su seguridad en que la interfaz no enseñe cómo llamarla.

2. Horizontal: modificar el carrito de otra persona

El capítulo 11 mostró la estructura exacta de un BasketItem -ProductId, BasketId, quantity- y probó a cambiar la cantidad del propio carrito. La pregunta que quedó sin hacer entonces: ¿hace falta que ese BasketItem sea tuyo?

GET /api/BasketItems/1 con la cuenta alumno (bid 6): el artículo pertenece a BasketId 1, un carrito que no es el suyo

GET /api/BasketItems/1 con la cuenta alumno (bid 6): el artículo pertenece a BasketId 1, un carrito que no es el suyo

Cambiar la cantidad de ese artículo ajeno es, otra vez, una sola petición:

PUT /api/BasketItems/1 con la cuenta alumno: 200 OK, la cantidad del carrito de otra persona cambia de 3 a 1

PUT /api/BasketItems/1 con la cuenta alumno: 200 OK, la cantidad del carrito de otra persona cambia de 3 a 1

Horizontal: mismo privilegio, objeto equivocado

Aquí no hace falta ningún rol especial -las dos cuentas son clientes normales, del mismo nivel. El fallo es que el servidor nunca comprueba que el BasketItem que se está modificando pertenezca al carrito de quien hace la petición. Enlazando con el capítulo 11: si el total del pedido se calcula en el checkout a partir de lo que haya en la base de datos en ese momento, esto no es solo "ver" el carrito ajeno -es poder alterar lo que esa persona va a pagar antes de que llegue a pulsar "Place order".

3. Dos preguntas distintas, dos comprobaciones distintas

EndpointLectura cruzada (GET)Escritura cruzada (PUT/DELETE)
/api/Addresss/{id}Bloqueada (cap. 13)No probada en este manual -buen ejercicio para practicar el método
/api/Users/{id}Sin filtrar (cap. 13)No aplica (endpoint de solo lectura para una cuenta normal)
/api/ComplaintsSin filtrar (cap. 16)No aplica en este manual (candidato a revisar)
/api/Feedbacks/{id}Sin filtrarDELETE sin comprobar rol -vertical
/api/BasketItems/{id}Sin filtrar (cap. 11)PUT sin comprobar propietario -horizontal

La tabla deja claro por qué no basta con probar un solo verbo: un GET protegido no dice nada sobre si el PUT o el DELETE del mismo endpoint también lo están, y viceversa. Cada verbo HTTP sobre un mismo recurso es, en la práctica, una comprobación de autorización independiente -y hay que tratarla como tal.

Conclusión

Vertical y horizontal son la misma pregunta de fondo -¿comprueba el servidor que quien pide algo tiene derecho a pedirlo?- aplicada en dos direcciones distintas: hacia arriba, en el nivel de privilegio; hacia los lados, entre cuentas del mismo nivel. Los dos casos de este capítulo comparten el mismo origen: un endpoint que solo comprueba que exista un token válido, y da por hecho el resto.

El capítulo siguiente deja el control de acceso y entra en un tipo de fallo completamente distinto -race conditions: qué pasa cuando dos peticiones legítimas llegan casi a la vez, y la aplicación no estaba preparada para esa coincidencia.

unfantasmaenelsistema.com | Seguridad, sistemas y todo lo que no debería funcionar (pero funciona)