In this website I pretend to list and describe all the possible covert-channels that could be used in a cross-domain communication between windos. Possibly these are well known things (the
scrollbar), but I've never heard of them as an all, neither seen this kind of use and demos.
Implementing some of them is a bit tricky, but I got some practice with this sop-error-based paradigm. Even so the code is not too clean, but the intersting things are the concepts.
I will also want to try to categorize them at some point, so these are some possible properties to keep in mind:
- Relation between sender's and receiver's windows:
SAMEwindow (after navigation),
- Dependency: logical, browser implementation or host's machine architecture dependant.
- Transmission rate.
Based on these parameters the following summary table has been constructed:
By now I'm just focusing on the logical ones. So below there is a more detailed explanation of each channel, a probe-of-concept and if necessary some comments about discrepancies between browsers.
Also note that all these PoCs would be much less practical if cross-domain-error exceptions weren't catcheable. Though this solution would probably break too many things...
Don't doubt to tell me if you find any inaccuracy or erratas :)
Originally designed  for setting targets for hyperlinks and forms, but it has been used in some frameworks for providing cross-domain communication. For that reason is not really "covert", but it serves as historical example. View more... 	 PoC
This channel can be used in both ways, just sharing a
window between sender and receiver, and redirecting the page for sending a message. By using
history.forward() the network requests can be avoided and the speed increased.
The shared resource can be the main window itself, an iframe or an opened tab.
The maximum length we can transmit is the length of a JS string, what is not fixed and depends on the machine.
Maximum speed? Use
try/catch for testing the SOP and detect the redirection.
With very long variables the browsers starts hanging, therefore we have to find a balance between message length and transmission speed.
Location hash or fragment identifier was thought to allow navigation towards a subresource in a document. The information in the URI fragment is not sent to the server, and after changing the page won't be reloaded. View more... 	 PoC
This is used in one direction, though it can be duplicated in order to have a bidirectional channel.
onhashchange will be triggered every time a new message is received, bringing an asynchronous messaging system, reliable and fast.
How much information can be passed? Again it dependes between browsers, but while testing the boundaries I found 2 bugs: a crash  in Firefox, and a weird behaviour in Chrome  that makes part of the URL to disappear.
history.length read-only property returns the number of elements in the session history , including the currently loaded page. It is maintained during navigation (and shared between iframes) and therefore shared across domains. By modifying this value and mapping it to a byte of information it is possible to create a communication channel.View more... 	 PoC, PoC 2*
I have implemented a communication protocol between frames as demonstration, but we could use it in other ways. Unfortunately, there is a race condition in my example limiting the performance. Probably due to the asynchrony of the history event loop.
- The sender emits one character increasing the
- The receiver is polling the object until it stops growing and reads the value N
- The receiver resets the
history.length(going back and pushing a new location)
- The sender detects the reset and go to 1 for sending the next character or finish the transmision.
history.length is 50. An the update-value operation is asynchronous after performing some redirection, though it depends on the way it is done (setting
location.hash seems to be synchronous, but not setting
location). Maybe using other system avoids the race condition and the transmission rate can be increased.
A part from this covert channel, it is possible to abuse this to leak the user's navigation history by probing, as I did some time ago in this probe of concept. I'm still trying to improve the timing constraints to check pages faster, but by now I haven't found a good solution.
*Edit: Ben Hayak suggested to use
onpopstate to fix the bug, and now it's actually much more reliable, thanks :)
Iframe's scrollbar position
location.hash does not only trigger the
onhashchange event, but also sets the focus of an element with an equal identifier and potentially moves the scrollbar to its position. In this way, we could stablish a channel by navigating to different elements of a cross-domain document.View more... 	 PoC
The example is pretty simple and probably uninteresting, but this behaviour has been abused in the real world, in combination with a CSS scroll bar detection trick, in order to probe elements of a cross-domain page and, for example, detect logged users.
In summary, some browsers allow to set the scroll bar brackground via CSS, the trick consists on requesting an image from the attacker domain which will set a cookie. After that we can inspect the
document.cookie to check if the scroll bar was requested.
As well, there is a much more sofisticated (and tricky) attack by Eduardo Vela , where he tries to leak the content of the pages abusing this same principle and being very clever.
Similar to the history object, the
window.frames is shared cross-domain and allows us to inspect its length (shorlink via
window.length), as well as navigate across iframes. This channel creates N frames in a web page, allowing the receiver to read that value.View more... 	 PoC
Apparently, the maximum number of
iframes in Chrome is 1000. Firefox does not seem to limit it, but bigger numbers degredate the performance too much. In any case using a dictionary again with values arround 50 works fine enought.
The protocol uses an iframe as
referee to detect when a message has been send/read:
Receiverloads and stores
referee, creates N iframes and switch
sender.lengthand gets N as
- If not EOF, go to
Notice a little degredation when sending values with bigger N, this could be improved using a frequency-ordered alphabet.
Deletion of cross-domain-referenceable properties (Only chrome. Bug? )
Modern browsers provide a few public functions accessible cross-domainly:
postMessage, blur, focus, close. Thought they can not be modified, Chrome allows to
delete them, throwing an error in cross-domain pages when accessed after deletion or not doing it in the other case.View more... 	 PoC
Additionally to these functions, some other cross-readable properties can be deleted:
parent, opener, length, frames, closed.
This gives us uppon 9 bits (1 byte + EOF) of information to transmit between windows.
The main limitation is that after deletion we can not restore the original function until refreshing the sender page, however we can easily maintain the execution trace between refreshes using the
localStorage or any other persistent mechanism.
This demo is a little bogus, but serves well enough as example.
It also works for
Symbol.toStringTag (on Chrome with
Symbol is a new ES6 API  for creating immutable data types. It contains a special property called
Symbol.toStringTag which is used for the default description of an object (when the
Object.prototype.toString() is called). Since the symbols are available even cross domain, is it possible to use it as a channel.View more... 	 PoC
Actually, use the
toStringTag property is the more direct way. But if the symbols table is shared across domain, it would be also possible to test which keys are used in the parent, for example, and therefore to send information.
Special thanks to Michal Bentkowski for the reference :)