Prototype Pollution in RequestPanel.js getProperties()
Categories
(DevTools :: Netmonitor, defect, P2)
Tracking
(firefox-esr115 wontfix, firefox-esr140148+ fixed, firefox147 wontfix, firefox148+ fixed, firefox149+ fixed)
People
(Reporter: cy1yang, Assigned: bomsy)
References
Details
(Keywords: csectype-priv-escalation, reporter-external, sec-moderate, Whiteboard: [adv-main148+] [adv-esr140.8+])
Attachments
(1 file, 3 external files, 1 obsolete file)
|
5.43 KB,
text/html
|
Details |
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
Steps to reproduce:
Reproduce
- Open the
poc.htmlwith FireFox - Open Firefox DevTools (F12)
- Navigate to the Network tab
- Click one of the test buttons (e.g., "Test 1: proto Parameter")
- In the Network Monitor, click on the POST request that appears
- Click on the "Request" tab/panel to view form data
Actual results:
The Network panel crashes with a TypeError:
The Network panel has crashed.
File Bug Report
TypeError: map[obj.name].push is not a function
React Component Stack
in RequestPanel
in Connect(RequestPanel)
in div
in TabPanel
...
Stacktrace
getProperties/<@resource://devtools/client/netmonitor/src/components/request-details/RequestPanel.js:145:23
getProperties@resource://devtools/client/netmonitor/src/components/request-details/RequestPanel.js:139:16
render@resource://devtools/client/netmonitor/src/components/request-details/RequestPanel.js:211:22
...
Close and reopen the toolbox to clear this error.
Expected results:
Analysis
The getProperties() method at lines 138-150 uses a plain object literal {} as the reduce accumulator. When a form parameter has name __proto__:
map["__proto__"]returnsObject.prototype(not undefined)Object.prototypepasses the checkif (value || value === "")- Code tries to call
Object.prototype.push()โ TypeError crash
Affected Code
Location: RequestPanel.js:138-150
getProperties(arr) {
return arr.reduce((map, obj) => {
const value = map[obj.name];
if (value || value === "") {
if (typeof value !== "object") {
map[obj.name] = [value];
}
map[obj.name].push(obj.value); // โ CRASH when obj.name = "__proto__"
} else {
map[obj.name] = obj.value;
}
return map;
}, {}); // โ BUG: Should be Object.create(null)
}
Trigger location: RequestPanel.js:211
this.getProperties(parseFormData(sections))
Expected results
The code should use Object.create(null) instead of {} as the reduce accumulator, preventing prototype chain lookups:
return arr.reduce((map, obj) => {
// ... same logic ...
}, Object.create(null)); // โ FIX: null-prototype object
This is exactly how the same vulnerability was fixed in:
UrlPreview.jsline 228 (bug 1801542, commit ccc6e2a65496)CookiesPanel.jsline 111
Comment 1•9 months ago
|
||
The testcase creates a request with a fetch call where the body is set to __proto__=evil_value&normal=test. Devtools then apparently parses that and assigns the key/value pairs into an object.
A more thorough fix here would be to use a Map rather than a plain object as an accumulator, so that there isn't a magic __proto__ key in the first place - or perhaps we can even use URLSearchParams or Form or something else builtin to do this parsing for us?
I also wonder if there is a way to opt the devtools env in which this code runs into the system principal environment where we just don't allow modifying the object prototype. Jan, do you know?
Comment 2•9 months ago
|
||
(In reply to :Gijs (he/him) from comment #1)
I also wonder if there is a way to opt the devtools env in which this code runs into the system principal environment where we just don't allow modifying the object prototype. Jan, do you know?
We have the setFreezeBuiltins(true) option for RealmCreationOptions to freeze/seal (some) builtin objects to make it harder to take advantage of prototype pollution. This flag is set in mozJSModuleLoader::CreateLoaderGlobal and the if-statement there suggests this already happens for the DevTools loader, but I don't know if that loader is used for the JS code executed here:
creationOptions.setFreezeBuiltins(true) <snip>
if (IsDevToolsLoader()) {
...
This wouldn't prevent the bug here though because we don't modify any prototypes, we just try to call Object.prototype.push and this property doesn't exist so we throw an exception.
Updated•9 months ago
|
Comment 3•9 months ago
|
||
The loader where IsDevToolsLoader()==true is the one used to load DevTools backend, but only when using the browser toolbox.
Here, the netmonitor code involved is executed from an iframe document's global (netmonitor/index.html). Can we freeze builtins for a document?
Note that I wouldn't be surprised if some of the frontend deps (react, redux,...) are modifying the builtins :/
| Assignee | ||
Comment 4•9 months ago
|
||
Updated•9 months ago
|
Comment 5•9 months ago
|
||
Gijs, should this still be a security bug, it seems to lead to a crash ? If yes, should it be sec-low, similar as https://bugzilla.mozilla.org/show_bug.cgi?id=1801542 ?
Updated•9 months ago
|
Updated•9 months ago
|
Comment 6•9 months ago
•
|
||
(In reply to Alexandre Poirot [:ochameau] from comment #3)
The loader where
IsDevToolsLoader()==trueis the one used to load DevTools backend, but only when using the browser toolbox.Here, the netmonitor code involved is executed from an iframe document's global (netmonitor/index.html). Can we freeze builtins for a document?
I don't see an API for windows/documents looking at the reference in comment #2 but I assume we already do this for browser windows so I figure it must be possible - but perhaps it's hardcoded for system principal windows or I'm misremembering - Jan?
Note that I wouldn't be surprised if some of the frontend deps (react, redux,...) are modifying the builtins :/
I don't think react and redux will (they're new enough to be from a time when we all agreed that doing this is bad, I would have thought?), but it's possible other things do. Anyway, trying is the only way to be sure.
(In reply to Julian Descottes [:jdescottes] from comment #5)
Gijs, should this still be a security bug, it seems to lead to a crash ? If yes, should it be sec-low, similar as https://bugzilla.mozilla.org/show_bug.cgi?id=1801542 ?
I'm unclear on what we mean by "crash" here; I can't easily safely test this right now. The way I read comment 0 actually made me think this was running into a JS exception, not a "real" crash. I also don't know that we're confident that a more severe outcome is not possible. It's now been marked as sec-moderate . Even if a more severe outcome was not possible, just the "crash" (real or exception-based) could still be a DoS type vulnerability, which we still treat as sec bug (though often sec-low instead of sec-moderate).
Comment 7•9 months ago
|
||
(In reply to :Gijs (he/him) from comment #6)
I don't see an API for windows/documents looking at the reference in comment #2 but I assume we already do this for browser windows so I figure it must be possible - but perhaps it's hardcoded for system principal windows or I'm misremembering - Jan?
We tried to enable this for system principal windows but it caused test failures so in the end we only did it for the shared system global (bug 1771084 comment 17).
I just pushed that change to try again and there is a lot of orange, especially in DevTools tests:
Comment 9•9 months ago
|
||
Comment 10•9 months ago
|
||
:bomsy, adding a need info for uplift requests.
I'm not sure if this is needed on esr115/esr140. It will need a rebase if needed on the ESR branches.
| Assignee | ||
Comment 11•9 months ago
|
||
Highlights of this patch
- Use
Object.create(null)instead of{}when mapping the format data sections array to a array of dictionaries. - Use
URLSearchParamsto parse the format-data sections - Added tests to assert the fix for the prototype pollution.
Original Revision: https://phabricator.services.mozilla.com/D279525
Updated•9 months ago
|
| Assignee | ||
Comment 12•9 months ago
|
||
Highlights of this patch
- Use
Object.create(null)instead of{}when mapping the format data sections array to a array of dictionaries. - Use
URLSearchParamsto parse the format-data sections - Added tests to assert the fix for the prototype pollution.
Original Revision: https://phabricator.services.mozilla.com/D279525
Updated•9 months ago
|
Comment 13•9 months ago
•
|
||
:bomsy, you might already be working through it but to call out:
- This will need a beta uplift request
- The uplift request needs an uplift request form for review
Comment 14•9 months ago
|
||
firefox-beta Uplift Approval Request
- User impact if declined: Might crash developer tools for users
- Code covered by automated testing: yes
- Fix verified in Nightly: yes
- Needs manual QE test: no
- Steps to reproduce for manual QE testing:
- Risk associated with taking this patch: low
- Explanation of risk level: Low risk. Medium size JS patch to fix and refactor the code.
- String changes made/needed: None
- Is Android affected?: no
| Assignee | ||
Comment 15•9 months ago
|
||
Highlights of this patch
- Use
Object.create(null)instead of{}when mapping the format data sections array to a array of dictionaries. - Use
URLSearchParamsto parse the format-data sections - Added tests to assert the fix for the prototype pollution.
Original Revision: https://phabricator.services.mozilla.com/D279525
| Assignee | ||
Updated•9 months ago
|
Updated•9 months ago
|
Updated•9 months ago
|
Comment 16•9 months ago
|
||
| uplift | ||
Updated•9 months ago
|
Updated•9 months ago
|
Updated•9 months ago
|
Comment 17•9 months ago
|
||
| uplift | ||
Updated•9 months ago
|
Updated•9 months ago
|
Comment 18•9 months ago
|
||
| uplift | ||
Comment 19•9 months ago
|
||
| uplift | ||
Comment 20•9 months ago
•
|
||
Backed out of ESR115 for causing test failures
ESR115 is eol in the next release, so setting it to wontfix instead.
Updated•9 months ago
|
Updated•8 months ago
|
Updated•8 months ago
|
Updated•8 months ago
|
Updated•5 months ago
|
Description
•