Closed Bug 1944126 (CVE-2025-1931) Opened 1 year ago Closed 1 year ago

heap-use-after-free in WebTransportChild::RecvRemoteClosed

Categories

(Core :: DOM: Networking, defect, P1)

Firefox 136
defect

Tracking

()

RESOLVED FIXED
137 Branch
Tracking Status
firefox-esr115 136+ fixed
firefox-esr128 136+ fixed
firefox134 --- wontfix
firefox135 --- wontfix
firefox136 + fixed
firefox137 + fixed

People

(Reporter: ssherkito, Assigned: jesup)

References

Details

(Keywords: csectype-uaf, reporter-external, sec-high, Whiteboard: [necko-triaged][necko-priority-queue][adv-main136+][adv-esr128.8+][adv-esr115.21+])

Attachments

(5 files)

Attached file asan.log

User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0
Firefox for Android

Steps to reproduce:

class WebTransportChild : public PWebTransportChild { public: NS_INLINE_DECL_REFCOUNTING(WebTransportChild) explicit WebTransportChild(WebTransport* aTransport) : mTransport(aTransport) {} ... protected: WebTransport* mTransport; // WebTransport holds a strong reference to us, and // calls Shutdown() before releasing it virtual ~WebTransportChild() { MOZ_ASSERT(!mTransport); } }; [0]

void WebTransport::Cleanup(WebTransportError* aError, const WebTransportCloseInfo* aCloseInfo, ErrorResult& aRv) { ... // Step 12: if (aCloseInfo) { // 12.1: Resolve closed with closeInfo. LOG(("Resolving mClosed with closeinfo")); mClosed->MaybeResolve(*aCloseInfo); [1] // 12.2: Assert: ready is settled. MOZ_ASSERT(mReady->State() != Promise::PromiseState::Pending); // 12.3: Close incomingBidirectionalStreams // This keeps the clang-plugin happy RefPtr<ReadableStream> stream = mIncomingBidirectionalStreams; stream->CloseNative(cx, IgnoreErrors()); // 12.4: Close incomingUnidirectionalStreams stream = mIncomingUnidirectionalStreams; stream->CloseNative(cx, IgnoreErrors());

The WebTransport Renderer IPC channel objectmanages the WebTransport Owner object as a raw pointer. [0]

The vulnerability occurs because the Cleanup function is called in the RecvRemoteClosed function, and a user event is called in that function. [1]

https://searchfox.org/mozilla-central/source/dom/webtransport/child/WebTransportChild.h#51 [0]
https://searchfox.org/mozilla-central/source/dom/webtransport/child/WebTransportChild.cpp#34 [1]

The http3 server was created using the open source https://github.com/aiortc/aioquic/tree/main and the demo.py wt function code was edited.

An easy patch that I thought of would be to either pass a WebTransport object to the stack in RecvRemoteClosed as a RefPtr, or check by handling WeakPtr since there is no part of the code that references the WebTransport object later.

Attached file poc.html
Attached file aioquic-main.zip

(In reply to sherkito from comment #0)

Created attachment 9462034 [details]
asan.log

User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0
Firefox for Android

Steps to reproduce:

 public:
  NS_INLINE_DECL_REFCOUNTING(WebTransportChild)
  explicit WebTransportChild(WebTransport* aTransport)
      : mTransport(aTransport) {}
 ...
 protected:
  WebTransport* mTransport;  // WebTransport holds a strong reference to us, and
                             // calls Shutdown() before releasing it
  virtual ~WebTransportChild() { MOZ_ASSERT(!mTransport); }
}; [0]```

```void WebTransport::Cleanup(WebTransportError* aError,
                           const WebTransportCloseInfo* aCloseInfo,
                           ErrorResult& aRv) {
   ...
  // Step 12:
  if (aCloseInfo) {
    // 12.1: Resolve closed with closeInfo.
    LOG(("Resolving mClosed with closeinfo"));
    mClosed->MaybeResolve(*aCloseInfo); [1]
    // 12.2: Assert: ready is settled.
    MOZ_ASSERT(mReady->State() != Promise::PromiseState::Pending);
    // 12.3: Close incomingBidirectionalStreams
    // This keeps the clang-plugin happy
    RefPtr<ReadableStream> stream = mIncomingBidirectionalStreams;
    stream->CloseNative(cx, IgnoreErrors());
    // 12.4: Close incomingUnidirectionalStreams
    stream = mIncomingUnidirectionalStreams;
    stream->CloseNative(cx, IgnoreErrors());```

The WebTransport Renderer IPC channel objectmanages the WebTransport Owner object as a raw pointer. [0] 

 The vulnerability occurs because the Cleanup function is called in the RecvRemoteClosed function, and a user event is called in that function. [1]

`https://searchfox.org/mozilla-central/source/dom/webtransport/child/WebTransportChild.h#51 [0]`
`https://searchfox.org/mozilla-central/source/dom/webtransport/child/WebTransportChild.cpp#34 [1]`

The http3 server was created using the open source `https://github.com/aiortc/aioquic/tree/main` and the demo.py `wt` function code was edited.

An easy patch that I thought of would be to either pass a WebTransport object to the stack in RecvRemoteClosed as a RefPtr, or check by handling WeakPtr since there is no part of the code that references the WebTransport object later.
Group: firefox-core-security → network-core-security
Component: Untriaged → DOM: Networking
Keywords: csectype-uaf
Product: Firefox → Core
Severity: -- → S2
Priority: -- → P1
Whiteboard: [necko-triaged][necko-priority-queue]
Assignee: nobody → rjesup

Thanks for the detailed report. So I can verify the bug and fixes, how are you starting the aioquic server?
python examples/demo.py --certificate tests/ssl_cert.pem --private-key tests/ssl_key.pem exits for me without errors, so I'm probably not starting it correctly

Flags: needinfo?(ssherkito)

(In reply to Randell Jesup [:jesup] (needinfo me) from comment #4)

Thanks for the detailed report. So I can verify the bug and fixes, how are you starting the aioquic server?
python examples/demo.py --certificate tests/ssl_cert.pem --private-key tests/ssl_key.pem exits for me without errors, so I'm probably not starting it correctly

I used the command python examples/http3_server.py -c cert.pem -k key.pem --host 127.0.0.1

First, run python http3_server.py -c cert.pem -k key.pem and if it doesn't work, try running it with --host 127.0.0.1 append as above

Flags: needinfo?(ssherkito)

Aha, from above I thought you were running demo.py. I can run now, but I don't have the cert/key mentioned above, which have to match the cert specified in the poc. I commented out the cert from the POC, and that changes the error, but I still don't hit an ASAN failure. It never get to 'ready'; I appear to be getting a rejection creating the session from the server:

[Child 1781830: Main Thread]: D/WebTransport Creating WebTransport for https://127.0.0.1:4433/wt
[Child 1781830: Main Thread]: D/WebTransport Creating WebTransport 51100033d8c0
[Child 1781830: Main Thread]: D/WebTransport Created datagram streams
[Child 1781830: Main Thread]: D/WebTransport Connecting WebTransport to parent for https://127.0.0.1:4433/wt
[Parent 1781656: IPDL Background]: D/WebTransport Created WebTransportParent 516000149a80 https://127.0.0.1:4433/wt Dedicated  congestion=Default
[Parent 1781656: Socket Thread]: D/WebTransport Binding parent endpoint
[Parent 1781656: Main Thread]: D/WebTransport WebTransport 516000149a80 AsyncConnect
[Parent 1781656: Main Thread]: D/WebTransport webtransport 516000149a80 session creation failed code= 0, reason= 
[Child 1781830: Main Thread]: D/WebTransport isreject: 0 nsresult 0x80004005
[Child 1781830: Main Thread]: D/WebTransport Rejected connection 51100033d8c0 80004005
[Child 1781830: Main Thread]: D/WebTransport Cleanup started
[Child 1781830: Main Thread]: D/WebTransport Rejecting mClosed
[Child 1781830: Main Thread]: D/WebTransport WebTransportChild::Shutdown() for 51300020a480 (51100033d8c0)
[Parent 1781656: Socket Thread]: D/WebTransport ActorDestroy WebTransportParent 3
JavaScript error: , line 0: WebTransportError: WebTransport connection rejected
[Parent 1781656: Socket Thread]: D/WebTransport Destroying WebTransportParent 516000149a80
JavaScript error: , line 0: WebTransportError: WebTransport connection rejected

(In reply to Randell Jesup [:jesup] (needinfo me) from comment #6)

Aha, from above I thought you were running demo.py. I can run now, but I don't have the cert/key mentioned above, which have to match the cert specified in the poc. I commented out the cert from the POC, and that changes the error, but I still don't hit an ASAN failure. It never get to 'ready'; I appear to be getting a rejection creating the session from the server:

[Child 1781830: Main Thread]: D/WebTransport Creating WebTransport for https://127.0.0.1:4433/wt
[Child 1781830: Main Thread]: D/WebTransport Creating WebTransport 51100033d8c0
[Child 1781830: Main Thread]: D/WebTransport Created datagram streams
[Child 1781830: Main Thread]: D/WebTransport Connecting WebTransport to parent for https://127.0.0.1:4433/wt
[Parent 1781656: IPDL Background]: D/WebTransport Created WebTransportParent 516000149a80 https://127.0.0.1:4433/wt Dedicated  congestion=Default
[Parent 1781656: Socket Thread]: D/WebTransport Binding parent endpoint
[Parent 1781656: Main Thread]: D/WebTransport WebTransport 516000149a80 AsyncConnect
[Parent 1781656: Main Thread]: D/WebTransport webtransport 516000149a80 session creation failed code= 0, reason= 
[Child 1781830: Main Thread]: D/WebTransport isreject: 0 nsresult 0x80004005
[Child 1781830: Main Thread]: D/WebTransport Rejected connection 51100033d8c0 80004005
[Child 1781830: Main Thread]: D/WebTransport Cleanup started
[Child 1781830: Main Thread]: D/WebTransport Rejecting mClosed
[Child 1781830: Main Thread]: D/WebTransport WebTransportChild::Shutdown() for 51300020a480 (51100033d8c0)
[Parent 1781656: Socket Thread]: D/WebTransport ActorDestroy WebTransportParent 3
JavaScript error: , line 0: WebTransportError: WebTransport connection rejected
[Parent 1781656: Socket Thread]: D/WebTransport Destroying WebTransportParent 516000149a80
JavaScript error: , line 0: WebTransportError: WebTransport connection rejected

Here's how I solved the certificate errors.

  1. Issue a certificate with a command like cargo run --example gencert via https://github.com/BiagioFesta/wtransport and specify the serverCertificateHashes value in poc.html with the output key value
  2. Since the certificate created above is not an official certificate, Firefox does not process WebTransport properly. To solve this, I set the value of network.http.http3.disable_when_third_party_roots_found in about:config listed in https://github.com/BiagioFesta/wtransport/issues/241 to true

just a small correction, set network.http.http3.disable_when_third_party_roots_found to false. Thanks!

I can run the poc now, thanks! But in repeated attempts I haven't been able to trigger an ASAN fault (though I believe it's real). Anything else about your environment which might affect it? OS, Firefox version, etc? I'm running a local ASAN build on Fedora 41, optimized.

I'll bet it's due to this: JavaScript error: http://127.0.0.1/webtrans.html, line 5: ReferenceError: FuzzingFunctions is not defined

With a --enable-fuzzing build, and fuzzing.enabled = true, I can repro. Thanks

Status: UNCONFIRMED → NEW
Ever confirmed: true

Comment on attachment 9462561 [details]
Bug 1944126: Update WebTransportChild r=#dom-storage-reviewers!

Security Approval Request

  • How easily could an exploit be constructed based on the patch?: Possible
  • Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: Yes
  • Which branches (beta, release, and/or ESR) are affected by this flaw, and do the release status flags reflect this affected/unaffected state correctly?: all
  • 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?: Trivial
  • How likely is this patch to cause regressions; how much testing does it need?: Impossible
  • Is the patch ready to land after security approval is given?: Yes
  • Is Android affected?: Yes

Beta/Release Uplift Approval Request

  • User impact if declined/Reason for urgency: Sec-high
  • Is this code covered by automated tests?: No
  • Has the fix been verified in Nightly?: No
  • Needs manual test from QE?: No
  • If yes, steps to reproduce:
  • List of other uplifts needed: None
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): Just holds a refptr
  • String changes made/needed: none
  • Is Android affected?: Yes
Attachment #9462561 - Flags: sec-approval?
Attachment #9462561 - Flags: approval-mozilla-beta?
Attachment #9462561 - Flags: approval-mozilla-esr128?
Attachment #9462561 - Flags: approval-mozilla-esr115?

Comment on attachment 9462561 [details]
Bug 1944126: Update WebTransportChild r=#dom-storage-reviewers!

Approved to land

Attachment #9462561 - Flags: sec-approval? → sec-approval+
Group: network-core-security → core-security-release
Status: NEW → RESOLVED
Closed: 1 year ago
Resolution: --- → FIXED
Target Milestone: --- → 137 Branch

Comment on attachment 9462561 [details]
Bug 1944126: Update WebTransportChild r=#dom-storage-reviewers!

Approved for 136.0b2

Attachment #9462561 - Flags: approval-mozilla-beta? → approval-mozilla-beta+

Comment on attachment 9462561 [details]
Bug 1944126: Update WebTransportChild r=#dom-storage-reviewers!

Approved for 128.8esr

Attachment #9462561 - Flags: approval-mozilla-esr128? → approval-mozilla-esr128+
QA Whiteboard: [post-critsmash-triage]
Flags: qe-verify-

Comment on attachment 9462561 [details]
Bug 1944126: Update WebTransportChild r=#dom-storage-reviewers!

Approved for 115.21esr

Attachment #9462561 - Flags: approval-mozilla-esr115? → approval-mozilla-esr115+
Whiteboard: [necko-triaged][necko-priority-queue] → [necko-triaged][necko-priority-queue][adv-main136+][adv-esr128.8+][adv-esr115.21+]
Attached file advisory.txt
Alias: CVE-2025-1931

Is there no bounty for this bug?

Group: core-security-release

(In reply to sherkito from comment #24)

Is there no bounty for this bug?

My apologies. It looks like a bug bounty was never requested when you filed this? Flagging it for the committee meeting next week.

Flags: sec-bounty?

FWIW you can file your bugs through the form at https://bugzilla.mozilla.org/form.client.bounty to make sure the bounty-request flag is properly set.

Flags: sec-bounty? → sec-bounty+
See Also: → 1995294
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: