CSRF Today: Techniques, Mitigations and Bypasses (Continued)
Continued….
Hi guys, so this blog is a continuation to my ‘CSRF Today: Techniques, Mitigations and Bypasses’ blog. If you haven’t read it yet, I recommend going through it once before starting on this one. It may help you learn about the basics of CSRF, how it is mitigated and how one can possibly bypass these mitigations.
Mtitgating CSRF:
- Anti-CSRF Tokens
- Double Submit Cookie
- Validating the Referrer and Origin of the request
- Sending data in JSON/XML format
- Sending some data in the request headers that violates the Browser’s Same Origin Policy if the request is sent cross-domain. (For eg; Authorization header that is used to authorize a user on the application, also used for authentication in certain applications)
- Samesite Cookie attribute
- Captcha
- Viewstate
Until now we have read about the conventional mechanisms used to mitigate CSRF and possible bypasses for them, which means we have read about Anti-CSRF Tokens, Double Submit Cookies and Referrer/Origin Header validation in my last blog post. Now lets move on to reading about some of the comparatively newer mechanisms that work against Cross-Site Request Forgery.
4) Sending data in JSON/XML format
Let us suppose the application sends data in JSON/XML format.
The request looks something like this:
If the server properly validates this request and doesn’t have a mis-configuration in the CORS settings allowed then CSRF is not possible in this scenario. This is because all modern browsers will send a Pre-Flight request to the application server before sending a cross-origin request with unusual headers such as ‘Content-Type: application/json’.
To this request the server responds with the allowed origins and the allowed headers. Visit this site to know more about Same Origin Policy (SOP) and Cross Origin Resource Sharing (CORS).
However, in cases where the server doesn’t validate the Content-Type header sent in the request, an attacker may generate a CSRF POC with Content-Type: text/plain using one of the below methods and may perform a successful attack-
- Generate a request using Fetch method
<html>
<body>
<h1> JSON CSRF</h1>
<script>
fetch(‘http://vulnerable-site.com/amountransfer', {method: ‘POST’, credentials: ‘include’, headers: {‘Content-Type’: ‘text/plain’}, body: ‘{“Amount”:”5000",”Action”:”Transfer”,”User”:”attacker-id”}’});
</script>
<form action=”#”>
<input type=”button” value=”Submit” />
</form>
</center>
</body>
</html>
Manipulate this code as per your requirement
-Generate a request using XMLHTTPRequest method
<html>
<body>
<script>
function jsonCsrf()
{
var xhr = new XMLHttpRequest();
xhr.open(“POST”, “http://vulnerable-app.com/amountransfer", true);
xhr.setRequestHeader(“Content-Type”, “text/plain”);
xhr.withCredentials = true; //sends user cookies along with request
xhr.send(“{“Amount”:”5000",”Action”:”Transfer”,”User”:”attacker-id”}”);
jsonCsrf();
</script>
</body>
</html>
This code sends an Ajax request to the server with ‘Content-Type: text/plain’, which, if accepted by the server leads to a successful CSRF attack.
If the application however, does validate the Content-Type header from the request, the only way an attacker can successfully perform a CSRF attack is if the application has mis-configured CORS.
The attack may be performed as follows:
1) The attacker uploads the CSRF POC to ‘attacker.com/csrf.html’ and lures the victim user to open this URL in his browser where he is authenticated on the Vulnerable application.
<html>
<body>
<script>
function jsonCsrf()
{
var xhr = new XMLHttpRequest();
xhr.open(“POST”, “http://vulnerable-site.com/amountransfer", true);
xhr.setRequestHeader(“Content-Type”, “application/json”);
xhr.withCredentials = true; //sends user cookies along with request
xhr.send(“{“Amount”:”5000",”Action”:”Transfer”,”User”:”attacker-id”}”);
jsonCsrf();
</script>
</body>
</html>
2) Since the request contains ‘Content-Type: application/json’ header which is not a usual header, the application will send a Pre-Flight request to the vulnerable application server.
3)The application responds with Access-Control-Allow-Origin: attacker.com’ and Access-Control-Allow-Credentials: true’ headers as shown below.
4)The browser now sends the actual request with attackers manipulated parameters and ‘Content-Type: application/json’ header which is accepted by the server and the attack is successfully performed.
5) Sending some data in the request headers that violates the Browser’s Same Origin Policy (SOP) if the request is sent cross-domain.
If the application sends some unique header in the request to the server that violates the browsers SOP when the request is sent cross-domain, the application will again be sending a Pre-Flight request to the server as it did in the previous case, this pretty much nullifies the possibility of a CSRF attack in the same way as it does when sending JSON/XML data in the request.
If this unique header is guessable or known to an attacker, and the application has mis-configured CORS, he may perform a CSRF attack by setting the unusual headers in the CSRF POC to generate the request.
However, if the additional header is a unique token generated uniquely for each session, for eg; Authorization header, Access-Token, Anti-CSRF-Token,etc.; then the attacker might need to somehow steal the unique token belonging to the victim user to be able to perform a CSRF attack on the user.(Possible ways are Cross-Site Scripting on Vulnerable application, Brute-forcing token, Understanding the algorithm used to generate token, etc.)
It is also important to note that sometimes the application sends requests with unusual methods such as PUT, DELETE, etc.
This again leads to a Pre-Flight request when the request is sent cross domain. Hence for such kind of requests we need to have mis-configured CORS on the target application along with it allowing usage of unusual methods such as PUT, DELETE in cross-domain requests. (Application should respond with ‘Access-Control-Allow-Methods: PUT, DELETE’ in response to the Pre-Flight request sent).
We need to modify the code to send a PUT request instead of a POST request.
<html>
<body>
<script>
function csrf()
{
var xhr = new XMLHttpRequest();
xhr.open(“PUT”, “https://vulnerable-site.com/amountransfer", true);
xhr.setRequestHeader(“Content-Type”, “text/plain”);
xhr.withCredentials = true; //sends user cookies along with request
xhr.send(“Amount=5000&to=Attacker-ID”);
csrf();
</script>
</body>
</html>
This will generate a Pre-Flight request to vulnerable-site.com which if vulnerable will further lead to a PUT request being sent from the browser
-If there is no mis-configured CORS in the target application an attacker may also try sending the same request using standard methods such as GET/POST.
-If that fails one can try Method Overriding to see if the application accepts this request as a valid one -
Suppose the application sends a PUT request to ‘https://vulnerable-site.com/transferamount'
You may try generating a CSRF POC that sends a POST request to ‘https://vulnerable-site.com/transferamount?_method=PUT'
or ‘https://vulnerable-site.com/transferamount' with _method=PUT in the POST request body
<body onload=’document.forms[0].submit()’>
<form action=”https://vulnerable-site.com/transferamount?_method=PUT" method=”POST” enctype=”text/plain”>
<input type=”text” name=’Amount’ value=’5000'>
<input type=”submit” value=”send”>
</form>Notice the actual method used in the request here is POST with _method=PUT as a query parameter
6) Samesite Cookie attribute
When the application uses Samesite Cookie attribute as an effective way to mitigate CSRF, there are one of the two implementations that the developer might have made
-Samesite= Strict
-Samesite= Lax
‘Samesite= Strict’ means cookies will only be sent in a first-party context and wont be sent along with requests initiated by third party websites.
‘Samesite= Lax’ means cookies are not sent on normal cross-site sub-requests (for example to load images or frames into a third party site), but are sent when a user is navigating to the origin site (i.e. when following a link).This is the default cookie value if Samesite has not been explicitly specified, in recent browser versions.
What is important to note here is when an application uses Samesite=Lax, it restricts the browser from sending the Samesite Cookies cross-domain in a POST request, however it allows the cookies to be sent in a GET request.
This provides a possible attack surface to a malicious actor where he can attack victim user on a vulnerable application by following the below mentioned steps:
1) Suppose an application performs a sensitive action using a POST Request as shown below
POST /amountransfer HTTP/1.1
Host: vulnerable-site.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 78
Origin: http://vulnerable-site.com
Referrer: http://vulnerable-site.com
Connection: close
Cookie: showhints=1; PHPSESSID=tr7qoooe0ifkb93s59jsjel1u4
Upgrade-Insecure-Requests: 1Amount=5000&Action=Transfer&User=attacker-id
2) Try converting the same request into a GET request as shown below:
GET /amountransfer?Amount=5000&Action=Transfer&User=attacker-id HTTP/1.1
Host: vulnerable-site.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://vulnerable-site.com
Referrer: http://vulnerable-site.com
Connection: close
Cookie: showhints=1; PHPSESSID=tr7qoooe0ifkb93s59jsjel1u4
Upgrade-Insecure-Requests: 1
3) If the application accepts this manipulated request and gives a successful response, an attacker could use this to successfully perform a CSRF attack.
4) An attacker can generate a CSRF POC for this GET request and host this on an attacker controlled domain. He may lure the the victim user and to visit the this URL .
5) When the victim user visits this malicious site on the same browser where he is authenticated on the vulnerable application, the browser sends a GET request to the application with the users cookies (including the SameSite cookies) which is accepted by the server and the action is performed successfully, hence, resulting in a successful CSRF attack.
7) CAPTCHA
CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) is a type of security measure known as challenge-response authentication. A CAPTCHA test is made up of two simple parts: a randomly generated sequence of letters and/or numbers that appear as a distorted image, and a text box. To pass a the test and prove your human identity, simply type the characters you see in the image into the text box.
Due to randomness of CAPTCHA, it can be used as an effective security measure against CSRF by requiring the user to solve a CAPTCHA for any state changing action and sending the CAPTCHA value along with the request. The attacker cannot guess the random keyword generated in the CAPTCHA and hence can’t perform a successful CSRF attack.
However, if the attacker is able to successfully bypass the CAPTCHA implementation he might be able to perform a CSRF attack. CAPTCHA bypasses are in some ways similar to the Anti-CSRF token bypasses mentioned here.
- An attacker can try sending empty CAPTCHA value in the request.
- An attacker can omit the CAPTCHA parameter entirely from the request.
- Re-sending an old valid captcha value.
- Changing request method, eg; POST request to GET request.
- Understand the algorithm used to generate CAPTCHA and predict the CAPTCHA value used in the request.
- Certain times based on the faulty implementation, parameter manipulation is all that is required to successfully bypass CAPTCHA. For eg; changing ‘captcha=false’ to ‘captcha=true’ in the request sent may also lead to a successful bypass.
8) VIEWSTATE
View state is the method that the ASP.NET page framework uses to preserve page and control values between round trips. When the HTML markup for the page is rendered, the current state of the page and values that must be retained during postback are serialized into base64-encoded strings. This information is then put into the view state hidden field or fields.
Types of Viewstate:
- Non -Encrypted Viewstate (Non-MAC Enabled)
- MAC-Enabled Viewstate
- Encrypted Viewstate
Implementation of MAC- Enabled and Encrypted Viewstates can definitely help with CSRF mitigation if done properly. Since MAC-Enabled viewstates send a hash created using a unique Machine key along with the Base-64 encoded data in the request to maintain the integrity of the data in the request; and Encrypted viewstates are encrypted using unique keys, they maybe treated same as Anti-CSRF tokens generated uniquely for each page.
Hence, techniques to bypass Viewstate implementation will also be the same as Anti-CSRF token bypasses. Additionally, since MAC-Enabled and Encrypted viewstates are generated using a unique machine key, an attacker may try to brute-force the machine key using different tools (This is only possible in case the attacker has somehow gained access to an existing viewstate value for the victim). Once the attacker has successfully brute-forced the viewstate to identify the unique machine key used to generate it, he may generate his own viewstates and send it along with the CSRF POC to perform a successful attack. Blacklist3r is one such tool that can be used for this purpose.
Additionally, if the viewstate is unencrypted and not MAC enabled then one may directly generate a viewstate for a request by Base 64 encoding the request data .
However, if the .NET version is less than 4.5, application may accept an unencrypted __VIEWSTATE
parameter from the users even if ViewStateEncryptionMode
has been set. In such cases, ASP.NET only checks for the presence of the __VIEWSTATEENCRYPTED
parameter in the request. If one removes this parameter, and sends the unencrypted viewstate parameter, it will be processed. An attacker can generate a CSRF POC with this unencrypted viewstate and lure a victim user to access it using social engineering hence leading to a successful attack.
In conclusion, we can say that all these mentioned mitigations will definitely help against CSRF attacks provided they are implemented properly, avoiding all the possible bypasses.
Happy Hacking!
References: