PortSwigger Academy – API Testing

¡Hola a todos! Aquí estamos de nuevo y esta vez seguimos preparando la certificación Burp Suite Certified Professional (BSCP), que proporciona una base muy buena de cara a auditorías web y gestión de la presión, sobre todo en el examen para el cual te proporcionan 4 horas y hay que explotar hasta 6 vulnerabilidades. Además, se trata de un examen muy barato (<100€) lo que la hace sumamente atractiva.

Antes de nada, recordad que PortSwigger nos permite probar 30 días su versión profesional de Burp Suite. Os recomendamos que activéis esta versión para poder probar todas las funcionalidades de la herramienta.

En esta entrada explicaremos y solucionaremos todos los laboratorios relativos a API Testing. Hay muchos walkthroughs sobre cómo resolver estos laboratorios, pero exponerlos aquí nos sirve para afianzar conceptos y enseñaros además algunos trucos de uso de Burp Suite Professional, así como comentar veces que hemos visto las vulnerabilidades estudiadas en los laboratorios en la vida real. Sin más dilación, ¡comenzamos!

De un tiempo a esta parte, la explosión de las API es indiscutible. A día de hoy, muchas aplicaciones basadas en microservicios o incluso aplicaciones monolíticas implementan APIs o tienen módulos basados en ellas. Por tanto, las vulnerabilidades que aquí veremos las hemos reproducido en entornos reales en multitud de ocasiones, siendo fundamental entenderlas porque las encontrarás tarde o temprano en entornos de producción.

Lab1 (Apprentice) – Exploiting an API endpoint using documentation

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, find the exposed API documentation and delete carlos. En este laboratorio debemos identificar la documentación de la API para poder proceder con el borrado del usuario «carlos».

Comenzamos mapeando la aplicación y, simplemente accediendo con el usuario «wiener:peter» y revisando las peticiones lanzadas por el sitio web, vemos que es muy probable que exista una API a juzgar por los recursos cargados:

Fíjate en la ruta: «/resources/js/api/changeEmail.js». Nos hace pensar que existe una API y que la funcionalidad de cambio de email puede estar relacionado con ella. Lanzamos un cambio de email y evaluamos las peticiones:

Como vemos, efectivamente, tenemos localizada la API y uno de sus endpoints «/api/user/wiener». Además, vemos el uso del método PATCH, muy común para hacer actualizaciones de datos. Procedemos ahora a buscar la documentación de la API y la encontramos en:

/api/openapi.json

El fichero define y documenta llamadas a la API. Para parsearlo de manera rápida y efectiva, haremos uso de la extensión de Burp Suite Professional denominada «OpenAPI Parser». Como vemos, indicándole la URL de la especificación OpenAPI (o incluso Swagger si fuera el caso) obtenemos una representación clara de cómo se interactúa con la API:

La propia extensión ya nos mapea la petición como podemos observar para borrar un usuario. Por tanto, procedemos a enviarla al Repeater y seleccionar el usuario «carlos»:

Nos muestra un error, porque como cabe esperar, lanzamos la petición sin ningún tipo de autenticación. No obstante, como tenemos las credenciales «wiener:peter», indicamos en la petición a la API la cookie de sesión obtenida anteriormente y vemos cómo se resuelve el laboratorio:

Lab2 (Practicioner) – Finding and exploiting an unused API endpoint

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, exploit a hidden API endpoint to buy a Lightweight l33t Leather Jacket. En este laboratorio debemos identificar una vulnerabilidad en la API que nos permita comprar el artículo deseado.

Comenzamos mapeando la superficie de exposición de la web, identificando de nuevo que se hace uso de una API:

Nuevamente, intentamos obtener la documentación asociada a dicha API, aunque en este caso no la obtenemos. Vamos a trabajar con la petición que tenemos «GET /api/products/1/price». ¿Qué ocurre si intentamos hacer un PATCH a ese mismo endpoint con el objetivo de modificar el precio de la chaqueta?

Como vemos, obtenemos un error de procesado de la petición. Pero parece que el método PATCH es válido en dicho endpoint. Procedemos, por tanto, a generar una petición más adecuada, ya que en un PATCH lo esperable es que existan datos y, más concretamente, en formato JSON a juzgar por el error. Para hacer esto, usamos la extensión de Burp Suite Professional denominada «Content-Type Converter». Haciendo botón derecho, nos permite convertir la petición a datos JSON de manera sencilla:

¡Hemos conseguido modificar el precio del artículo, algo que no debería de haber sido posible! Ahora, simplemente, vuelve a la web, selecciona el artículo (verás que su precio es $0.00), añádelo a la cesta y emplaza la orden para resolver el laboratorio.

Lab3 (Practicioner) – Exploiting a mass assignment vulnerability

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, find and exploit a mass assignment vulnerability to buy a Lightweight l33t Leather Jacket«. En este laboratorio debemos identificar una vulnerabilidad en la API que nos permita comprar el artículo deseado.

Comenzamos mapeando la superficie de exposición en este laboratorio. Si echamos un ojo a las peticiones y respuestas enviadas y recibidas, vemos que solamente existe una interacción con la API a la hora de hacer el checkout de la compra. Esto se hace en dos peticiones (un GET y un POST) por separado:

Como podemos apreciar, resulta curioso que el contenido enviado como datos en la petición POST es parte del contenido obtenido en la petición GET. Por tanto, podemos probar a añadir contenido de forma arbitraria a dicho POST con el objetivo de ver si el servidor se comporta de manera diferente. Algo a tener en cuenta es el descuento. Dado que no disponemos de él, a priori el POST no nos aplica descuento (además de que al hacer el GET, el descuento es 0). Por ello, vamos a forjar el siguiente payload en el que incluimos un descuento arbitrario del 100%:

{"chosen_discount":{"percentage":100},"chosen_products":[{"product_id":"1","name":"Lightweight \"l33t\" Leather Jacket","quantity":1,"item_price":133700}]}

Una vez tenemos claro el payload, enviamos la petición POST con todos los datos en este caso (no solamente los productos elegidos) y vemos cómo se comporta el servidor.

Y… Como vemos… Hemos conseguido ejecutar la orden de compra. Esto es porque el servidor ha cogido nuestro descuento del 100%. Aunque no esperaba encontrarlo, se lo hemos metido «a calzador», lo ha procesado y ejecutado todo por 0 dólares.

Lab4 (Practicioner) – Exploiting server-side parameter pollution in a query string

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, log in as the administrator and delete carlos. En este laboratorio debemos identificar una vulnerabilidad de Server-Side Parameter Pollution e iniciar sesión como el usuario «administrator».

Comenzamos, como siempre, mapeando la superficie de exposición de la aplicación y procedemos a observar cómo la aplicación posee un mecanismo de reseteo de contraseña. Intentamos resetear la contraseña del usuario «administrator»:

Lanzamos los payloads típicos de detección de vulnerabilidades Server-Side Parameter Pollution: #, & y =. Comenzando por el hash value (#), vemos que la aplicación responde de manera diferente al añadirlo de manera codificada (%23):

Teniendo en cuenta que el error que nos devuelve la aplicación no es de usuario inválido, podemos deducir que se está realizando otra petición al backend de la aplicación por detrás, de modo que ahora nos falta el parámetro field por definir. Incluimos, por tanto, el parámetro «field» con un valor arbitrario en el payload:

administrator%26field=foo%23

En este punto, el error cambia nuevamente y nos indica que el campo que estamos inyectando no es válido. Obviamente, estamos utilizando un campo «foo» que no existe. Podemos llevar a cabo un ataque de fuerza bruta para intentar descubrir valores válidos pero, si revisamos el código Javascript de la aplicación, podemos observar que existe un parámetro denominado «reset-token» o similar.

Sabiendo que este parámetro puede existir, adaptamos los payloads y observamos las respuestas:

administrator%26field=reset-token%23 => Invalid field
administrator%26field=resettoken%23 => Invalid field
administrator%26field=ResetToken%23 => Invalid field
administrator%26field=reset_token%23 => Respuesta válida

Como podemos ver, obtenemos el token del usuario «administrator». Eso es así porque hemos truncado la llamada a la API interna y, en lugar de obtener el «field» denominado «email», ahora obtenemos el «field» denominado «reset_token». Toda vez que tenemos el token de reseteo de contraseña y que sabemos que la URL para realizarlo es «/forgot-password?reset-token=<resettoken>» gracias al código Javascript, simplemente navegando a la siguiente URL podemos resetear la contraseña del usuario «administrator», acceder y resolver el laboratorio borrando el usuario «carlos».

https://0a3000bc03f0ae8081689d7c00b80075.web-security-academy.net/forgot-password?reset_token=zj5xs3iwma0ete6l3j5ulr2mrj7a9c7i

Lab5 (Expert) – Exploiting server-side parameter pollution in a REST URL

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, log in as the administrator and delete carlos. En este laboratorio debemos identificar una vulnerabilidad de Server-Side Parameter Pollution e iniciar sesión como el usuario «administrator».

Comenzamos mapeando la superficie de exposición de la aplicación. En este caso, vemos que solamente el endpoint de reseteo de contraseña parece ser el indicado para testing. Reenviamos la petición y evaluamos qué ocurre al incluir payloads típicos de Server-Side Parameter Pollution:

administrator%23 => Invalid route (Uso de #, lo que indica que hay una parte de la URL que puede estar quedando inutilizada)

administrator%3f => Invalid route (Uso de ?, lo que indica y reafirma lo anterior, ya que si se usasen parámetros la llamada podría tener éxito)

administrator%2f..%2fcarlos => Obtenemos los datos de "carlos", por lo que vemos que es vulnerable a Server-Side Parameter Pollution en el path

Dado que la aplicación nos da errores de rutas inválidas, podemos tratar de obtener la ruta a los ficheros de documentación. Fíjate en los siguientes payloads:

administrator%2f..%2fopenapi.json%23
administrator%2f..%2f..%2fopenapi.json%23
administrator%2f..%2f..%2f..%2f..%2fopenapi.json%23
administrator%2f..%2f..%2f..%2f..%2f..%2fopenapi.json%23

Tal como vemos en la especificación, se define el siguiente endpoint:

/api/internal/v1/users/{username}/field/{field}

Teniendo en cuenta esto, construimos el siguiente payload:

administrator%2ffield%2ffoo%23

El error que nos devuelve la aplicación hace referencia a la versión concreta de la API. Por tanto, intentaremos retroceder directorios hasta utilizar la versión obtenida en el fichero OpenAPI:

administrator..%2f..%2f..%2f..%2fv1%2fusers%2fadministrator%2ffield%2ffoo%23

Tenemos lo que queremos, podemos obtener campos arbitrarios del usuario. Ahora, como último paso, debemos obtener el nombre del campo. Si localizamos el fichero Javascript «/static/js/forgotPassword.js», podemos observar que se hace referencia a «passwordResetToken». Lo obtenemos con la petición anterior.

administrator..%2f..%2f..%2f..%2fv1%2fusers%2fadministrator%2ffield%2fpasswordResetToken%23

Y… Finalmente, con el token de reseteo de contraseña podemos cambiar la contraseña de «administrator» mediante la siguiente URL y resolver el laboratorio borrando el usuario «carlos»:

https://0a97003404cc204aa5317218000f004e.web-security-academy.net/forgot-password?passwordResetToken=4cyjstzpclg3r6mm0cw2dp2otrqn0awk

~km0xu95