The Mystery of postMessage – Ron Chan


From time to time we see postMessage bug in H1 hacktivity, some write ups mentioning the word postMessage, but do you really know what is going on with postMessage? Honestly I didn’t, but now I do, after reading the docs and some experiment and a real life bug, I am confident to say I know what it is about finally.

First thing first, the docs tell us postMessage is an attempt to beat “circumvent SOP”, in a controlled way. This sentence should draw every hacker’s attention, SOP bypass is great, but how? Here is some example.

https://example.com/test.html

var popup = window.open('https://ngailong.com');
function post(){popup.postMessage("hi","*")}
setInterval(post,1000);

If https://ngailong.com has no message event listener setup, nothing will happen. If it has message event listener setup, depends how it is setup, it could be vulnerable to XSS or info disclosure.

https://ngailong.com/

window.addEventListener("message", function(event){alert(event.data);}, false);

Since I setup setInterval function to postMessage every 1 second, https://ngailong.com will have an alert box every 1 second accordingly.

What are some real life example use of postMessage?

Maybe something.google.com needs to get the email address of the user, so it needs to fetch the email address from accounts.google.com, CORS is problematic for such a simple task, since it needs server side code change, postMessage is easy and fast, only javascript change is necessary.

This concept seems pretty neat right? The thought that postMessage could ignore fundamental security of web browser (SOP) is very exciting. ANY window can reference ANY other window at any time, X-Frame-Options couldn’t stop you, you can always send something to any window as long as the window has setup a postMessage event listener, right? Not so fast.

From Mozilla

Consequently, any event listener used to receive messages must first check the identity of the sender of the message, using the origin and possibly source properties. This cannot be overstated: Failure to check the origin and possibly source properties enables cross-site scripting attacks.

Oh so every event listener needs to check the identity of the sender of the message, before executing any javascript. This is written in the spec, every developer must have read this note and follow the rules before using postMessage! Unfortunately this is not the case, if the developer forgot to check the event origin in evenListener, it may cause XSS or sensitive information disclosure.

Try the above example again but this time we check the origin.

https://example.com/test.html

var popup = window.open('https://ngailong.com');
function post(){popup.postMessage("hi","*")}
setInterval(post,1000);

If https://ngailong.com has no message event listener setup, nothing will happen. If it has message event listener setup, depends how it is setup, it could be vulnerable to XSS or info disclosure.

https://ngailong.com

window.addEventListener("message", function(event){if (event.origin!="https://ngailong.com"){return}alert(event.data);}, false);

This time, no alert box, the reason is that this time I check if even.origin!=https://ngailong.com, end the function immediately. So posting a message from https://example.com will cause the event.origin equals to https://example.com, thus failing the test.

I hope now you have some understanding of  postMessage. But reading alone is not enough, let’s try to steal the {token} of the below HTML, to see if you really 100% understand what is going on with postMessage.

(These exercise aren’t for the experts, just for someone who want to have a feeling of how postMessage bug works)

Level – 0

var token ="super_secret_token";function receiveMessage(event) { event.source.postMessage(token, event.origin); }window.addEventListener("message", receiveMessage, false);

Level – 1

var token ="super_secret_token";function receiveMessage(event) { if (/^(?:https?:)?//www.target.com/i.test(event.origin)){event.source.postMessage(token, event.origin);}window.addEventListener("message", receiveMessage, false);

You got them all? Good, now go find some postMessage to play.

 

 

The exercise idea is originated from two blog posts from Detectify. Kudos to them.



Source link