Cross-Site Content and Status Types Leakage
When doing my usual Bug Bounty research routine, I found an interesting behavior that occurred on a popular website, let’s say censored.com. Depending on whether the user was authorized to display the website two completely different pages were being shown. One, with
content-type:text/html;charset=utf-8 HTTP header, and the second, without Content-Type header at all, which in that case becomes
text/plain by default. So I’ve asked myself: Is there a clever way to differentiate between these two responses? If so, could this be generalized to all websites? What threats does it pose?
Let’s start with the threats that differentiating between responses mentioned earlier poses. It may not be obvious but it’s a serious information leak that could be performed across different websites. In this specific example, the malicious third party could obtain information on whether the visitor had been authorized to display a given set of resources on another website, e.g. Facebook.com, and hence, based on the results — expose their identity.
In some cases, the impact could be more severe than user deanonymization, so giving the idea a shot seemed worth the effort. At the time, I had a hunch that it was possible to achieve, so I began researching the MDN Web Documentation in the hope of finding any interesting attributes that could help. It didn’t take too long because I quickly discovered the worthy one.
HTMLObjectElement.typeMustMatchproperty is a
Booleanthat reflects the
typemustmatchattribute of the
<object>element. It indicates if the resource linked by it must match the MIME type given by
HTMLObjectElement.typein order for this resource to be used. 
Armed with this property, I began experimenting with it in Chrome. However, after a few tries, I was nowhere close in making it work. It seemed that the attribute was getting completely ignored by the browser. Indeed, I overlooked the browser compatibility section. Luckily, Eduardo enlightened me that it should work in Firefox. Indeed it did, like a charm.
Going into the details, if the
content-type header doesn’t match the
type attribute of the
HTML <object> the resource will not be loaded. Unfortunately, the
<object> element does not trigger the
onload nor the
onerror event, so detecting whether the object has loaded is yet another nut to crack.
Eduardo came up with an interesting idea of detecting that state by using an inline element inside the
<object type= data= typemustmatch> not_loaded </object>
Basically, the idea was that if the
not_loaded text was rendered, either the
type attribute didn’t match the
content-type header or the server responded with the status different than
HTTP 200 OK. It doesn’t look like something very easy to pull off, so I kept digging around.
After a closer look at the
<object> attributes, I noticed that
<object>.clientWidth varies in some scenarios. These attributes are nothing less than the
width and the
height of the rendered object. If the object was not yet rendered its dimensions are
0, otherwise, likely greater than that. Like in the previous paragraph, same here, the object will only be rendered if the server responded with
HTTP 200 OK status. I am unsure whether it’s an obstruction to the attack or a bridge to another, very efficient, technique of error pages detection? 
The last missing piece to the attack is detecting when the
<object> has loaded. Because, how do we know when we should read its
height property if we don’t know when the
<object> has loaded in the first place? A naive solution would be the use of timings after which the
object.clientHeight is being read. But no one likes timings, these are pretty unstable and sensitive to connection drops.
Well, maybe the
<object> itself doesn’t trigger the
onload event, but the
window object surely does. That event in the window will only be triggered when all components, including
iframes, are loaded. Hence, by creating a new isolated document (e.g. iframe) it’s easy to listen to its
onload event and then read the
height of the
<object> placed in there.
As for the result of combining the simulated
<object>.onload event with detection of the
<object> rendering, we’ve got a complete brand new technique for Cross-Origin Content and Status Types detection. And with that, it’s safe to say that the method proudly joins the XS-Leaks family.
I embedded a fancy Proof of Concept that successfully leaks the
content-type of the resource. Enjoy the ride!