Cross-site Scripting


Web Security.

Your browser is a powerful program. It renders HTML, executes JavaScript, parses CSS, connects you to pwn.college, and handles the countless interactions that make modern web applications work.

But that great power comes with great responsibility.... and risk.

When a web server takes user-controlled input and inserts it into a page without properly handling or escaping it, the browser may interpret that input as markup or even executable script instead of plain text. In other words, data becomes code.

This class of vulnerabilities is known as Cross-Site Scripting (XSS): injection flaws that occur in client-side web content, such as HTML, and execute within a victim's browser.

There are three types of XSS:

  • DOM-based (Type 0): the vulnerability exists entirely in client-side JavaScript.
  • Reflected (Type 1): malicious input is immediately reflected in the server's response.
  • Stored (Type 2): malicious input is stored and later served to other users.

In this module, you'll explore how these vulnerabilities arise, why they matter, and how small mistakes in handling input can lead to powerful client-side compromise.

Let's see what runs.



Reflected XSS

Cross-Site Scripting (XSS) is one of the most common web security vulnerabilities. It happens when a web application takes user input and displays it in the browser without handling or escaping it appropriately. When that input is treated as code instead of plain text, attackers can execute JavaScript in someone else's browser.

In this level, you'll explore how reflected input can turn into executable code. Your goal is simple: trigger a JavaScript alert().

Every security journey starts with understanding the basics, and this is where reflection begins.


Challenge Environment

The challenge files are located in /challenge.

To begin, start the web server: /challenge/server

Once running, you can access the website at: https://challenge.internal

You can visit it using a browser inside the Desktop workspace.

Once you've created a URL that triggers an alert(), go ahead and run: /challenge/victim.

When you're prompted, paste your crafted URL there. If your payload pops an alert in the victim's browser, you'll get the flag!


In this challenge, the server and the victim are isolated inside an air-gapped™ network namespace. This means the victim cannot access any external URLs or services outside that namespace—its only reachable destination is the server itself.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Cross-Site Scripting is about more than just making the browser behave unexpectedly; it is also about what that behavior enables. When user input is reflected on a page without being properly handled, the injected JavaScript executes with the same privileges as the victim who sees it.

In practice, that means an attacker's code can access sensitive information stored in the browser, including session cookies that keep users authenticated.

In this level, you'll explore how a simple reflected XSS vulnerability can be used to retrieve a victim's cookie.

Sometimes all it takes is a single crumb to expose the whole session.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Cross-Site Scripting does not always show up in obvious places. Sometimes user input is reflected inside an HTML attribute, where one misplaced quote is enough to turn harmless text into executable behavior.

In this level, your input is reflected into an attribute value. Escape that context and use your payload to retrieve the victim's cookie.

Sometimes one loose quote is all it takes for a session to slip away.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Cross-Site Scripting gets trickier when the app stops handing you neat quote boundaries. If input lands in an unquoted attribute, spaces and delimiters become part of the game.

In this level, your input is reflected into an unquoted attribute value. Break out cleanly and use your payload to retrieve the victim's cookie.

No quotes, and mind your spacing.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Not every client-side injection bug starts in page text. A link target can be just as dangerous when it is built from untrusted input.

In this challenge, user input is placed directly into an href attribute. Your goal is to creatively extract the victim's authenticated token.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Message links now begin with an internal https://challenge.internal/preview?msg= prefix, and the obvious payload characters <> and / get stripped on the way in.

The victim will still follow the link, and the authenticated token is still there to be taken.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

The message box still trusts your input, but no inline JavaScript.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Your message now passes through an inline JavaScript snippet before it ever reaches the page.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Your message still passes through an inline JavaScript snippet before it ever reaches the page, but this time the template engine gets a chance to touch it first.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Your message is now tucked into a JavaScript object before the page reads it back out.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

The page tries to look safer by packing your message into JSON and parsing it in the browser.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

The preview now lives behind an iframe src, not an inline document. Your input still reaches a same-origin page, just one step later.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

The ability to read a server's log is powerful.

Leak the flag.


Challenge Environment

In this challenge, the server and the victim are isolated inside an air-gapped™ network namespace. This means the victim cannot access any external URLs or services outside that namespace—its only reachable destination is the server itself.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

In real systems, the bug is not always kind enough to hand you the secret outright. Sometimes all that is left is a tiny clue: a response that is a little slower. Even one single bit of information at a time can be enough.

That is the idea behind a side channel: leaking data through some secondary signal instead of the intended output. Timing is one of the most common examples.

Creatively leak the flag.


Challenge Environment

In this challenge, the server and the victim are isolated inside an air-gapped™ network namespace. This means the victim cannot access any external URLs or services outside that namespace—its only reachable destination is the server itself.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Stored XSS

This new pwnpost™ platform looks unsafe...

Maybe an admin should check the posts out to see if they are safe?


Challenge Environment

You can login into pwnpost™ using these accounts:

  • guest:password
  • hacker:1337

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

pwnpost™ no longer supports admin posting due to rampant flag disclosure.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

pwnpost™ now supports creating new accounts. But admin can't see the posts due to security concerns.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

What happens in pwnpost™, stays in pwnpost™.


Challenge Environment

In this challenge, the server and the victim are isolated inside an air-gapped™ network namespace. This means the victim cannot access any external URLs or services outside that namespace—its only reachable destination is the server itself.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

What happens in pwnpost™, stays in pwnpost™ for real this time.


Challenge Environment

In this challenge, the server and the victim are isolated inside an air-gapped™ network namespace. This means the victim cannot access any external URLs or services outside that namespace—its only reachable destination is the server itself.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

How to stop hackers from leaking admin's private posts? By not posting at all!!!

Modern problems require modern solutions.


Challenge Environment

In this challenge, the server and the victim are isolated inside an air-gapped™ network namespace. This means the victim cannot access any external URLs or services outside that namespace—its only reachable destination is the server itself.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Injection into HTML attributes doesn't always require JavaScript. When a Content Security Policy blocks script execution entirely, an attacker who controls part of an attribute value may still be able to exfiltrate data from the page using nothing but HTML — no code required.

In this challenge, your input is reflected into an HTML attribute without sanitization. A Content Security Policy prevents JavaScript execution.

The admin's token is present in the page — but only when the admin is viewing it.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

pwnpost™ now supports user avatars. Files can be uploaded at /profile and are served back at /avatar/<username>.

File upload validation is a surprisingly difficult problem. There are many ways to determine what kind of file something is, and they don't always agree with each other.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Stored XSS doesn't always fire in the attacker's own browser. When the vulnerable page is restricted to privileged users, your payload executes somewhere you can't see — and the results must arrive out-of-band. This is called blind XSS, and it's how real-world XSS canaries work.

pwnpost™ now has a bug reporting system. The admin reviews submitted reports.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Same setup as the previous challenge — blind XSS via the bug reporting system, admin reviews at /reports.

This time, the session cookie is HttpOnly. But the flag is still somewhere the admin's browser can reach.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

DOM XSS

The server is no longer reflecting your input into the page. That does not mean the page stopped trusting it.

In this level, the dangerous part happens entirely in the browser. Client-side JavaScript reads data from the URL and writes it back into the DOM. This is DOM-based XSS: the bug lives in the page's own logic, not in the server response.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

The browser is still doing the dangerous part. This time it reads from the query string instead of the hash.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Data parked in one DOM attribute can turn into HTML again if later code treats it like markup.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Not every DOM bug goes straight from the URL into the page. Sometimes the browser stores attacker input in harmless-looking metadata first, then another script turns it back into markup later.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

The ephemeral page does not trust the URL anymore. It trusts whoever can message it. Without an origin check, another window can make this preview treat attacker data like local content.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Mutation XSS

A sanitizer that removes dangerous tags and strips event handlers should make HTML safe — but "safe" depends on who's doing the parsing. When a server sanitizes HTML with one parser and then the browser re-parses the sanitized output via innerHTML, the two parsers may not agree on what the HTML means. The difference can be exploitable.

This is called Mutation XSS (mXSS): the payload mutates between sanitization and rendering.

In this challenge, the server uses Python's html5lib (via BeautifulSoup) to sanitize your input. The sanitized HTML is then rendered via innerHTML in the browser.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Parsers track more than just element nesting — they also track context and namespace. How an element is serialized, and how it's re-parsed, can differ depending on where in the document tree it appears.

In MathML, <annotation-xml> is special: its encoding attribute declares what namespace its content lives in. The same element can mean something entirely different depending on where it appears and how it declares its content. Sanitizers and browsers don't always agree on what that means.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Filters

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following element is banned:

  • script (case sensitive)

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements are banned:

  • script, img

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements and patterns are banned:

  • script
  • img
  • on*= event handler attributes (e.g. onclick=, onerror=, onload=)

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements are banned:

  • script, img, svg, iframe, input
  • video, audio, source, track

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements are banned:

  • script, img, svg, iframe, input
  • video, audio, source, track
  • html, body, marquee, details

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements are banned:

  • script, img, svg, iframe, input
  • video, audio, source, track
  • html, body, frameset, details, dialog

The following handler is also banned:

  • ontoggle

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements are banned:

  • script, img, svg, iframe, input
  • video, audio, source, track
  • html, body, frameset, details, dialog, marquee

The following handlers are also banned:

  • ontoggle, onstart
  • onload, onerror, onclick
  • onfocus, onfocusin, onfocusout, autofocus

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements are banned:

  • script, img, svg, iframe, input
  • video, audio, source, track
  • html, body, frameset, details, dialog, marquee

The following handlers are also banned:

  • ontoggle, onstart
  • onload, onerror, onclick
  • onfocus, onfocusin, onfocusout, autofocus

The following attribute is also banned:

  • style=

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements are banned:

  • script, img, svg, iframe, input
  • video, audio, source, track
  • html, body, frameset, details, dialog, marquee

The following handlers are also banned:

  • ontoggle, onstart
  • onload, onerror, onclick
  • onfocus, onfocusin, onfocusout, autofocus
  • onanimationstart, onanimationiteration, onanimationend, onanimationcancel

The following attributes are also banned:

  • style=, animation, keyframes

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements are banned:

  • script, img, svg, iframe, input
  • video, audio, source, track
  • html, body, frameset, details, dialog, marquee
  • style, link

The following handlers are also banned:

  • ontoggle, onstart
  • onload, onerror, onclick
  • onfocus, onfocusin, onfocusout, autofocus
  • onanimationstart, onanimationiteration, onanimationend, onanimationcancel

The following attributes are also banned:

  • style=, animation, keyframes
  • transition, transform

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements are banned:

  • script, img, svg, iframe, input
  • video, audio, source, track
  • html, body, frameset, details, dialog, marquee
  • style, link, template

The following handlers are also banned:

  • ontoggle, onstart
  • onload, onerror, onclick
  • onfocus, onfocusin, onfocusout, autofocus
  • onanimationstart, onanimationiteration, onanimationend, onanimationcancel

The following attributes are also banned:

  • style=, animation, keyframes, content-visibility
  • transition, transform

The following shadow DOM primitives are also banned:

  • shadow, slot

A Content Security Policy is also applied: style-src 'nonce-{random}'; style-src-attr 'none'

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements are banned:

  • script, img, svg, iframe, input
  • video, audio, source, track
  • html, body, frameset, details, dialog, marquee
  • style, link, template

The following handlers are also banned:

  • ontoggle, onstart
  • onload, onerror, onclick
  • onfocus, onfocusin, onfocusout, autofocus
  • onanimationstart, onanimationiteration, onanimationend, onanimationcancel

The following attributes are also banned:

  • style=, animation, keyframes, content-visibility
  • transition, transform

The following shadow DOM primitives are also banned:

  • shadow, slot

A Content Security Policy is also applied: style-src 'nonce-{random}'; style-src-attr 'none'


Challenge Environment

In this challenge, the server and the victim are isolated inside an air-gapped™ network namespace. This means the victim cannot access any external URLs or services outside that namespace—its only reachable destination is the server itself.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following element is allowed:

  • script

The following APIs are banned:

  • fetch, XMLHttpRequest, sendBeacon

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following element is allowed:

  • script

The following APIs are banned:

  • fetch, XMLHttpRequest, sendBeacon
  • location, open(), assign(), replace()

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following element is allowed:

  • script

The following APIs are banned:

  • fetch, XMLHttpRequest, sendBeacon
  • location, open(), assign(), replace(), pushState, replaceState
  • submit, click, write, writeln
  • createElement, createElementNS, adoptNode, importNode, cloneNode
  • append, appendChild, prepend, before, after, insertBefore
  • insertAdjacentHTML, insertAdjacentText, insertAdjacentElement
  • replaceChildren, replaceChild, replaceWith, remove, removeChild
  • innerHTML, outerHTML, innerText, outerText, textContent
  • setAttribute, setAttributeNS, removeAttribute, removeAttributeNS, toggleAttribute
  • querySelector, querySelectorAll, getElementById, getElementsByTagName, getElementsByClassName, getElementsByName
  • href, action, formAction, srcdoc
  • cookieStore, localStorage, sessionStorage
  • FormData, URL, URLSearchParams, Request, Headers, Response
  • eval, Function, setTimeout, setInterval

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following element is allowed:

  • script

The following APIs are banned:

  • fetch, XMLHttpRequest, sendBeacon
  • location, open(), assign(), replace(), pushState, replaceState
  • submit, requestSubmit, click, write, writeln
  • createElement, createElementNS, adoptNode, importNode, cloneNode
  • append, appendChild, prepend, before, after, insertBefore
  • insertAdjacentHTML, insertAdjacentText, insertAdjacentElement
  • replaceChildren, replaceChild, replaceWith, remove, removeChild
  • innerHTML, outerHTML, innerText, outerText, textContent
  • setAttribute, setAttributeNS, removeAttribute, removeAttributeNS, toggleAttribute
  • querySelector, querySelectorAll, getElementById, getElementsByTagName, getElementsByClassName, getElementsByName
  • href, src, action, formAction, srcdoc
  • cookie, cookieStore, localStorage, sessionStorage
  • FormData, URL, URLSearchParams, Request, Headers, Response
  • media, Image, Audio, Video, Track, Source, Bitmap, Canvas, Blob, File
  • navigator, postMessage, document, window, globalThis, self, top, frames, form, element
  • eval, Function, setTimeout, setInterval

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following element is allowed:

  • script

The following APIs and globals are banned:

  • fetch, XMLHttpRequest, sendBeacon
  • location, open(), assign(), replace(), pushState, replaceState
  • submit, requestSubmit, click, write, writeln
  • createElement, createElementNS, adoptNode, importNode, cloneNode
  • append, appendChild, prepend, before, after, insertBefore
  • insertAdjacentHTML, insertAdjacentText, insertAdjacentElement
  • replaceChildren, replaceChild, replaceWith, remove, removeChild
  • innerHTML, outerHTML, innerText, outerText, textContent
  • setAttribute, setAttributeNS, removeAttribute, removeAttributeNS, toggleAttribute
  • querySelector, querySelectorAll, getElementById, getElementsByTagName, getElementsByClassName, getElementsByName
  • href, src, action, formAction, srcdoc
  • cookie, cookieStore, localStorage, sessionStorage
  • FormData, URL, URLSearchParams, Request, Headers, Response
  • media, Image, Audio, Video, Track, Source, Bitmap, Canvas, Blob, File
  • navigator, postMessage, document, window, globalThis, global, self, this, top, parent, frames, form, element, constructor
  • eval, Function, setTimeout, setInterval

String literals are also limited to one character.

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following element is allowed:

  • script

Within that script block, only the following characters are allowed:

  • [, ], (, ), !, +

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

Through this series of challenges, you will become familiar with the concept of XSS filters bypass!

In this challenge, the following elements are banned:

  • script, img, svg, iframe, input
  • video, audio, source, track
  • html, body, frameset, details, dialog, marquee
  • style, link, template

The following handlers are also banned:

  • ontoggle, onstart
  • onload, onerror, onclick
  • onfocus, onfocusin, onfocusout, autofocus
  • onanimationstart, onanimationiteration, onanimationend, onanimationcancel

The following attributes are also banned:

  • style=, animation, keyframes, content-visibility
  • transition, transform

The following shadow DOM primitives are also banned:

  • shadow, slot

The following APIs and globals are also banned:

  • fetch, XMLHttpRequest, sendBeacon
  • location, open(), assign(), replace(), pushState, replaceState
  • submit, requestSubmit, click, write, writeln
  • createElement, createElementNS, adoptNode, importNode, cloneNode
  • append, appendChild, prepend, before, after, insertBefore
  • insertAdjacentHTML, insertAdjacentText, insertAdjacentElement
  • replaceChildren, replaceChild, replaceWith, remove, removeChild
  • innerHTML, outerHTML, innerText, outerText, textContent
  • setAttribute, setAttributeNS, removeAttribute, removeAttributeNS, toggleAttribute
  • querySelector, querySelectorAll, getElementById, getElementsByTagName, getElementsByClassName, getElementsByName
  • href, src, action, formAction, srcdoc
  • cookie, cookieStore, localStorage, sessionStorage
  • FormData, URL, URLSearchParams, Request, Headers, Response
  • media, Image, Audio, Video, Track, Source, Bitmap, Canvas, Blob, File
  • navigator, postMessage, document, window, globalThis, global, self, this, top, parent, frames, form, element, constructor
  • eval, Function, setTimeout, setInterval

A Content Security Policy is also applied: style-src 'nonce-{random}'; style-src-attr 'none'

Connect with SSH

Link your SSH key, then connect with: ssh [email protected]

30-Day Scoreboard:

This scoreboard reflects solves for challenges in this module after the module launched in this dojo.

Rank Hacker Badges Score