window.name DOM XSS: Hunting Cross-Window Payloads
window.name survives navigation. Open a tab, set the name, send the victim to another origin, and the property still holds your string. That persistence is why hunters keep grepping for it even when reflected XSS looks dead.
If the app reads window.name and drops it into HTML, script, or an eval-style sink, you get DOM XSS that never appears in the URL bar.
Why hunters care about this sink
Query and hash payloads are noisy. Filters, WAFs, and screenshot tools all stare at them. window.name sits off the address bar. I've seen triage underrate it until you show a clean opener page that plants the name and a victim URL that executes with no attacker-controlled query.
The classic pattern looks like this in bundled JS:
const label = window.name;
document.getElementById("banner").innerHTML = label;
Or worse: eval(window.name), setTimeout(window.name), location = window.name. Minifiers rename locals, so search the source maps when you can. In DevTools, break on DOM modifications and watch which properties feed the write.
Deliver the payload without looking like spam
Build a tiny opener you host:
window.name = '<img src=x onerror=alert(document.domain)>'(or your canary).location = 'https://target.example/app/dashboard'.
Use a unique callback to a server you own instead of alert in real reports. Confirm the sink runs in the target origin, not only on your opener.
Some apps clear window.name on load. Good for them. Others copy it into sessionStorage first, then render later—still exploitable if that copy is unsanitized. Test SPA route changes too; a sink may fire only after soft navigation.
Filters that fail in practice
Escaping designed for attributes often fails when the sink is innerHTML. A length check that allows a few hundred characters still fits an event handler. HTML entity encoding that runs after the dangerous write does nothing useful.
Watch for "safe" wrappers that only sanitize location.search while leaving window.name, document.referrer, and postMessage data alone. Defense in depth that covers one source is not defense.
Mobile WebViews deserve a separate pass. Some wrap the name property for deep links or restore session labels after an OAuth bounce. If the native shell sets window.name before injecting the URL, your opener may be the app itself—still document the exact navigation path.
Report shape that reproduces cleanly
Give the opener HTML, the exact victim path, and a screenshot or HAR showing the canary request from the target origin. State that no query parameter was required. Severity tracks impact: session theft on an authenticated page beats a public marketing widget.
If the sink only fires for logged-in users, say so and use two accounts you control: one to plant nothing, one to prove execution in the authenticated shell. Triage should not need to invent missing steps.
Recommend never reading window.name into HTML sinks; if a product feature needs a cross-page label, pass an allowlisted token and map it server-side. Clearing the name after a trusted read is a nice extra, not a substitute for safe sinks.
window.name bugs feel old until you find one on a modern React shell. The property did not get safer. The sinks just moved into webpack chunks.