Reflected XSS on a News Site: How One URL Ran My JavaScript

I changed one thing in the address bar of a news website, and the site ran my JavaScript. This is a real reflected XSS, explained step by step: how input is reflected into an HTML attribute, how an autofocus/onfocus breakout fires code on page load, why a slash filter completely missed it, and the fix that actually works.

https://www.youtube.com/@devsecknight

I changed one thing in the address bar of a news website, and the site ran my JavaScript. Not its code. Mine.

This is a real reflected cross-site scripting (XSS) bug I found. The organisation is not named. To see why it works, we need to look at what the page does with the text you give it.

What reflection really is

When you load a URL with something extra in it, the server often takes a piece of that URL and prints it straight back into the page's HTML. That's reflection, and it's everywhere: search results that echo your query, error pages that repeat what you typed, links that remember where you came from.

Reflection is fine, as long as the site treats your text as text. The danger starts when your input is dropped into the HTML without being encoded, because then the browser can no longer tell your data apart from the site's own code.

The attribute breakout

Here, my input landed inside an HTML attribute, in quotes — conceptually something like this:

html
<input value="MY_INPUT_HERE">

So I didn't try to inject a <script> tag. I did something quieter: I closed that attribute's quote early, and then added a brand-new attribute of my own. Two attributes, specifically:

  • autofocus — tells the browser to put the cursor on that element the moment the page loads.
  • onfocus — runs JavaScript the instant the element receives focus.

The shape of the result looked like this:

html
<input value="x" autofocus onfocus="/* my JavaScript */">

Put those together and the trap springs itself. The page loads, the element grabs focus automatically, the onfocus handler fires, and my JavaScript executes. The victim doesn't have to click anything on the page — they just have to open the link.

Why not a <script> tag?

Because the site was filtering. It escaped certain special characters, including the forward slash. Without a slash, you can't write a closing </script> tag, so classic script injection was blocked. The developers saw that and assumed they were safe.

But blocking one character is not the same as encoding output. My attribute-based payload needed no slash at all. The filter was looking for one shape of attack and completely missed another. This is the recurring lesson of XSS: blocklists lose, because there is always another shape.

What runs, and why it matters

Because this executes as real JavaScript inside the news site's own origin, it can do almost anything the site can do: read the page, read cookies and tokens that aren't otherwise protected, rewrite what the victim sees, redirect them, or quietly phish them on a domain they already trust. That last point is what makes XSS so dangerous — the browser believes the code belongs to the site.

Severity

CVSS 3.1 base score: 6.1 (Medium) — CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

It's "only" medium because successful exploitation requires a victim to open a crafted link (User Interaction: Required). But the Scope is Changed: code from the attacker now executes in the trusted site's context, which is why the real-world impact of a single opened link can be far larger than the number suggests.

The fix

Not a bigger blocklist. The real fix is context-aware output encoding: any data you reflect into HTML must be encoded for exactly where it lands, so that a quote stays an inert quote and can never break out into a new attribute. Layer on a strong Content Security Policy as a safety net, and let a framework that escapes by default do the encoding for you. Filtering a character or two will always lose this race.

Takeaways

  • Treat every value you reflect into a page as untrusted, and encode it for its exact context (HTML body, attribute, JavaScript, URL).
  • Attribute-based XSS (autofocus + onfocus, and similar event handlers) needs no <script> tag and no slash — blocklists miss it.
  • Add a Content Security Policy and prefer frameworks with automatic, context-aware escaping.

References

  1. OWASP — Cross Site Scripting (XSS)
  2. OWASP — Cross Site Scripting Prevention Cheat Sheet
  3. OWASP — Reflected XSS
  4. MDN Web Docs — Content Security Policy (CSP)
  5. MDN Web Docs — autofocus attribute
  6. FIRST — CVSS v3.1 Specification

For educational purposes only. The organisation is not named. Only test systems you own or have written permission to test. Follow @devsecknight for more real bug reports.

Related posts