Full Report
The challenge uses Chromium and abuses HTTP Disk Cache keys to trigger a client-side cache-poisoning issue. It contains two endpoint: /view and /. /view only succeeds if the header contains From-Fetch but contains an XSS sink within it via the HTML parameter. / performs a call to /view via a Fetch and places the contents within iframe without script execution. This is the setup for the challenge. The goal is to trigger the XSS but there's a paradox. /view cannot be called directly because of the header check. / places the code into an iframe so we can't do anything. The trick of the challenge is to trick the browser into adding the From-Fetch header to an unintended request. Moderen browsers have a split cache in order to prevent cross-site leaks. This is derived via the Top site domain and the resource URL. Chromium added the cn_ prefix to prevent cache poisoning during mainframe navigation. In. Particularly, this is added when the top level of the page has its location.href modified. By using the history API, it's possible to bypass the usage of cn_ on the page. Notably, history.back() doesn't count as a cross-site main-frame navigation for whatever reason! So, the following sequence of events will lead to XSS: Do a window.open() to /?html= to populate the cache. Redirect to another page and preform history.back(). Redirect to /view?html=. Do the final history.back() to load the cached version of the page to get XSS. Weird challenge with some weird browser quirks. I still don't 100% understand this, but I appreciate the trick of telling the browser to use the cache when the requests are somewhat different.
Analysis Summary
# Research: Exploiting Chromium’s Split Cache and Navigation Persistence for XSS
## Metadata
- **Authors:** m0z (with contribution from team mörger: AresX & Zer0RocketWrecks)
- **Institution:** Independent Security Research (m0z.ie)
- **Publication:** SECCON CTF 2025 Writeups
- **Date:** December 19, 2025
## Abstract
This research explores a sophisticated client-side cache-poisoning attack within the Chromium browser. By exploiting the nuances of the "Split Cache" mechanism and the History API, a vulnerability is demonstrated where a browser can be tricked into serving a cached response containing a restrictive security header (`From-Fetch`) in a context where it should have been absent. The attack bypasses modern navigation-based cache protections (specifically the `cn_` prefix) to trigger Cross-Site Scripting (XSS).
## Research Objective
The study addresses a "paradoxical" XSS scenario involving two endpoints:
1. **`/view`**: Contains an XSS sink but only renders if the `From-Fetch` header is present.
2. **`/`**: Performs a `fetch` to `/view` and renders it in an `iframe` (neutralizing scripts).
The objective is to find a way to load `/view` as a main-frame navigation (to execute JS) while tricking the browser into believing the request is a `fetch` to satisfy the header check.
## Methodology
### Approach
The researchers utilized a "State-Machine" style manipulation of the browser’s History API and Cache-Key generation logic. The goal was to populate the HTTP Disk Cache during a legitimate fetch and then force the browser to retrieve that specific cache entry during a top-level navigation.
### Dataset/Environment
- **Browser:** Modern Chromium-based browsers.
- **Challenge Setup:** A web application with specific CORS and header-check logic designed to prevent direct access to sensitive sinks.
### Tools & Technologies
- Chromium Browser Engine
- JavaScript History API (`history.back()`, `location.href`)
- HTTP Disk Cache (Chromium-specific implementation)
## Key Findings
### Primary Results
1. **Navigation Prefix Bypass:** Chromium's `cn_` prefix (designed to distinguish main-frame navigations from sub-resource fetches in the cache) can be bypassed using specific History API movements.
2. **Persistence of Cache Keys:** A cache entry created via `fetch` (which includes the `From-Fetch` header in the response) can be re-used for a main-frame navigation under specific conditions.
3. **History API Quirk:** While modifying `location.href` triggers a cross-site main-frame navigation flag in the cache key, `history.back()` does not always trigger the same "cache-clearing" transition.
### Novel Contributions
- Identifies an inconsistency in how Chromium applies the `cn_` (cache-navigation) prefix between direct URL modifications and history-based navigation.
## Technical Details
Chromium implements a **Split Cache** to prevent cross-site leaks. The cache key is typically `(Top-level site, Resource URL)`. To prevent cache poisoning during main-frame navigations, Chromium adds a `cn_` prefix to the key when the top-level location is modified.
The successful exploit sequence discovered:
1. **Populate:** Use `window.open('/?html=...')` to initiate a fetch and fill the cache with a response containing the `From-Fetch` header and the XSS payload.
2. **Pivot:** Redirect the window to an external domain.
3. **First Back:** Use `history.back()` to return to the application state without triggering a new "navigation" cache key.
4. **Target:** Redirect to `/view?html=...`.
5. **Trigger:** Use `history.back()` again. Because this is a history navigation, the browser looks for the cached resource. It finds the entry previously stored by the `fetch` (because the `cn_` prefix was bypassed or matched), and the browser renders the cached content—complete with the `From-Fetch` header—in the top-level frame, executing the XSS.
## Practical Implications
### For Security Practitioners
- Traditional header-based authentication/verification (like checking for `X-Requested-With` or `From-Fetch`) is not a definitive security boundary if the resource is cacheable.
### For Defenders
- **Cache-Control:** Ensure that sensitive endpoints or those with XSS sinks use `Cache-Control: no-store` to prevent them from being placed in the Disk Cache.
- **Vary Header:** Use `Vary: From-Fetch` to ensure the browser differentiates between fetch-based and navigation-based requests in the cache.
### For Researchers
- There is a need to further investigate how different "types" of navigation (Back/Forward, Reload, Hyperlink, Scripted) interact with Chromium's `net/disk_cache` logic.
## Limitations
- The attack requires the victim to interact with a malicious site that can control window references or history.
- Success depends on the specific heuristic of the Chromium version's disk cache management.
## Comparison to Prior Work
This builds upon known "Cross-Site Search" (XS-Leak) and "Cache-Poisoning" research but elevates it by specifically targeting the internal "partitioning" prefixes (`cn_`) used by Chromium to prevent these exact types of attacks.
## Real-world Applications
- Bypassing CSRF protections that rely on custom headers.
- Accessing "Internal-Only" views that are gated by headers added by a front-end proxy or fetch-handler.
## Future Work
- Analyzing if similar History API quirks exist in Safari (WebKit) or Firefox (Gecko).
- Testing if `Pragma` headers or older cache-related headers influence the `cn_` prefix assignment.
## References
- SECCON CTF 2025 (Web/Broken-Challenge)
- Chromium Source: `net/disk_cache/cache_util.cc`
- Research into "Cross-site leaks" and "First-party sets" in Chromium.