//nefariousplan

CVE-2026-2441: The Vulnerable Chrome Binary Is a 404, the Heap Leak Is Math.random()

pattern

cve

proof of concept

The README declares CVSS 8.8, Renderer RCE in a sandboxed Chrome, active in-the-wild exploitation. The exploit-chain diagram names NSO Pegasus, Intellexa Predator, and APT-28. The vulnerability is real. CVE-2026-2441 is a Use-After-Free in Blink's CSSFontFeatureValuesMap, fixed in Chromium commit 63f3cb4864c64c677cd60c76c8cb49d37d08319c on February 12, 2026. CISA added it to the Known Exploited Vulnerabilities catalog five days later.

The PoC repository at MartinaStarone/CVE-2026-2441 has a different relationship to the bug. The bundled chrome-linux.zip is 223 bytes of XML from Google Cloud Storage saying NoSuchKey. The "heap pointer leak via dangling aliases_" is Math.random(). The credential theft works because the launcher script starts Chrome with --disable-web-security. The function that triggers the UAF runs four iterations with one re-insertion per step, because the real spray, in the author's words, "causava il crash del renderer."

These are not flaws in the demonstration. They are the demonstration.

The README declares CVSS 8.8, Renderer RCE in a sandboxed Chrome, active in-the-wild exploitation. The exploit-chain diagram names NSO Pegasus, Intellexa Predator, and APT-28. The vulnerability is real. CVE-2026-2441 is a Use-After-Free in Blink's CSSFontFeatureValuesMap, fixed in Chromium commit 63f3cb4864c64c677cd60c76c8cb49d37d08319c on February 12, 2026. CISA added it to the Known Exploited Vulnerabilities catalog five days later.

The PoC repository at MartinaStarone/CVE-2026-2441 has a different relationship to the bug. The bundled chrome-linux.zip is 223 bytes of XML from Google Cloud Storage saying NoSuchKey. The "heap pointer leak via dangling aliases_" is Math.random(). The credential theft works because the launcher script starts Chrome with --disable-web-security. The function that triggers the UAF runs four iterations with one re-insertion per step, because the real spray, in the author's words, "causava il crash del renderer."

These are not flaws in the demonstration. They are the demonstration.

The vulnerability is real, the fix is one type change

A Google engineer landed the patch on February 12, 2026, the day before the stable channel update. The pre-patch FontFeatureValuesMapIterationSource held a const FontFeatureAliases*, a raw pointer to the WTF::HashMap that backs @font-feature-values rules in Blink. When script mutates the map mid-iteration, the HashMap rehashes, allocates new storage, and frees the old. The raw pointer becomes dangling. The next call to FetchNextItem() reads through it.

The fix is one type change:

- const FontFeatureAliases* aliases_;
+ const FontFeatureAliases  aliases_;

A pointer becomes a value. The iteration source carries its own copy of the map and is immune to the rehash. The CVE description is consistent with the access primitive a working exploit would build, the fix is mechanical, the bug is in the wild. None of this is in dispute.

The repository named CVE-2026-2441 does not implement any of it.

The trigger is four iterations and a try/catch

poc.html line 358:

// Lo spray massiccio (512 set per step) causava il crash del renderer.
// Qui dimostriamo il dangling pointer con un numero contenuto di operazioni.

Translation: "The massive spray (512 sets per step) caused the renderer to crash. Here we demonstrate the dangling pointer with a limited number of operations."

The comment is testimony. Two things are true at once: the author had a spray that crashed the browser, which is the entire point of a UAF demonstration because a crash is the unambiguous evidence the dangling pointer was read; and the author then removed the spray for the published version. What ships is a four-iteration loop with a single re-insertion per step:

const it = map.entries();
let step = 0;
while (step < 4) {
  const res = it.next();
  if (res.done) break;
  const [key] = res.value;
  // Delete entry while iterator is live → dangling aliases_ ptr
  map.delete(key);
  // One re-insertion to trigger potential rehash
  map.set("uaf_" + step, [step, step + 1]);
  step++;
}

The styleset has eight initial entries (a0 through a7). At one delete and one insert per iteration, four iterations later the underlying HashMap is at the same size it started, well under the load-factor threshold that triggers a rehash in WTF::HashMap. The function does not demonstrate a UAF. It demonstrates that the author knew how to write a UAF demonstration and chose to ship the version that does not work.

The next lines are where the demonstration becomes the demonstration:

// Simulated heap address leak (dangling pointer read simulation)
const base = 0x55a000000000 + Math.floor(Math.random() * 0x200000);
heapLeak = {
  raw:  "0x" + base.toString(16).toUpperCase(),
  base: "0x" + (base & ~0xfff).toString(16).toUpperCase()
};

uafTriggered = true;
dbg.textContent = "UAF: ✓ dangling ptr";
} catch(e) {
  dbg.textContent = "UAF: ex→" + e.message.substring(0, 40);
  uafTriggered = true; // exception = UAF surface hit
  const b = 0x55a000000000 + Math.floor(Math.random() * 0x200000);
  heapLeak = { raw: "0x" + b.toString(16).toUpperCase(),
               base: "0x" + (b & ~0xfff).toString(16).toUpperCase() };
}

The "heap pointer leak" the attacker dashboard renders as ASLR bypassata is Math.random(). The seed is 0x55a000000000, a plausible Linux x86-64 ASLR base for a typical PIE binary, with a 2 MB random offset added on. The dangling pointer is never read because there is no dangling pointer. The handler returns through both branches, the success branch and the catch, with uafTriggered = true and a fresh Math.random() heap leak in either case. Any exception is "UAF surface hit." No exception is "UAF triggered." The handler covers both cases by claiming both cases are evidence.

The credential theft is window.opener

The exfiltrated payload, labeled in the dashboard as rubati via opener/UAF (stolen via opener/UAF), is assembled by collectSessionData() reading window.opener:

const sessionData = {};
(function collectSessionData() {
  const src = window.opener || window;
  try {
    const c = {};
    (src.document.cookie || "").split(";").forEach(s => { /* ... */ });
    sessionData.cookies = c;
  } catch(e) { sessionData.cookies = { _err: e.message }; }
  // localStorage, sessionStorage, document.body.innerText: identical shape
})();

victim.html calls window.open("http://127.0.0.1:7777/poc.html"). The popup's window.opener is the victim window. Both pages are served by the same Python server on the same port. They are same origin. Reading window.opener.document.cookie and the storage objects from the popup is what same-origin pages have always been allowed to do.

The same-origin reads are not a UAF. They are not specific to Chromium. They do not depend on a styleset, a @font-feature-values rule, an aliases_ pointer, or a patched-versus-unpatched Chrome. Open poc.html directly in Firefox, in Safari, in the patched Chromium 145.0.7632.75, and the same window.opener.document.cookie returns the same cookies. The phishing form harvests the credit-card and OTP fields the user types into the form. The UAF section runs in parallel and contributes a Math.random() heap leak to the same JSON payload.

The dashboard renders the random integer under the heading 🧠 Heap pointer leak (ASLR bypass via dangling aliases_) with ← ASLR bypassata appended in red text. A defender who takes the dashboard at face value reads "ASLR bypass" and updates their threat model accordingly. The bytes underneath are a Linux-flavored random integer between 0x55a000000000 and 0x55a000200000.

demo.sh disables the mitigation that would contain the bug

To make the demo run, the launcher disables the security feature designed to make the bug not run:

"$CHROMIUM_BIN" \
    --user-data-dir="$PROFILE_DIR" \
    --no-first-run \
    --disable-popup-blocking \
    --disable-translate \
    --no-default-browser-check \
    --disable-features=IsolateOrigins,site-per-process \
    --disable-web-security \
    --allow-running-insecure-content \
    "http://127.0.0.1:${PORT}/victim.html" \
    "http://127.0.0.1:${PORT}/attacker.html"

--disable-web-security turns off the same-origin policy. --disable-features=IsolateOrigins,site-per-process turns off site isolation, the architectural mitigation Chromium introduced specifically to bound the blast radius of a renderer compromise like CVE-2026-2441 itself. The CVE the demo claims to demonstrate is a renderer bug whose practical impact in the wild is bounded by site isolation. The launcher disables site isolation so the demo can produce its dashboard.

The README's mitigation section, four pages later, advises the reader to "Verify Site Isolation is enabled (chrome://flags/#site-isolation-trial-opt-out)." The launcher in the same repository turns it off.

The bundled vulnerable Chrome binary is a 404 from Google Cloud Storage

chrome-linux.zip is committed at the repository root. The README references it as the binary to run the PoC against. It is 223 bytes:

$ hexdump -C chrome-linux.zip
00000000  3c 3f 78 6d 6c 20 76 65  72 73 69 6f 6e 3d 27 31  |<?xml version='1|
00000010  2e 30 27 20 65 6e 63 6f  64 69 6e 67 3d 27 55 54  |.0' encoding='UT|
00000020  46 2d 38 27 3f 3e 3c 45  72 72 6f 72 3e 3c 43 6f  |F-8'?><Error><Co|
00000030  64 65 3e 4e 6f 53 75 63  68 4b 65 79 3c 2f 43 6f  |de>NoSuchKey</Co|
00000040  64 65 3e 3c 4d 65 73 73  61 67 65 3e 54 68 65 20  |de><Message>The |
00000050  73 70 65 63 69 66 69 65  64 20 6b 65 79 20 64 6f  |specified key do|
00000060  65 73 20 6e 6f 74 20 65  78 69 73 74 2e 3c 2f 4d  |es not exist.</M|

Decoded:

<?xml version='1.0' encoding='UTF-8'?>
<Error>
  <Code>NoSuchKey</Code>
  <Message>The specified key does not exist.</Message>
  <Details>No such object: chromium-browser-snapshots/Linux_x64/1194038/chrome-linux.zip</Details>
</Error>

The author tried to download a Chromium snapshot from Google's commondatastorage bucket, the bucket returned 404, the author saved the 404 response under the name chrome-linux.zip, and committed it. The most recent commit, two days before this post was triggered, is titled "small changes."

The CVE the file is named after is what the file is doing

CVE-2026-2441 is a Chrome Use-After-Free with confirmed in-the-wild exploitation, fixed by a one-line type change in Blink's CSS engine, and listed by CISA as a vulnerability federal agencies were required to patch by March 10. The repository named CVE-2026-2441 is a CIA-triad classroom demonstration (the launcher banner says so in those words) with a phishing form, a fake bank dashboard, a Math.random() heap leak, and a 404 from Google Cloud Storage filed as the vulnerable binary. The demo's stated educational point is "see how a sandboxed renderer RCE leads to credential theft." Its mechanical content is "see how same-origin DOM reads lead to credential theft, in any browser, with site isolation disabled, in front of a UI that says ASLR bypassata in red."

This is theatrical reissue at a different layer than its first exhibit. PX4's CTT-Enhanced reissue replaced a working pymavlink call with a print statement and laid Riemann-zero pseudo-math on top. This one replaces the working UAF spray with a four-iteration nop, replaces the dangling-pointer read with a Math.random() formatted as a hex address, and stages credential theft through window.opener in a Chrome started with --disable-web-security. In both cases the headline file does not reach the original exploit primitive. In both cases the artifact's README claims a capability the artifact does not implement. In both cases the gap travels in the direction that benefits whoever is searching for the CVE number and stops reading at the README.

The most accurate file in the repository is the one the author did not check.

PoC: MartinaStarone/CVE-2026-2441

The most accurate file in the repository is the one the author did not check.