This post may contain affiliate links/ads and I may earn a small commission when you click on the links/ads at no additional cost to you. As an Amazon Affiliate, I earn from qualifying purchases. You can read my full disclaimer here.
Cross-site Scripting aka XSS is a web-based attack used to infect the users of the website by injecting client-side malicious code into the user’s web browser using a legitimate webpage.
XSS attacks are used to target the users of the website, rather than the web-server itself. So, even if you managed to get a reverse shell, or able to run malicious code. It will be executed on the user’s device and not the web-server.
These are the most common type of vulnerability out there on the internet. Hacking and gaining access to the server is more difficult as compared to hack a normal person, also users are tricked easily as hackers use a vulnerability present in the legitimate website to carry out the attacks.
In this post, you’ll learn how to discover, exploit and secure against cross-site scripting vulnerability.
Note: I will be performing all these exploits on my pen-testing lab. Don’t try these on real-world websites or the websites you don’t have permission to test. I/TechSphinx shall not be held responsible for your illegal actions.
Checkout my Setup a Penetration testing lab post, if you don’t know how to create your own pen-testing lab.
Let’s start by looking at the types of XSS vulnerability.
Types of XSS
There are 3 major types of XSS vulnerability:
1. Stored or Persistent XSS
In stored XSS, an attacker is able to inject malicious script into the webpage which is stored into the web site’s database and every user that visits the webpage will be infected.
For instance, take the scenario of a blog where you can comment on people’s post. These comments are stored and visible by every other visitor of that blog post. Now, imagine if this website is vulnerable to stored cross-site scripting. A hacker can easily insert his malicious script into the comment box, which will be stored by the web-server and everyone (including the owner or the staff of website) who visits the web-page will be infected by the script.
In this type of XSS, the hacker doesn’t have to trick the user into clicking a URL or download anything. The user will come and browse the webpage normally and get hooked by the malicious script.
This is why stored XSS is the most dangerous type of XSS vulnerability.
2. Reflected XSS
Unlike stored XSS, reflected XSS will not get stored into a website’s database. It will only work when the user clicks on the malicious URL crafted by the attacker. The URL will be based on a legitimate website, so users can be tricked easily to click on it.
The attacker has to send the URL to the target user by email, SMS, messaging apps or by any means necessary.
3. DOM based XSS
The cool animations on a webpage, changing of colors or text on the fly or any updating of data without refreshing or reloading the web page are done by manipulating DOM.
DOM-based XSS attacks are carried out just like reflected XSS by manipulating a URL and sending it to the target user.
However, there is a difference, in reflected XSS the hacker manipulates the URL and send it to the user, when the user clicks the URL it sends a request to the web-server, then the web-server serves the webpage to the user with the malicious script injected by the hacker.
In DOM-based attacks, no request is sent to the server. This means no server-side validations or filters will work against the DOM-based XSS attacks.
Now you know the types of XSS, let’s see them in action one by one.
Discovering Cross-Site scripting vulnerability
Stored or Persistent XSS
Let’s start by discovering Stored XSS on different difficulties of DVWA.
Login to DVWA and go to DVWA security tab, then set the difficulty of DVWA to low.
Go to XSS (Stored) and enter any name and message and click on sign guestbook to see everything is working normally.
As you can see, the name and message are stored in the database successfully and are persistent.
name = rahul
message = <script>alert("hello")</script>
the above script creates an alert dialog box which says “hello”.
As you can see it’s executed successfully. This means you can execute any malicious script instead of an alert dialog.
Later in the post we will see how to exploit them and hack our target user.
Before proceeding to medium difficulty let’s Clear/Reset our DVWA database, as stored XSS vulnerability stores the script in the database permanently, it will run every time we visit the XSS (stored) tab of DVWA.
To Clear/Reset db, navigate to Setup / Reset db tab of DVWA and click Clear / Reset Database button at the bottom of the page.
Let’s change the difficulty to medium and try out the code from the low difficulty. You’ll notice it doesn’t work. This means the server is using some sort of validation.
Also in this level, the message body is secure against the XSS vulnerability.
Right click on the name field and click on inspect element.
Change the max length value from 10 to 100.
Now, you can enter more than 10 characters in the name field.
The normal script tag won’t work, so just modify the code by capitalising some of the letters of the word “script” to “sCriPt”.
Name = <sCriPt>alert("hello")</sCrIpt>
Message = some message
And click on “sign guestbook”.
You’ll see an alert dialog box just as before.
Clear / Reset database and change the difficulty to high.
Inspect element and change max length of name field from 10 to 100 just as we did in medium difficulty.
Now, the code of low or medium difficulty will not work because in high difficulty it’s filtering the word “script” (Even the capitalised ones).
So let’s try some other code.
Name = <img src=x onError=alert("xss")>
Message = any message
And click on “sign guestbook”.
This code will also create an alert dialog but only when a certain condition is met. The condition here is “onError”, if there is an error it will create the alert dialog.
The condition is fulfilled in this case because the image tag is given a source (src=x) that doesn’t exist therefore it will throw and error and onError will create alert dialog.
None of the exploits will work in the impossible difficulty level. It contains all necessary security measures against stored XSS.
You’ve bypassed low, medium and high levels of difficulty in stored XSS. It’s time to move on to reflected XSS.
All the exploits we tried in stored XSS low, medium and high difficulty will also work on reflected XSS low, medium and high difficulty respectively.
Set DVWA difficulty to low and navigate to XSS (Reflected) tab.
It’s asking for your name, enter your name and click submit.
You will notice the name is reflected.
Now, let’s try to inject script instead of an actual name.
It will reflect an alert dialog saying “hello”. This means its vulnerable to reflected XSS, so whatever malicious script we inject in here will get executed. Also note, the code we injected is also reflected in the URL.
Now, this is not permanently stored in the database. (Check by clicking on XSS (reflected) tab again, you’ll notice everything is back to normal, without reflecting anything.)
You have to send the above URL to your target user. Only when he/she clicks it, the script will execute.
Change the difficulty to Medium and run the code from low difficulty, you’ll notice it doesn’t work anymore. The server is filtering the word “<script>”, so let’s try the capitalisation trick we used earlier in the stored XSS.
Some real world web-servers also filter out the quotes (“,’), so let’s see how to bypass that.
If you cannot use quotes (single or double both), then you have to convert your script into CharCode.
Go to any online string to CharCode converter.
I am using https://charcode98.neocities.org
Write the text, you want to convert and click on “charCodeAt()” button.
Now, our new script code (without any quotes) will be something like this:
<ScRipT>alert(String.fromCharCode(116, 101, 99, 104, 115, 112, 104, 105, 110, 120))</sCriPt>
If you execute this script, it will show the alert dialog containing your desired text (in my case, it’s techsphinx).
This way you can bypass the filtering of quotes.
Set difficulty to High. The webserver is filtering the word “script” altogether.
So let’s try the same exploit from stored XSS high difficulty.
<img src=x onError=alert("xss")>
Great, it works!!
This difficulty contains all the security measures, to secure against reflected XSS vulnerability.
Now, let’s head over to XSS (DOM) to discover DOM-based XSS vulnerabilities.
DOM Based XSS
Just like reflected XSS, you need to send the manipulated URL to the target user.
Set difficulty to low and navigate to the XSS (DOM) tab of DVWA.
Select any language from the dropdown and click on select. You will notice a change in URL.
The “default=English” will reflect in the URL if you have selected English language.
Let’s change it from English to our alert script and click enter on your keyboard.
An alert dialog popup appears. This means this webpage is vulnerable to XSS attack (DOM) .
Now, let’s level up the difficulty to medium and try running a script.
If you execute the script for alert dialog, it won’t work. The server is more secure now and is filtering the script tag.
So, let’s try alert using the image tag.
But if you put it directly just like we did before, it won’t work because we are entering value in place of the word “English” which is a part of select tag.
We need to close the select tag before entering our image tag code.
You can inspect element and see the structure for better understanding.
Type anything in place of English in the URL and hit enter. It will reflect your text in the dropdown.
Right click on the dropdown and click on Inspect Element.
Open the select tag, and you will see that the text you entered is in the value attribute of the option tag (which is a part of select tag).
If you put your image script in here, it won’t work as we want it to.
So, let’s include the closing of the select tag in our code.
</select><img src=x onError=alert("hello")>
Doing this, will close the select tag and execute the image tag without being a part of select tag.
You will see the alert dialog saying “hello”.
Set the difficulty to high.
Now, the webserver is more secure and is not excepting anything that doesn’t include the given options.
This means if we change the word “English” (or any other given option) from the parameter “default=English”, it will change back to default selected language.
You can try by entering any text in place of English, it won’t work.
So, let’s try to enter anything with the word English using “&”.
So, it’s accepting anything we write after “&”.
Let’s try to inject script using the above trick.
It works like a charm.
This level is impossible to hack; it contains all the security measures to secure against XSS (DOM) vulnerabilities.
Congrats, you have bypassed all levels of difficulty in all three types of XSS.
But in real webservers, there are many filtering and validation to prevent XSS.
This means you cannot always just capitalise some of the words to bypass the filters, you have to be more creative than that.
The Open Web Application Security Project (OWASP) has provided a cheat sheet to bypass many XSS filters and validation. You can check the cheat sheet and get an idea how to bypass XSS validations in an advanced way.
Exploiting Cross-Site Scripting Vulnerability
You know how to discover XSS vulnerabilities, but simply creating an alert dialog will not do you any good. So, let’s use XSS vulnerability to hack a target computer and do some fun stuff.
It doesn’t matter what method you use to discover XSS vulnerability, to exploit it you need a malicious script.
This malicious script will be injected in a URL which you send to target user except for the stored XSS in which you don’t have to send anything, the victim will be automatically hooked whenever he/she visits the website.
To create the malicious script and do other fun stuff, we need a tool called “Beef”.
First let’s install beef on our Kali machine.
1. Open a terminal and run the command to install beef via apt.
apt install beef-xss
2. Once installed start beef by running:
3. It will ask you to set a password for beef, enter your desired password (The password is invisible, you will not see anything like “****” while typing the password)
4. After typing the password hit enter.
5. Beef will automatically open its login page in the browser.
6. Use login username: beef and password: which you’ve set in the 3rd step.
7. If everything done correctly, then you’ll login into beef’s control panel.
Now, beef has automatically created our malicious script named hook.js, you may have noticed when you were configuring beef.
Replace the <IP> with the IP of attacker machine. In my case it’s 10.0.2.15
Finally, our script is ready for action.
Let’s use stored XSS to convey our exploit to the target.
1. Set security to low
2. Navigate to XSS (Stored)
3. Type anything in the name field
4. Right-click on the message box and change its max length value from 50 to 500. (just like we did before when discovering stored XSS vulnerability)
5. Inject the exploit:
6. Click on “sign guestbook”
We’ve successfully planted the exploit. Now wait for a victim to fall in our trap.
In this case, the victim is a Windows machine. So, let’s pretend to be the victim and navigate to XSS (Stored) tab on DVWA from the windows machine.
Now, head over to beef control panel on the attacker machine and check “online browsers” under in the hooked browser section (on the left side).
You will see the two hooked browsers, one of the attacker machine and second from the target windows machine.
Now, if you click on the IP address of the target in beef, you can gather necessary info about the target’s browser. This info is great if you want to further exploit the user and hack his/her entire computer.
If you navigate to the Commands tab, you’ll see a number of exploits. Since this is not a Beef tutorial, I will just show you only 2 of them.
First: To send an alert to the target user saying that “You have been hacked!”
1. Navigate to commands tab
2. Search for an exploit called alert.
3. Click on the “Create alert dialog”
4. Type any message in the alert text box on the right.
5. Click on execute.
5. Navigate to the windows machine, you’ll see an alert dialog.
Second: To take a screenshot of the target’s browser. (Spy on the target)
1. Go to commands tab again and search for “spy”.
2. Click on Spyder Eye.
3. Click on execute.
4. Now, click on the command that get executed.
5. You will see the result of the command (screenshot of the user’s web browser)
There are many other cool exploits you can perform using beef. Experiment with them, especially the keylogger which tells you everything the user types in the browser. You can get the username and password of their accounts.
Secure against Cross-Site scripting vulnerability
Now, you know how to discover and exploit XSS vulnerability. It’s time for you to know how to secure against them.
XSS exploits are injected into the parameters of the web page. Some of these parameters are provided to the user such as a search box or comment box, other parameters are that invisible to the user such as the “id”, “img src” can be injected too.
So, in order to prevent XSS attacks, a security expert should not allow users to inject any type of code into the web page. You must escape any special characters so that the browser treats the given input as text to display rather than treating it as code and executing them.
HTML SPECIAL CHARACTERS AND THEIR RESPECTIVE ESCAPE CODES. & --> & < --> < > --> > " --> " ' --> ' / --> /
Now, there are many ways to escape special characters. You can create a script of your own that converts it or you can use a function called “htmlspecialchars” to achieve the same.
This is what happening in the impossible difficulty.
Whatever script you inject in here will not be treated as a code by the browser, so it won’t execute. It will be treated and displayed as a regular text.
You should always use validations, filters and escape all elements in a webpage where an untrusted data can be injected.
OWASP has also provided an XSS Prevention cheat sheet. You can check that out if want to go into detail of escaping every injectable parameter in a website.
More Cross-Site Scripting Challenges
To practice your cross-site scripting skills, there are many online and offline challenges. I am mentioning a few of them in this post.
1. Google XSS Game
Google has created its own XSS game. The game is simple and contains 6 levels. It’s great for beginners to practice their newly gained XSS exploiting skills.
2. Leettime XSS lab
Leettime also contains interesting challenges like Google XSS game. Challenges are fairly easy and great for beginners. It doesn’t take much time to complete. Leettime XSS lab is a great way to put our skills to the test.
3. Alert(1) to win
This one has 8 levels and poses a greater challenge than Google’s XSS game and Leetime XSS challenge. As the name suggests, you have to create an alert dialog saying “1” to win. Once you’re familiar with XSS exploits, give this one a go.
4. OWASP Mutillidae II
If you have metasploit 2 installed, you’ll get both DVWA and mutillidae. We’ve seen DVWA in this post, you can also get your hands dirty on mutillidae.
5. OWASP Webgoat
OWASP provides many great tools, related to cyber-security. Webgoat is one of their pen-testing tool which consists of many vulnerabilities like DVWA. You can download and install webgoat from their official page and practice your XSS skills. They even have a container of webgoat on docker hub. If you are familiar with docker, you can even try that.
Congrats, you’ve reached the end of the ultimate guide of cross-site scripting. Now, you know how to discover, exploit and prevent cross-site scripting. The only thing that’s left is to practice your newly gained skills.
There are so many ways to bypass cross-site scripting filtering and validation including the ones that are provided in the OWASP cheat sheet, you need to have patience and creativity to try and explore new ways.
You should also try my other posts, to learn about other vulnerabilities in web applications.