Finding and Exploiting 20-Year-Old Bugs in Web Browsers

Ivan Fratric (Security Researcher · Google Project Zero)

OffensiveCon 2025 · Day 1 · Main · Briefings

Overview

Ivan Fratric of Google Project Zero audited the XSLT processing engines embedded in all major web browsers and discovered multiple use-after-free and memory corruption vulnerabilities, some dating back over 20 years — predating Firefox 1.0. The root cause across most findings is that XSLT parsers were written assuming single-threaded, non-reentrant execution, while browsers have since made JavaScript callable during XSLT transformation, turning virtually every DOM interaction mid-parse into a potential use-after-free. At least one full working exploit against a current browser was demonstrated. ---

Watch on YouTube

Visual summary for Finding and Exploiting 20-Year-Old Bugs in Web Browsers by Ivan Fratric
Visual summary for Finding and Exploiting 20-Year-Old Bugs in Web Browsers by Ivan Fratric

Key moments

  1. 3:44 XSLT removal from Chrome failed — frozen attack surface persists
  2. 10:19 XSLT reachable with JavaScript disabled via embedded stylesheets
  3. 10:53 Single LibXSLT bug simultaneously affects all Blink/WebKit browsers
  4. 13:51 One moderate bug as seed reveals entire UAF class
  5. 14:51 mStyleSheetDocument UAF: mutation observer early-return leaves dangling pointer
  6. 17:09 Firefox UAF from 2003 predates Firefox 1.0 release
  7. 25:50 LibXSLT grammar fuzzer surfaces additional corruption in all Blink browsers
  8. 45:03 Live calc popup: working RCE against current Firefox XSLT

Finding and Exploiting 20-Year-Old Bugs in Web Browsers

Speaker: Ivan Fratric (Google Project Zero)

Conference: OffensiveCon 2025 — May 16–17, 2025, Berlin

YouTube: https://www.youtube.com/watch?v=U1kc7fcF5Ao

Reading time: ~10 minutes

TL;DR

Ivan Fratric of Google Project Zero audited the XSLT processing engines embedded in all major web browsers and discovered multiple use-after-free and memory corruption vulnerabilities, some dating back over 20 years — predating Firefox 1.0. The root cause across most findings is that XSLT parsers were written assuming single-threaded, non-reentrant execution, while browsers have since made JavaScript callable during XSLT transformation, turning virtually every DOM interaction mid-parse into a potential use-after-free. At least one full working exploit against a current browser was demonstrated.

Introduction

XSLT — the XML Stylesheet Language for Transformations — is not a feature most developers think about in 2025. Chromium's engineering team tried to remove it in 2013 and abandoned the attempt because 0.02% of all page loads still used it. That tiny fraction is enough to keep the feature alive indefinitely, and with it an attack surface that received almost no security scrutiny in the intervening decade.

At OffensiveCon 2025, Fratric presented a systematic audit of XSLT implementations across Chromium, Firefox, Safari, and Edge. The research combined manual code review, variant analysis from previously reported moderate-severity bugs, and a purpose-built structured fuzzer. The outcome was full vulnerability coverage across all major browsers — bugs in both the LibXSLT library used by Chromium/WebKit-based browsers and in Firefox's bespoke DOM-integrated XSLT engine. Several of the bugs had been dormant since the early 2000s, when XSLT support was first added. The talk is a case study in how legacy features with frozen codebases accumulate exploitable debt, and how combining fuzzing and variant analysis can efficiently mine it.

XSLT in Web Browsers: The Attack Surface

▶ Watch: XSLT Overview and Attack Surface (4:00)

XSLT is a templating language for XML documents. A style sheet defines templates keyed to XPath expressions; the engine traverses a source XML document, matches each node against templates, and assembles an output document — commonly HTML for browser rendering. XPath, the node-selection language embedded throughout XSLT, provides a SQL-like query mechanism for XML trees and is itself an additional source of parser complexity.

There are two primary attack vectors in browsers:

  1. The XSLTProcessor JavaScript interface, which gives script direct control over transformation — loading a style sheet document, triggering a transform, and accessing the output fragment — all from within a page's JavaScript context.
  2. XML documents with embedded style sheets, where navigating to an XML URL automatically applies the XSLT engine without JavaScript being active. This path reaches the attack surface even with JavaScript disabled, making it relevant for more restricted browsing environments.

▶ Watch: LibXSLT vs. Firefox Implementation (10:00)

The two underlying implementations differ in a security-critical way. LibXSLT (used by Chromium, Safari, and all Blink-based browsers) is a standalone open-source library that operates on its own internal DOM representation. Chromium feeds it data by serializing to strings and parsing through libxml2 — creating a translation boundary between the browser's DOM and the XSLT engine. Firefox's implementation, by contrast, is woven directly into Gecko's DOM engine and operates on the live DOM node structures shared with the rest of the browser runtime. This tight coupling means that any operation during XSLT processing that modifies the DOM — including JavaScript execution triggered via XSLT's xsl:script or mutation observers — can invalidate pointers currently held by the XSLT engine, yielding use-after-free conditions.

The Firefox mStyleSheetDocument Use-After-Free (Introduced 2003)

▶ Watch: Firefox UAF Vulnerability (14:00)

The first major vulnerability Fratric disclosed is a use-after-free in Firefox's XSLTProcessor JavaScript interface. The implementation maintained a raw pointer, mStyleSheetDocument, marked in a code comment as "weak" — meaning it was expected to be nulled before the referent was destroyed. The mechanism for doing this was a mutation observer callback: when the style sheet document was about to be destroyed, the observer fired, nulled the pointer, and the UAF was prevented.

The bug was that the mutation observer callback contained an early-return path. If that path was taken — requiring a specific set of preconditions to be true simultaneously — the callback exited without nulling mStyleSheetDocument. The pointer remained dangling. Any subsequent access to mStyleSheetDocument after the document was freed constituted a use-after-free, enabling type confusion or controlled memory corruption depending on what allocation landed in the freed memory region.

When Fratric reported the bug to Mozilla, a developer noted that the flaw was introduced in 2003, predating the Firefox 1.0 release. The bug survived more than two decades undetected, illustrating the extreme longevity of XSLT-surface vulnerabilities: because the XSLT codebase rarely changes, bugs introduced when it was written tend to persist indefinitely.

Variant Analysis: JavaScript-XSLT Reentrancy as a Bug Class

▶ Watch: Variant Analysis Methodology (18:00)

Fratric's most impactful methodological observation was that XSLT implementations were designed for single-threaded, non-reentrant execution, but browsers subsequently introduced JavaScript execution at points inside the XSLT transformation pipeline. Nearly any DOM mutation that JavaScript can perform while XSLT is mid-transformation can invalidate live pointers held by the engine.

This insight guided a variant analysis pass: given a known moderate-severity bug involving unexpected JavaScript-DOM interaction during XSLT parsing, Fratric systematically searched for other call paths in both Firefox and Chromium where:

  • The XSLT engine holds a reference to a DOM node or document.
  • JavaScript can be invoked (through event handlers, mutation observers, or explicit script elements) during that reference's lifetime.
  • The JavaScript operation can cause the referenced object to be destroyed or moved.

The analysis yielded multiple additional use-after-free candidates across both engines. The class is structurally robust because the XSLT specification itself requires certain features — such as xsl:message elements with terminate="yes" and JavaScript-accessible document transformations — that create reentrant execution paths, making the problem architectural rather than a one-off coding error.

LibXSLT Bugs via Fuzzing

▶ Watch: Fuzzing LibXSLT (14:00)

Beyond the JavaScript-interaction class, Fratric applied structured fuzzing to LibXSLT itself to surface bugs intrinsic to the transformation engine, independent of any browser integration. The fuzzer was built around a grammar that models valid XSLT and XPath constructs — functions, variables, for-each loops, template calls, and XPath axes — and then mutates those constructs to exercise edge cases in the parser and evaluator.

The fuzzing campaign found multiple memory corruption issues in LibXSLT's handling of complex XPath expressions and XSLT template recursion. Because LibXSLT is shared across Chromium, Edge, Safari, and all WebKit-derived browsers, a single LibXSLT bug produces full cross-browser coverage simultaneously. Patches for the LibXSLT findings were coordinated across the open-source library and subsequently pulled into browser releases.

Working Exploit and Full Vulnerability Coverage

The culmination of the research is a working remote code execution exploit against at least one major browser, demonstrated at OffensiveCon. The exploit leverages a use-after-free discovered through the JavaScript-XSLT reentrancy analysis, using JavaScript to reclaim freed memory with a controlled allocation, achieving type confusion, and then building standard exploitation primitives (addrof / fakeobj style) to escape the renderer sandbox.

As Fratric noted, the public disclosure set already represents full vulnerability coverage: all major browsers — Chrome, Firefox, Safari, and Edge — received at least one XSLT-related security fix as a result of this research. The bugs span both the LibXSLT path (affecting all Blink/WebKit browsers) and Firefox's custom engine, with several having been reachable without JavaScript enabled via the embedded-stylesheet navigation path.

Notable Quotes

"I'm really wondering why we still have XSLT in web browsers, because it seems like one of those features that in this day and age is primarily useful to attackers as a source of vulnerabilities rather than for any legitimate use case."

— Ivan Fratric, ▶ 2:00

"When I reported it, one of the comments from a Mozilla person looking into this issue was that it got introduced back in two thousand and three. The bug predates Firefox 1.0, and if anyone had this bug, it would give them quite a long timeframe of ownage."

— Ivan Fratric, ▶ 16:00

"XSLT is so old and not much development is happening in that space, unlike, say, JavaScript engines, which change a lot. And so because of that, all vulnerabilities you find in this code tend to be quite old and the vulnerabilities tend to be quite long-lived."

— Ivan Fratric, ▶ 18:00

Key Takeaways

  • Legacy parser surfaces accumulate compound debt. XSLT code has barely changed since ~2003, meaning unfixed bugs age gracefully from the attacker's perspective — a 20-year-old use-after-free is just as exploitable as a freshly introduced one.
  • JavaScript reentrancy is an architectural bug class. XSLT engines were designed as non-reentrant single-pass transformers; any browser that allows JavaScript execution mid-transformation has fundamentally broken that assumption and created a systematic source of use-after-free conditions.
  • LibXSLT bugs hit every Chromium-based browser simultaneously. Because Chrome, Edge, Brave, Opera, and all WebKit browsers share LibXSLT, a single library bug gives an attacker cross-browser reach without additional effort.
  • Moderate-severity bugs are variant-analysis gold mines. Fratric found the most interesting vulnerability not by looking for critical bugs directly, but by starting from a publicly known moderate-severity issue and systematically asking: where else in the codebase does the same pattern appear?
  • XSLT is reachable without JavaScript. The embedded-stylesheet XML navigation path means that XSLT vulnerabilities can be triggered even in heavily restricted browser configurations, expanding the practical attacker surface beyond what most threat models assume.

Reviews

Dr. Zero (Offensive Security Researcher) — MUST SEE

Fratric audited XSLT engines across all major browsers, found 20-year-old use-after-free bugs in Firefox that predate Firefox 1.0, identified a structural reentrancy bug class that produces UAFs wherever JavaScript can execute during XSLT transformation, built a grammar-based fuzzer that hit LibXSLT bugs affecting every Chromium-family browser simultaneously, and demonstrated a working RCE exploit. Full browser coverage. This is what a systematic research program looks like.

Heather Calloway (CISO) — SOLID

Ivan Fratric identified XSLT processing bugs — some predating Firefox 1.0 — in every major browser, demonstrated working RCE, and traced the root cause to a design assumption baked in before the modern web threat model existed. The governance question is real but underdeveloped.

→ Top-rated talks at OffensiveCon 2025

All talks from OffensiveCon 2025