Firefox 122.01: postMessage event.source validation bypass
Categories
(Core :: DOM: postMessage, defect, P2)
Tracking
()
People
(Reporter: frsantos9989, Assigned: edenchuang)
References
Details
(Keywords: csectype-mitigation-bypass, reporter-external, sec-moderate, Whiteboard: [fixing in 2003504][adv-main150+])
Attachments
(1 file, 1 obsolete file)
|
385 bytes,
text/html
|
Details |
| Comment hidden (obsolete) |
| Reporter | ||
Comment 1•2 years ago
•
|
||
(Droping the md report here because I didn't know the original placeholder allowed md)
Firefox 122.01: postMessage event.source validation bypass
Summary
I identified a security vulnerability in Firefox 122.01 that allows sending postMessage from cross-origin sources. This bypasses the expected event.source validation, potentially leading to security risks such as cross-origin actions or data exposure.
By accessing win.location.constructor.constructor of the initial about:blank page, an attacker can execute this.postMessage in the context of the cross-origin page.
Environment
- Browser Version: Firefox 122.01.
- Operating System:
ZorinOS 6.5.0-17-generic #17~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC
Steps to Reproduce
-
Host the following HTML page on a server or open it directly in a browser:
<html> <body> <script> win = open("https://mdn.github.io/webextensions-examples/content-script-page-script-messaging.html"); loc = win.location.constructor.constructor; setTimeout(() => { loc('return this.postMessage({"direction":"from-content-script","message":"Message from the page"}, "*")')() }, 3000) </script> </body> </html> -
After 3 seconds, observe that a message is sent to the demo extension's page, bypassing the expected
event.sourcevalidation.
Expected Behavior
The event.source property value should be a ProxyWindow to the same-origin window.
Observed Behavior
The event.source property value is a ProxyWindow to the cross-origin window.
Impact
A threat actor can abuse this behavior to compromise Firefox extensions and web applications that validate the origin of incoming message with the event.source property. This can lead to other client-side vulnerabilities such as Cross-Site Scriping, Prototye Pollution, DOM Clobering...
Recommendations
Block access to the Function constructor of any property of a window loading about:blank page if it will be replaced by a cross-origin document.
| Comment hidden (obsolete) |
Updated•2 years ago
|
Updated•2 years ago
|
Comment 3•2 years ago
|
||
Comment 4•2 years ago
|
||
Comment 5•2 years ago
|
||
It looks like the MDN page listens for the postMessage via this bit of code:
window.addEventListener("message", function(event) {
if (event.source == window &&
event.data.direction &&
event.data.direction == "from-content-script") {
alert("Page script received message: \"" + event.data.message + "\"");
}
});
Updated•2 years ago
|
Comment 6•2 years ago
•
|
||
I don't think this is postMessage specific.
loc = win.location.constructor.constructoris effectivelyloc = win.Function. Both are supposed to fail with DOMException SecurityError and it does fail after the new window load completes, but it does not fail before that and successfully return the JS Function constructor.loc("this.foo")()now does weird thing wherethisis the third party window. Accessing anything e.g.this.consolestill throws SecurityError, but accessing postMessage is allowed just aswin.postMessageis allowed, except nowpostMessagethinks the caller is not cross origin.
Chrome also allows the access described at 1, while the returned Function constructor is not functional at all as loc("any string")() throws TypeError saying "no access".
As such I'll pass this back to Core & HTML.
Edit: Safari also has the same behavior described at 2 except it sets event.source to null.
Updated•2 years ago
|
Comment 7•2 years ago
|
||
(But perhaps it fits better in SpiderMonkey?)
Comment 8•2 years ago
|
||
This is still postMessage :) Safari behavior might be quite doable.
Comment 9•2 years ago
|
||
Setting null? Why not just block such script run at all?
Comment 10•2 years ago
|
||
The recommended check when receiving a postMessage() is to check event.origin, not compare event.source. It's bad that it's misleading, but do lots of people do it this wrong way?
Observed Behavior
The event.source property value is a ProxyWindow to the cross-origin window.
Is it? Or is it rather that event.source is actually the receiver's window and nobody has access to the other window? They shouldn't be equal, but Firefox is the one that mixed it up, but it doesn't mean the attacking page has access to it. if event.source == window then the event listener hasn't gained any powers because we know window is safe.
Comment 11•2 years ago
|
||
peterv, are you perhaps looking into this some more? (This was discussed during DOM security meeting)
Comment 12•2 years ago
|
||
Yeah, I'll take it.
| Reporter | ||
Comment 13•2 years ago
|
||
(In reply to Daniel Veditz [:dveditz] from comment #10)
The recommended check when receiving a postMessage() is to check event.origin, not compare event.source. It's bad that it's misleading, but do lots of people do it this wrong way?
Observed Behavior
The event.source property value is a ProxyWindow to the cross-origin window.Is it? Or is it rather that event.source is actually the receiver's window and nobody has access to the other window? They shouldn't be equal, but Firefox is the one that mixed it up, but it doesn't mean the attacking page has access to it. if event.source == window then the event listener hasn't gained any powers because we know window is safe.
Hello, I hope all is well with everyone. :)
I do not think the question: "It's bad that it's misleading, but do lots of people do it this wrong way?" is an important question to ask here. Where does that question leads us? What can we gain from thinking about that? Would you relax on this security issue if proven that lots of people don't do it this wrong way?
People are led to implement proper BAC rules everyday from various sources. It's still the OWASP TOP 1 2021. If it's still OWASP TOP 1 when people are led to mitigate BAC issues, imagine if they were mislead.
On another note:
I did not gain access to a cross-origin page from an attacking page.
I deem the impact of confusing Firefox about who is event.source worthy of a report: Mozilla's add-ons team included an example extension that allows exploiting this issue: https://github.com/mdn/webextensions-examples/blob/main/page-to-extension-messaging/content-script.js
Updated•2 years ago
|
Comment 14•2 years ago
|
||
(In reply to Daniel Veditz [:dveditz] from comment #10)
The recommended check when receiving a postMessage() is to check event.origin, not compare event.source. It's bad that it's misleading, but do lots of people do it this wrong way?
event.source === window is (commonly?) used to make sure that the message came from the window itself. This is reasonable because it is usually not possible for two (cross-origin) scripts to be actively associated with the same window at the same time. Checking event.origin === origin && event.source === window would seemingly work, except in case of opaque origins, in which case the serialization of the origin is "null".
Expected Behavior
The event.source property value should be a ProxyWindow to the same-origin window.
I think that a more sensible expected behavior is to just drop the message. There is no situation where it makes sense to deliver a message from a window to the same outer window, when the inner window has changed to a cross-origin one. While a check could be added to postMessage to fix this, in theory it is possible for the navigation to occur between a postMessage call and the event dispatch.
Another option, if feasible, is to not execute JS code when content has navigated. Judging by comment #6, Chrome does this, which is why the situation does not happen in Chrome.
Updated•2 years ago
|
Updated•2 years ago
|
| Reporter | ||
Comment 15•1 year ago
|
||
Hello.
How are you?
I havn't been able to escalate this. Do you have new info on your end? Thank you.
| Reporter | ||
Comment 16•1 year ago
|
||
Request information to ensure the ping is received. Thank you. Have a great day. :)
| Assignee | ||
Comment 17•1 year ago
|
||
I think currently we don't have a conclusion about which one in comment #6 should be implemented.
From an implementation aspect, we could have a quick fix for this security bug, a solution that nulls the event.source, and then a follow-up bug for blocking script execution.
Olli, how do you think?
Updated•1 year ago
|
Comment 18•1 year ago
|
||
Yes, the plan is to have the same behavior what webkit has.
Comment 19•1 year ago
|
||
If I'm reading the code correctly webkit can return non-null source if the right timing is used, and Chrome throwing exception is against the specs.
Comment 20•1 year ago
|
||
Comment 21•1 year ago
|
||
Comment on attachment 9472853 [details]
(secure)
Security Approval Request
- How easily could an exploit be constructed based on the patch?: I think relatively easily
- Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: Unknown
- Which branches (beta, release, and/or ESR) are affected by this flaw, and do the release status flags reflect this affected/unaffected state correctly?:
- If not all supported branches, which bug introduced the flaw?: None
- Do you have backports for the affected branches?: No
- If not, how different, hard to create, and risky will they be?: The code looks the same also in esr115
- How likely is this patch to cause regressions; how much testing does it need?: This is unfortunately not-specified behavior (I'll need to file a security sensitive spec bug).
But the situation should happen only in very rare cases. - Is the patch ready to land after security approval is given?: Yes
- Is Android affected?: Yes
Comment 22•1 year ago
|
||
Comment on attachment 9472853 [details]
(secure)
Approved to land and request uplift
Updated•1 year ago
|
Comment 23•1 year ago
|
||
Comment 24•1 year ago
|
||
Comment 25•1 year ago
|
||
Push where failures first started: https://treeherder.mozilla.org/jobs?repo=autoland&group_state=expanded&selectedTaskRun=biyqbSMuT8Gs5cA6VaU3Lg.0&resultStatus=testfailed%2Cbusted%2Cexception%2Cretry%2Cusercancel&revision=7d198c9f032e6c16d7f9798c504b3b5708ce9760
Failure log: https://treeherder.mozilla.org/logviewer?job_id=500248839&repo=autoland&lineNumber=3495
Comment 26•1 year ago
|
||
Boo. I had run the tests.
Comment 27•1 year ago
|
||
| Reporter | ||
Comment 28•1 year ago
|
||
Hello. I hope everyone is well.
How's the fix looking? Can you share code details of this bug? I would love to learn what happened on your side.
Would it be possible to write a blog post about this when it's fixed? It would be posted at https://paranoidmoth.github.io/
For the rock music fans here, paranoidmoth is a mild reference to paranoid android from radiohead.
Thanks! :)
Comment 29•1 year ago
|
||
There is an r+ patch which didn't land and no activity in this bug for 2 weeks.
:smaug, could you have a look please?
If you still have some work to do, you can add an action "Plan Changes" in Phabricator.
For more information, please visit BugBot documentation.
Updated•1 year ago
|
| Reporter | ||
Comment 30•1 year ago
|
||
Hello. I hope everything is well.
Any feedback on this bug?
Comment 31•1 year ago
|
||
Need to figure out how to schedule the postMessage task in a bit different way so that we don't break that test.
| Reporter | ||
Comment 32•1 year ago
|
||
Hello. I hope you are well.
Have you figured out how to schedule the postMessage task?
I would love to dive deep into your progress.
Thanks. :)
| Reporter | ||
Updated•1 year ago
|
| Assignee | ||
Comment 33•1 year ago
|
||
Olli, I take the bug to investigate the test failures.
Updated•1 year ago
|
Comment 34•1 year ago
|
||
The test failures are clear. We need to do some interesting scheduling related magic.
Comment 35•9 months ago
|
||
Comment on attachment 9472853 [details]
(secure)
Revision D242075 was moved to bug 2003504. Setting attachment 9472853 [details] to obsolete.
| Assignee | ||
Updated•9 months ago
|
Comment 36•9 months ago
|
||
I believe the patch was moved to a public bug, which is fine, but since it hasn't landed this one isn't fixed, is it?
Making this "depend on" the public bug, but reopening so we can correctly track status (advisories, bounties) of the security bug separately.
Comment 37•6 months ago
|
||
Bug 2003504 was resolved. Can this bug be closed as FIXED now?
Updated•6 months ago
|
Updated•6 months ago
|
Updated•6 months ago
|
Updated•5 months ago
|
Updated•5 months ago
|
Updated•5 months ago
|
Updated•4 months ago
|
Updated•22 days ago
|