Misc CSRF Exploitation
After discussing CORS misconfigurations in the last few sections, we will explore different miscellaneous CSRF attack vectors that may be used to bypass weak CSRF defenses.
Combining Attack Vectors to Bypass SameSite Cookies
Web browsers decide whether or not to send SameSite cookies with requests depending on the request's source site and the intended target. This differs from the origin considered for the Same-Origin policy, as seen a few sections ago. The key difference is that the port and subdomain are not considered part of the site. Therefore, two domains are considered the same site, even if the port and subdomain differ, and in certain cases, a cross-origin request is still considered SameSite. Consider the following examples:
https://vulnerable.htbandhttps://sub.vulnerable.htbare SameSitehttps://vulnerable.htbandhttps://vulnerable.htb:9001are SameSitehttps://vulnerable.htbandhttps://sub.vulnerable.htb:9001are SameSitehttp://vulnerable.htbandhttps://vulnerable.htbare NOT SameSitehttps://vulnerable.htbandhttps://exploitserver.htbare NOT SameSite
Here's a table summarizing SameSite cookie behavior:
Strict
Cookies are sent only with same-site requests. Not sent with cross-site requests, even when navigating to the origin URL.
Lax
Cookies are sent with same-site requests and with top-level cross-site GET requests (e.g., navigation). Not sent with POST requests or requests embedded in img, script, iframe etc.
None
Cookies are sent with all requests, both same-site and cross-site. Requires the Secure attribute.
There are a few ways we can utilize this behavior to bypass the restrictions posed by SameSite cookies. For instance, when the session cookie has the SameSite attribute set to Lax, it is only sent with safe requests such as GET requests. If the web application contains any endpoints that are state-changing and are done with GET requests, the SameSite protection is ineffective. The same applies if all state-changing operations use POST requests, but the web application is misconfigured and accepts GET requests.
If we must bypass Strict SameSite restrictions, we can combine the misconfiguration discussed above with a client-side redirect on the target site. If we write a payload that sends the victim to the client-side redirection endpoint, the client-side redirect is initiated by the target site and is thus considered SameSite. Therefore, the victim's cookies are sent along the resulting request even though the SameSite attribute is set to Strict. If we redirect the victim to the misconfigured endpoint that accepts GET requests for state-changing operations, we can execute a successful CSRF attack.
Note: this bypass only works with client-side redirects, not server-side redirects such as HTTP 3xx status codes.
As an example, consider the following web application that sets the SameSite cookie attribute to Strict on the session cookie:
HTTP request to /login.php with user credentials; response shows 302 redirect to admin.php with session cookie.
Interestingly, the web application redirects us to a temporary page after a successful login, which then redirects us to our profile:
https://vulnerablesite.htb/profile.php
Welcome message indicating the endpoint is under construction and redirecting to profile.Looking at the source code, we can see that the resulting redirect is implemented using an HTML meta tag, which is a client-side redirect:
HTTP GET request to /admin.php with user info; response shows HTML with welcome message and redirect to profile page with same parameters.
Furthermore, we can inject additional GET parameters to the URL via the user GET parameter, as the web application seems to copy that parameter in the redirection URL:
HTTP GET request to /admin.php with user and hello parameters; response shows HTML with welcome message and redirect to profile page with same parameters.
The user profile is vulnerable to the CSRF vulnerability discussed a couple of sections ago, allowing us to promote our user via the /profile.php?promote=htb-stdnt endpoint. However, since the SameSite attribute is set to Strict, our previous payload will not work. Instead, we can leverage the client-side redirect to craft a successful CSRF exploit. To achieve this, we must ensure the victim accesses the endpoint, resulting in a client-side redirect. Furthermore, the victim needs to be redirected to a URL containing the promote=htb-stdnt GET parameter to promote our user to administrator privileges. We can achieve this with a payload similar to the following:
<script>
document.location = "https://vulnerablesite.htb/admin.php?user=htb-stdnt%26promote=htb-stdnt";
</script>Setting this payload as our exploit on the exploit server and delivering it to the victim successfully executes the CSRF attack. Subsequently, we obtain administrator privileges on the web application.
Lastly, because subdomains are considered SameSite, we can bypass SameSite cookie restrictions by exploiting XSS vulnerabilities in them. In that case, the cross-origin request is considered to be SameSite. Therefore, the victim's cookies are sent with the request, resulting in a successful CSRF attack. We will explore this scenario in more detail in the upcoming sections.
Looking at our sample web application, we can see that it sets the SameSite=Strict attribute on the session cookie, preventing the cookie from being sent along any cross-site requests:
HTTP POST request to /login.php with user credentials; response shows 302 redirect to profile.php with session cookie.
However, as we have discussed above, subdomains are considered to be the same site. Thus, let us try to identify subdomains that are potentially vulnerable to XSS. We can do this using gobuster:
kabaneridev@htb[/htb]$ gobuster vhost -k -u https://vulnerablesite.htb -w /path/to/SecLists/Discovery/DNS/subdomains-top1million-20000.txt
<SNIP>
===============================================================
2023/08/26 12:09:40 Starting gobuster in VHOST enumeration mode
===============================================================
Found: guestbook.vulnerablesite.htb (Status: 200) [Size: 2317]
===============================================================
2023/08/26 12:09:43 Finished
===============================================================Looking at the subdomain https://guestbook.vulnerablesite.htb, we can identify the guestbook web application we have seen a few sections ago. We can confirm that the same XSS vulnerability is still present:
https://guestbook.vulnerablesite.htb/view.php
Alert box from guestbook.vulnerablesite.htb displaying the number 1 with an OK button.Assuming the administrator monitoring the guestbook entries is also logged in to the main application at https://vulnerablesite.htb, we can abuse this XSS vulnerability to bypass the SameSite restriction and make the administrator promote our user to admin. To do that, we need to force the administrator user to send the corresponding POST request, which we can achieve with the following XSS payload:
<script>
var csrf_req = new XMLHttpRequest();
var params = 'promote=htb-stdnt';
csrf_req.open('POST', 'https://vulnerablesite.htb/profile.php', false);
csrf_req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
csrf_req.withCredentials = true;
csrf_req.send(params);
</script>After posting our payload to the guestbook and waiting for a few seconds for the administrator user to access the page, we can see that the CSRF attack was successful, and our user has been promoted:
https://vulnerablesite.htb/profile.php
Welcome message for htb-stdnt; application under construction; user has administrator permissions with a 'Promote' option.Weak Token Brute-Force
As briefly discussed in the Session Security module, weak CSRF tokens may be bypassed to conduct a successful CSRF attack. Simple bypasses can occur when the CSRF token is not tied to a user session. In that case, an attacker accessing the vulnerable web application can add a valid CSRF token to the cross-origin request from their own session. The backend will then accept the cross-origin request from the victim's session, as the CSRF token is valid. Another example is when CSRF tokens are not entirely random, making them predictable. Depending on how the CSRF token is created (such as a hash of the username or current timestamp), we might be able to guess it in a single attempt or brute-force it using a payload.
This time, the web application has been protected with CSRF tokens such that a plain CSRF attack will not succeed anymore. However, if we obtain multiple CSRF tokens, we can deduce that it is an incrementing number, potentially something like a counter, and can thus possibly be brute-forced:
HTTP GET request to /profile.php; response shows user permissions, hidden form with promote and CSRF values, and a 'Promote' button.
If we analyze the CSRF tokens more closely, we can notice that the CSRF token is simply the current time as a Unix Timestamp. This makes the CSRF token predictable and allows us to create a working exploit to conduct a CSRF attack successfully. To do this, we must correctly guess the victim's CSRF token, i.e., the last time the victim accessed the /profile.php endpoint before accessing our payload. Getting the timing exactly right is difficult since we cannot dynamically brute-force the CSRF token using JavaScript code due to the restrictions posed by the default SameSite Lax policy. Thus, we need to hardcode the guessed CSRF token in our HTML form and update the value for each guess:
<html>
<body>
<form method="GET" action="https://vulnerablesite.htb/profile.php">
<input type="hidden" name="promote" value="htb-stdnt" />
<input type="hidden" name="csrf" value="1692981700" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>While this makes brute-forcing the CSRF token more challenging and thus reduces the likelihood of a successful attack, it is feasible to predict a valid CSRF token and bypass the weak protection.
Bypassing Header-based Defense Measures
If header-based CSRF protection measures are improperly implemented, we can bypass them similarly to other domain name or URL filters. For instance, if the vulnerable web application https://vulnerablesite.htb only checks for the presence of the string vulnerablesite.htb in the Referer header, we can host the payload at a URL containing this string like https://exploitserver.htb/somepath/vulnerablesite.htb. Similarly, we can bypass filters that check if the Referer header ends with the corresponding string.
CSRF with JSON Request Body
Many modern web applications expect data in a POST request to be JSON. If that is the case, we can only carry out CSRF attacks under certain conditions because, with a CSRF payload, we can only send URL-encoded POST parameters, not JSON-formatted POST parameters. However, a web application only accepting JSON data might still be vulnerable to CSRF.
Firstly, suppose the web application suffers from a CORS misconfiguration that allows us to specify the Content-Type header. In that case, we can simply set the header to application/json and send a JSON body from JavaScript code. However, this requires the additional CORS misconfiguration to be present.
Alternatively, the web application might be vulnerable to CSRF if it does not correctly check the Content-Type header sent in the request. An HTML-based CSRF payload only supports the Content-Type headers application/x-www-form-urlencoded, text/plain, and multipart/form-data, which we can set using the enctype attribute on the HTML form. Thus, by checking the Content-Type header, the web application can determine that the request body is not of the expected JSON format and, thus, potentially reject the CSRF request. However, if the web application does not properly check the Content-Type header and only relies on the syntax of the request body, we can forge a JSON body with a CSRF payload similar to the following:
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<form method=\'POST\' action=\'https://vulnerablesite.htb/profile.php\' enctype=\'text/plain\'><input type=\'hidden\' name=\'{\"promote\": \"htb-stdnt\", \"dummykey\' value=\'\": \"dummyvalue\"}\' /></form><script>document.forms[0].submit();</script>"></iframe>This CSRF payload results in the following request:
POST /profile.php HTTP/1.1
Host: vulnerablesite.htb
Content-Length: 53
Content-Type: text/plain
{"promote": "htb-stdnt", "dummykey": "dummyvalue"}We can see that the Content-Type header is not application/json as discussed above. However, the request body is valid JSON and can thus be parsed by the web application. We injected a dummy key and dummy value into the JSON because the HMTL payload technically submits a request body with Content-Type text/plain and thus inserts an = between the parameter's name and its value. By inserting the dummy key and dummy value, we can ensure that the = is inserted in the dummy key and does not pollute our payload data. This method enables us to carry out CSRF attacks even in cases where the web application only accepts a JSON-encoded request body.
Misc CSRF Exploitation Questions
Question 1: Identify a way to bypass the SameSite cookie restriction to conduct a successful CSRF attack to obtain administrator privileges.
Add the necessary vHosts to your
/etc/hostsfile:sudo sh -c 'echo "10.129.63.111 exploitserver.htb misc-csrf.htb" >> /etc/hosts'Navigate to
https://misc-csrf.htb, then sign in with the credentialshtb-stdnt:Academy_student!.Inspect the response headers to identify the
SameSite=Strictcookie attribute on the session cookie.Identify a client-side redirect on the application (e.g., after logging in, a redirect to
/admin.php?user=htb-stdnt).Observe if additional GET parameters can be injected into the redirect URL via the
userGET parameter.Identify the CSRF vulnerable endpoint for promoting users (e.g.,
/profile.php?promote=htb-stdnt).Craft a payload on the exploit server (
https://exploitserver.htb) that leverages the client-side redirect to perform the CSRF attack. The payload should redirect the victim to the client-side redirect endpoint with thepromote=htb-stdntparameter injected.<script> document.location = "http://misc-csrf.htb/admin.php?user=htb-stdnt%26promote=htb-stdnt"; </script>Save the payload to the exploit server.
Deliver the payload to the victim by navigating to
https://exploitserver.htb/deliver, selectingmisc-csrf.htbas the target vHost, and clicking Deliver.After a few moments, the victim will trigger the payload. Return to
https://misc-csrf.htb/profile.php?user=htb-stdntand refresh the page to confirm your user has been promoted to administrator.
Last updated