High CVSS 7.5 firefox Cross Origin 🔧 Commit mapped

Overview

High
Severity
7.5
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionAn attacker could, via a specially crafted multipart response, execute arbitrary JavaScript under the `resource://pdf.js` origin. This could allow them to access cross-origin PDF content. This access is limited to "same site" documents by the Site Isolation feature on desktop clients, but full cross-origin access is possible on Android versions.
ComponentToolkit
Bug ClassCross Origin
Tracker1918301
Fix commit866a4943cb8c (firefox) +7/-0
CWECWE-346 (Origin validation error)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedMasato Kinugawa
Disclosed2024-10-01

Background

`PdfStreamConverter`
A Firefox stream converter that intercepts application/pdf responses and hands them to the built-in PDF.js viewer running under the privileged resource://pdf.js origin.
`nsIStreamConverter`
The XPCOM interface whose getConvertedType / convert methods let a component transform a channel’s payload from one MIME type into another before it reaches the document.
`nsIMultiPartChannel`
A channel interface implemented for multipart/x-mixed-replace responses, where a single HTTP response delivers a sequence of independent parts, each surfaced as its own sub-channel.
Site Isolation
The desktop process-separation feature that confines documents to a process keyed by site, limiting (but not fully preventing) cross-origin data access.

Root Cause Analysis

The vulnerable code path was PdfStreamConverter.prototype.getConvertedType, which selected the PDF.js viewer for a response without accounting for the case where the underlying channel was an nsIMultiPartChannel. When a server returned a multipart/x-mixed-replace response containing a PDF part, the stream converter would still route the multipart channel to the PDF.js viewer, and the viewer document ended up loaded under the privileged resource://pdf.js origin while an attacker retained influence over subsequent parts of the same channel. This violated the invariant that only a single, well-formed PDF response should be promoted into the resource://pdf.js viewer context, allowing attacker-controlled content to run script in that privileged origin and read cross-origin PDF data.

The fix adds an explicit type check at the top of getConvertedType: if aChannel instanceof Ci.nsIMultiPartChannel, it throws Components.Exception with Cr.NS_ERROR_NOT_IMPLEMENTED, refusing to convert multipart responses at all. Because PDF.js was never designed to handle the sequential-replacement semantics of multipart channels, rejecting them outright removes the confused-deputy behavior rather than trying to sanitize each part.

Key insight
The core mistake was that getConvertedType trusted the response MIME type alone and ignored the multipart nature of the channel, letting a multipart/x-mixed-replace response smuggle content into the privileged resource://pdf.js viewer. The fix closes this by detecting nsIMultiPartChannel and refusing conversion, so multipart responses can never reach the PDF.js origin.

Attack Path

  1. Serve a crafted multipart response The attacker hosts a URL that returns a multipart/x-mixed-replace response whose part(s) are typed as application/pdf.
  2. Trigger PDF stream conversion The victim navigates to or embeds the URL, and Firefox invokes PdfStreamConverter.getConvertedType, which selects the PDF.js viewer despite the channel being an nsIMultiPartChannel.
  3. Load the viewer under `resource://pdf.js` The multipart channel is promoted into the privileged PDF.js document context, where the attacker’s controlled part content is treated as viewer content.
  4. Execute script in the privileged origin The crafted content runs arbitrary JavaScript under the resource://pdf.js origin, escaping the attacker’s own origin restrictions.
  5. Read cross-origin PDF content Using the elevated origin, the script accesses PDF content from other origins, fully cross-origin on Android and limited to same-site documents by Site Isolation on desktop.

Impact Assessment

An attacker gains arbitrary JavaScript execution under the privileged resource://pdf.js origin, enabling theft of cross-origin PDF content. On desktop this is constrained to “same site” documents by Site Isolation, while on Android full cross-origin access is possible. The only precondition is that the victim loads an attacker-controlled URL that returns a specially crafted multipart/x-mixed-replace response.

Changed Functions

FunctionChangeNotes
getConvertedType
toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs
modified
if
toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs
modified

Files Changed

  • toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs

Audit Directions

  • Type-based dispatch that ignores channel capabilities
    Review other nsIStreamConverter and content-handler implementations that select a privileged viewer based solely on MIME type without checking whether the channel is multipart or otherwise streams attacker-controllable follow-up data.
  • Privileged `resource://` origins reachable from web content
    Audit any code that promotes a network response into a resource:// or chrome:// document context, ensuring the response shape and origin are validated before the elevation.
  • Multipart response handling
    Search for components that consume multipart/x-mixed-replace streams and confirm each part is re-validated, since later parts can differ from the first and subvert one-time trust decisions.
diff --git a/toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs b/toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs
index 2c85c1ae25d..bf64528d51f 100644
--- a/toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs
+++ b/toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs
@@ -1018,6 +1018,13 @@ PdfStreamConverter.prototype = {
   },
 
   getConvertedType(aFromType, aChannel) {
+    if (aChannel instanceof Ci.nsIMultiPartChannel) {
+      throw new Components.Exception(
+        "PDF.js doesn't support multipart responses.",
+        Cr.NS_ERROR_NOT_IMPLEMENTED
+      );
+    }
+
     const HTML = "text/html";
     let channelURI = aChannel?.URI;
     // We can be invoked for application/octet-stream; check if we want the
Loading diff…