Closed Bug 1943804 (CVE-2025-6432) Opened 11 months ago Closed 8 months ago

DNS requests bypass the configured proxy. Privacy may be compromised.

Categories

(Core :: Networking: Proxy, defect, P2)

Firefox 134
defect

Tracking

()

RESOLVED FIXED
140 Branch
Tracking Status
firefox-esr115 --- wontfix
firefox-esr128 --- wontfix
firefox138 --- wontfix
firefox139 --- wontfix
firefox140 + fixed

People

(Reporter: ackbeat, Assigned: kershaw)

Details

(4 keywords, Whiteboard: [necko-triaged][necko-priority-queue][adv-main140+])

Attachments

(3 files)

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:134.0) Gecko/20100101 Firefox/134.0

Steps to reproduce:

In Multi-Account container I use socks proxy: socks://127.0.0.1:1080
I can confirm, that when I visit sites the DNS requests go through my proxy.
However! When there is a typo in the address, so that the domain name does not exist...

Actual results:

I see Firefox trying to use the system's default resolver. I see these DNS requests on my router.

Expected results:

Firefox should NEVER try to send requests anywhere other than the proxy, provided the proxy is configured. Does not matter if domain name exists or not. Because the original site can be easily deduced from the address with typo.

Group: firefox-core-security → network-core-security
Component: Untriaged → Networking: Proxy
Product: Firefox → Core

I suspect this is related to the newish fallback behavior for "DNS over HTTPS", assuming you're not using "Max Protection". You're right that the "use system settings" fallback should not ignore/override the "Proxy DNS when using SOCKS" prefs!

Could you confirm that there's no problem if you change the DNS over HTTPS settings to "Max Protection" (typo should give you an error page) or "Off" (all requests presumably honor the SOCKS setting)?

Flags: needinfo?(ackbeat)
Status: UNCONFIRMED → NEW
Ever confirmed: true

Redirect a needinfo that is pending on an inactive user to the triage owner.
:jesup, since the bug has recent activity, could you have a look please?

For more information, please visit BugBot documentation.

Flags: needinfo?(ackbeat) → needinfo?(rjesup)

redirect to valentin

Flags: needinfo?(rjesup) → needinfo?(valentin.gosu)

I am able to reproduce this issue.
Using MultiAccountContainers, if I set the proxy for a container to socks://hello:8080 and attempt to load google.com instead of the usual "This proxy is refusing connections" I get Hmm. We’re having trouble finding that site. errors, and indeed I see all of the DNS lookups for google.com in Wireshark.
This is indeed a proxy issue - I think it's the proxy error (in this case DNS) being passed presented as the error for google.com
The proxy Info coming from MAC has proxyDNS = true, which means we are leaking DNS when the proxy connection fails, which seems like a valid bug.

I think I'll try to work on this, but if I don't get to it soon, my approach would be:

Severity: -- → S3
Flags: needinfo?(valentin.gosu)
Priority: -- → P2
Whiteboard: [necko-triaged][necko-priority-queue]

I believe the DNS leak is caused by the speculative connection made here.
Since we don’t set the callbacks, the tab ID for this dummy channel is -1.

As shown in the web extension code below, a valid tab ID is required for assigning proxy info:

  async handleProxifiedRequest(requestInfo) {
    // The following blocks potentially dangerous requests for privacy that come without a tabId

    if(requestInfo.tabId === -1) {
      return {};
    }

As a result, the extension fails to set proxy info on the channel, which causes the DNS lookup from the speculative connection to bypass the proxy — leading to a DNS leak.

Hi Gijs,

Do you probably know how to get the load context in RemoteWebNavigation? ideally, I'd like to do something like this.
Apparently, this._browser is a remote browser and I don't know how to get load context from it.

Thanks.

Flags: needinfo?(gijskruitbosch+bugs)

(In reply to Kershaw Chang [:kershaw] from comment #6)

Hi Gijs,

Do you probably know how to get the load context in RemoteWebNavigation? ideally, I'd like to do something like this.
Apparently, this._browser is a remote browser and I don't know how to get load context from it.

Well, the example from SearchEngine relies on docshell which is in the child process and RemoteWebNavigation is in the parent, so that won't work.

But if you look for nsILoadContext then allegedly BrowsingContexts implement this, so you could try using this._browser.browsingContext, maybe? Does that work?

I'm a bit surprised because this is a very different discussion from the one in comment #4. Also comment #5 mentions an extension but I don't see the steps involving an extension so I don't understand how that's related...

Flags: needinfo?(gijskruitbosch+bugs) → needinfo?(kershaw)

(In reply to :Gijs (he/him) from comment #7)

(In reply to Kershaw Chang [:kershaw] from comment #6)

Hi Gijs,

Do you probably know how to get the load context in RemoteWebNavigation? ideally, I'd like to do something like this.
Apparently, this._browser is a remote browser and I don't know how to get load context from it.

Well, the example from SearchEngine relies on docshell which is in the child process and RemoteWebNavigation is in the parent, so that won't work.

But if you look for nsILoadContext then allegedly BrowsingContexts implement this, so you could try using this._browser.browsingContext, maybe? Does that work?

Unfortunately, no. I got the error below when using this._browser.browsingContext.

JavaScript error: resource://gre/modules/RemoteWebNavigation.sys.mjs, line 114: NS_ERROR_XPC_BAD_CONVERT_JS: Could not convert JavaScript argument arg 2 [nsISpeculativeConnect.speculativeConnect]

I'm a bit surprised because this is a very different discussion from the one in comment #4. Also comment #5 mentions an extension but I don't see the steps involving an extension so I don't understand how that's related...

As mentioned in comment #0, reproducing this bug requires installing the MultiAccountContainers extension.

While investigating, I found that the DNS leak originates from a speculative connection created in RemoteWebNavigation.
More details:

  1. The speculative connection is created here.
  2. For this connection, we create a dummy channel, which is used for proxy resolution.
  3. During proxy resolution, MultiAccountContainers should set the appropriate proxy info based on the dummy channel’s metadata.
  4. However, since we didn’t provide a load context, MultiAccountContainers fails to assign proxy info. As a result, the speculative connection is made without a proxy.
  5. Because there's no proxy assigned, Firefox performs a DNS lookup directly — which causes the leak.
Flags: needinfo?(kershaw)

(In reply to Kershaw Chang [:kershaw] from comment #8)

Thanks for the more detailed investigation and explaining it, and sorry for being slow on the uptake.

(In reply to :Gijs (he/him) from comment #7)

(In reply to Kershaw Chang [:kershaw] from comment #6)

Hi Gijs,

Do you probably know how to get the load context in RemoteWebNavigation? ideally, I'd like to do something like this.
Apparently, this._browser is a remote browser and I don't know how to get load context from it.

Well, the example from SearchEngine relies on docshell which is in the child process and RemoteWebNavigation is in the parent, so that won't work.

But if you look for nsILoadContext then allegedly BrowsingContexts implement this, so you could try using this._browser.browsingContext, maybe? Does that work?

Unfortunately, no. I got the error below when using this._browser.browsingContext.

JavaScript error: resource://gre/modules/RemoteWebNavigation.sys.mjs, line 114: NS_ERROR_XPC_BAD_CONVERT_JS: Could not convert JavaScript argument arg 2 [nsISpeculativeConnect.speculativeConnect]

Aha. I'm sorry for giving the wrong answer - you asked for nsILoadContext, but the interface definition for that call is: https://searchfox.org/mozilla-central/rev/e92cea05ec336d87e495fb42d02652ba2e839592/netwerk/base/nsISpeculativeConnect.idl#44 which says that needs an nsIInterfaceRequestor object.

Then AFAICT we try to get a loadContext out of that here using getInterface. But it means that passing a loadcontext directly won't work.

Instead, you should find or create (!) some object that supports getInterface (in JS) or the C++ equivalent, and can return a load context when asked via that method.

Something like this appears to work, from the browser console (at least doesn't throw or log any exceptions immediately, but I haven't checked if it creates a connection etc.):

Services.io.speculativeConnect(
  makeURI("https://example.com/"),
  Services.scriptSecurityManager.createNullPrincipal({}),
  ({QueryInterface: ChromeUtils.generateQI([Ci.nsIInterfaceRequestor]), getInterface(iid) { if (iid.equals(Ci.nsILoadContext)) { return gBrowser.selectedBrowser.browsingContext } } }), 
  false
);

obviously you'll want to pass the correct browser's browsingContext there, but this works from the browser console.

Flags: needinfo?(kershaw)

Thanks a lot for the provided example.
It works, and I'll submit a patch soon.

Flags: needinfo?(kershaw)
Assignee: nobody → kershaw
Status: NEW → ASSIGNED
Attachment #9478941 - Attachment description: Bug 1943804 - Disallow speculative connections lack callbacks and proxy filters are registered, r=#necko → Bug 1943804 - Disallow speculative connections that lack callbacks when proxy filters are registered, r=#necko
Pushed by kjang@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/f184a6c17119 Make speculative connections created from RemoteWebNavigation have tabId, r=Gijs,necko-reviewers,valentin https://hg.mozilla.org/integration/autoland/rev/779ff79f5223 Disallow speculative connections that lack callbacks when proxy filters are registered, r=necko-reviewers,valentin

Backed out for linting failures:
https://hg.mozilla.org/integration/autoland/rev/80ce77579cf09a9113b51e7a6af1828d34f5cf83

Push with failures
Failure log

TEST-UNEXPECTED-ERROR | /builds/worker/checkouts/gecko/netwerk/test/unit/test_speculative_connect.js:363:7 | 'pps' is not defined. (no-undef)
TEST-UNEXPECTED-ERROR | /builds/worker/checkouts/gecko/netwerk/test/unit/test_speculative_connect.js:367:9 | 'proxy_auth' is not defined. (no-undef)
TEST-UNEXPECTED-ERROR | /builds/worker/checkouts/gecko/netwerk/test/unit/test_speculative_connect.js:368:9 | 'proxy_isolation' is not defined. (no-undef)
TEST-UNEXPECTED-ERROR | /builds/worker/checkouts/gecko/toolkit/components/remotebrowserutils/RemoteWebNavigation.sys.mjs:16:3 | Expected to return a value at the end of method 
Flags: needinfo?(kershaw)
Flags: needinfo?(kershaw)
Pushed by kjang@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/4d793c8aade9 Make speculative connections created from RemoteWebNavigation have tabId, r=Gijs,necko-reviewers,valentin https://hg.mozilla.org/integration/autoland/rev/d4c83c44d6e2 Disallow speculative connections that lack callbacks when proxy filters are registered, r=necko-reviewers,valentin

Backed out for causing xpcshell failures @ test_ext_proxy_speculative.js

TEST-UNEXPECTED-FAIL | xpcshell-remote.toml:toolkit/components/extensions/test/xpcshell/test_ext_proxy_speculative.js | test_speculative_connect - [test_speculative_connect : 66] Extension left running at test shutdown - "running" == "unloaded"

Flags: needinfo?(kershaw)
Flags: needinfo?(kershaw)
Pushed by kjang@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/6bbb8875d5c6 Make speculative connections created from RemoteWebNavigation have tabId, r=Gijs,necko-reviewers,valentin https://hg.mozilla.org/integration/autoland/rev/abffbde1e3d7 Disallow speculative connections that lack callbacks when proxy filters are registered, r=necko-reviewers,valentin
Group: network-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 8 months ago
Resolution: --- → FIXED
Target Milestone: --- → 140 Branch

The patch landed in nightly and beta is affected.
:kershaw, is this bug important enough to require an uplift?

For more information, please visit BugBot documentation.

Flags: needinfo?(kershaw)

I think it's fine to let this ride the train, since this is a sec-low bug and we have this behavior for a long time.

QA Whiteboard: [sec] [qa-triage-done-c141/b140]
Flags: qe-verify-
Whiteboard: [necko-triaged][necko-priority-queue] → [necko-triaged][necko-priority-queue][adv-main140+]
Alias: CVE-2025-6432
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: