10

I am testing web application for which business actions are done by sending JSON requests like for example:

POST /dataRequest HTTP/1.1
Host: test.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 
Firefox/55.0
Accept: */*
Accept-Language: pl,en-US;q=0.7,en;q=0.3
Content-Type: application/json; charset=utf-8
Content-Length: 99
Cookie: SESSIONID=7jtyutuytu1a
Connection: close

{"F":"test.AppRequestFactory","I":[{"O":"5vhghgjhgjE0="}]}

I made the HTML auto-submit page like this

<html>
<head>
</head>
<body onload=document.getElementById('xsrf').submit()>
    <form id="xsrf" action="https://test.com/dataRequest" method=post enctype="text/plain">
    <input name='{"F":"test.AppRequestFactory","I":[{"O":""O":"5vhghgjhgjE0' value='"}]}' type='hidden'>
    </form>
</body>
</html>

The problem is that it will be send with header Content-Type: text/plain, but the server only accepts Content-Type: application/json; charset=utf-8.

I've read the discussion CSRF with JSON POST where one of the comments states:

Use something like this: var blob= new Blob([JSON.stringify(YOUR JSON)], {type : 'application/json; charset=UTF-8'}); to generate a JSON blob and it'll send perfectly. CSRF in seconds!

But I have no idea how to use this approach.

Is this application vulnerable to CSRF attack?

  • None of what you've posted looks relevant to xsrf. Are you even using cookie auth? What happens if you set the encType to application/json? Have you looked at any of the mitigation here. owasp.org/index.php/… – ste-fu Oct 1 '17 at 19:33
8
+100

Is this application vulnerable to CSRF attack?

Yes, it's vulnerable. The prerequisite, however, here is Flash. With the help of Flash, it's possible to forge a Content-type header with any arbitrary value. What you need to do is POST a request to your own domain, and then issue a 307 redirect. Please refer to below screenshot:

enter image description here

For more information, please refer to this cm2.pw article.

Use something like this: var blob= new Blob([JSON.stringify(YOUR JSON)], {type : 'application/json; charset=UTF-8'}); to generate a JSON blob and it'll send perfectly. CSRF in seconds!

This, afaik, is already fixed in modern browsers. However, it still works in IE with file URI.

  • This is a great point that I completely missed in my answer. Thanks for pointing it out! – Anders Jan 4 '18 at 19:58
  • 1
    @Anders What's still unclear to me - isn't this Flash behavior fixed by now? – Arminius Jan 4 '18 at 20:09
  • @Arminius Don't know, but there is still old Flash and old browsers out there. I think that is enough to merit swift action for OP. – Anders Jan 4 '18 at 20:13
  • 1
    @Arminius It's not. Only the behavior allowing to set arbitrary HTTP Headers was fixed. – 1lastBr3ath Jan 5 '18 at 2:47
  • Another example about the Flash CSRF attack can be found in a blog by Appsecco; source code available on GitHub. – Franklin Yu Sep 5 at 20:21
5

Warning: This answer may be wrong and to optimistic. See 1lastBr3ath's answer above.

No, I don't think the application is vulnerable.

You can change the Content-Type header, e.g. using the fetch API. However, there are only three values that you can use for cross domain requests:

application/x-www-form-urlencoded
multipart/form-data
text/plain

If you change it to anything else, such as application/json, the browser will first make an OPTIONS request to server, to see if it allows that header to be changed. This behaviour is part of CORS, and it is designed to limit what cross domain requests you can make with JavaScript to the old fashioned ones you could do with simple HTML. So unless the server specifically allows any domain to set this header (which would be a stupid thing to do), you are out of luck.

Note, however, that this seems to be a case of "security by accident". I would rely on something stronger for my CSRF protection (and perhaps they do, once you get past the content type hurdle). What happends if someday someone thinks it would be nice if the server accepts other content types and removes that limitation? With this configuration, it would be easy to accidentally open up a security hole.

0

An another way to achieve this is by taking advantage of the fact that most of json parsers respect the use of comments. So by creating a simple html form with a hidden input, you can put the json data as name of the input element in order to get it posted to the body. In this senario the only problem is that when the form is submitted the body of the post will have the '=' (from the input's name=value format). So to avoid that and make the json valid again u can add a comment indicator at the end of the name (your json data). This way the '=' character will be commented when parsed.

Here is an example:

<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="https://www.example.com/" method="POST" enctype="text/plain">
      <input type="hidden" name='{"name":"value"}//' value="" />
    </form>
    <script>
      document.forms[0].submit();
    </script>
  </body>
</html>
  • The question was not about making the parser validate the json but how to send it such as the browser set the Content-Type: application/json header in the request. I've tested your solution and despite being valid json and having set enctype to application/json it still send it as text/plain. – Xavier59 Jan 8 at 8:40
  • There are no comments in JSON, it should be: <input type="hidden" name="&#123;&quot;test&quot;&#58;&quot;X" value="&quot;&#125;" /> or, <input type="hidden" name="&#123;&quot;test&quot;&#58;&quot;X&quot;&#44;&quot;ignore&quot;&#58;&quot;" value="&quot;&#125;" /> Reference: gracefulsecurity.com/csrf-vs-json – aDoN yesterday

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.