Reflected XSS, also known as Reflected Cross-Site Scripting, is a dangerous vulnerability that occurs when a web application includes user input in its output without proper validation or encoding. Reflected XSS allows attackers to inject malicious scripts that are then executed in the context of the victim's browser.
Identifying and understanding how reflected input parameters are vulnerable is crucial in ensuring the security of web applications. By following a systematic approach and using tools like Arjun, Param Miner, and Paramspider, security researchers can uncover hidden parameters and potential points of exploitation.
Preventing XSS requires proper input validation, output encoding, and awareness of where user input is reflected within the HTML context. Today, we'll cover best practices to prevent a reflective XSS attack to help developers protect their applications from the risks posed by this attack.
We'll also explore how to exploit XSS when input is reflected in JavaScript code, for example, when the user input is reflected inside a <script> tag or an event handler. Let's dive in.
What is a Reflected XSS attack?
Reflected XSS is a type of Cross-Site Scripting attack where the malicious script is injected via user input and immediately reflected back in the web page’s response. This occurs when input is improperly validated and included in the output without proper encoding.
Finding the Reflected Input Parameter
To begin, understand the application's functionality and identify all input vectors, such as URL parameters, forms, and headers. Test these points using simple, unique strings to determine if the input is reflected in the response. Use browser developer tools to inspect the responses and confirm reflections. Once you have identified the points where inputs are reflected, you can proceed to test for XSS vulnerabilities.
GET /search?query=test1234 HTTP/1.1
Host: example.com
User-Agent: test1234
Referer: https://example.com/previous-page
Cookie: session=test1234; user=test1234
Manual Identification
To identify reflected input parameters in a web application, follow these steps:
1. Identify Input Vectors
- Locate Input Fields: Identify all the places in the application where user input can be provided. This includes URL parameters, form fields, and HTTP headers.
2. Choose a Unique String
- Simple Test Payload: Use a unique string as a basic payload to help identify reflected parameters. For example, use “test1234”.
3. Inject string into Parameters
- URL Parameters: Add the test payload to different URL parameters and observe the responses.
- Example: http://example.com/search?q=test1234
- Form Fields: Enter the payload in various form fields and submit the forms.
- HTTP Headers: Modify headers like “User-Agent”, “Referer”, and “Cookie” to include the payload.
4. Analyze Responses
- View Source Code: Use the browser’s developer tools to view the HTML source code of the response. Look for the injected string in the response.
- Highlighted Reflection: If the string appears in the HTML, the parameter is likely reflected.
5. Confirm Reflection
- Context Reflection: In View-Source ensure the reflection of user input “test1234” appears in a context where we can potentially break the HTML context.
Automated Testing
- Automated Tools: Use tools like OWASP ZAP or Burp Suite to automate the process of injecting payloads and analyzing responses.
- OWASP ZAP: Use the Active Scan feature to automatically test for reflected parameters.
- Burp Suite: Use Intruder to send multiple payloads to different parameters and analyze the reflected responses.
Open Source Tools:
1. Arjun
Purpose: Arjun helps find hidden GET and POST parameters in endpoints.
Installation: Install from https://github.com/s0md3v/Arjun
Usage:
- Identify Hidden Parameters:
arjun -u http://example.com
- Scan Multiple URLs:
arjun -i urls.txt -o results.json
2. Param Miner
Purpose: Param Miner, a Burp Suite extension, discovers hidden and unlinked parameters.
Installation: Install Param Miner from the Burp Suite BApp Store.
Usage: Right-click on the target in Burp Suite → extensions → param Miner → guess params.
Analyze the requests in extensions → param Miner → output
3. Paramspider
Purpose: Param Spider fetches the parameters from the web archive.
Installation: Install from https://github.com/devanshbatham/ParamSpider
Usage:
- Finding Parameters from web archive:
paramspider -d example.com
- Customize Search:
- Use options like l to limit the scope and p to specify a particular parameter.
Paramspider will show you the parameters and where exactly you can fuzz.
Enumeration for Reflection Context
In HTML context, string reflection refers to situations where a web page includes user-supplied input in its HTML output without the proper sanitization or encoding, such as within HTML tags. This lack of procedures can potentially lead to Cross-Site Scripting (XSS) vulnerabilities. Let’s look at some examples.
Reflection: In Between Tags
- Suppose you have a search form on your website where users can enter a search query. If a user searches for “test1234”, the URL might look like this:
- After processing this input, the web application includes it in the HTML response:
In this instance, the system reflects the string in the HTML context between HTML <h1> tags. Sometimes, the reflection might occur within a <p> tag, <title> tag, or even a <div> tag, depending on the application.
Reflection: Attribute Value
When an application reflects user input within an HTML attribute without proper sanitization or encoding, it creates XSS vulnerabilities. For instance, if an application directly reflects your input like test1234 in a tag such as <input type="text" value="test1234">, you can inject an XSS payload to execute malicious scripts.
Input: https://abc.com/?search=test1234
Reflected Output:
Methodology for Testing XSS
Once you have identified where the string is reflected and how it appears, such as between HTML tags, you need to know how to inject an XSS payload that will work. Simply choosing random payloads from cheat sheets and spamming them in input parameters, or running fuzzing tools, won’t work all the time. This approach can miss potential XSS vulnerabilities if the payloads don't hit. A better approach is to test characters individually or in combination and observe how the application responds. Let’s see how it is done.
Step 1: Enumeration for Reflection Points
In this step, we examine how the user input from the search parameter is reflected within the HTML context.
- Input: https://abc.com/?search=test1234
- The user input from the GET-based “search” parameter is reflected in two places within the HTML context.
We identified two primary reflection points:
- Reflection 1: The user input is reflected between HTML tags, specifically <h1>. Other tags where input might be reflected include <p>, <strong>, <head>, and more.
- Reflection 2: The user-supplied input is reflected in the attribute value. There could be different possibilities for exploitation depending on the input, such as if type=hidden.
After this, we proceed to step 2 to understand how the input is being encoded and how to break the context in both reflection cases.
Step 2: Understanding the HTML Context
The purpose of this step is to understand what is being blocked or filtered by the application.
- Input: https://abc.com/?search=test1234<
- This input includes a starting angle bracket < and double quotes ", which are crucial to disrupt the HTML contexts for Reflection 1 and Reflection 2.
We need to observe how the reflection would appear in the application’s “view-source:” if there is no filtration of the user’s potentially harmful input.
Step 3: No Sanitization in Both of the Reflections
In this case, no encoding of special characters is happening. It is noticeable that our second special character’s color is changed in the Reflection 2 which can be considered as the valid syntax of HTML.
- Input: https://abc.com/?search=test1234<img+src=x></img>"
The color change in Reflection 1 indicates that the <img> tag we entered is interpreted as valid HTML. In contrast, the color of the user input in Reflection 2 remains the same, suggesting the input is not interpreted as HTML.
After this, we understand how we can break the syntax in these two cases when the input is getting encoded in URL encoding or getting sanitized.
Step 4: Performing XSS in Reflection 1
Input: https://abc.com/?search=test1234<img+src=x+onerror=alert('Cobalt')></img>"autofocus=""onfocus="alert('Cobalt')"
In this case, the payload is:
- <img>: This is an HTML image tag used to embed an image in the web page.
- src=x: This is an attribute of the image tag. src stands for source, which is used to specify the URL of the image. Here, x is a non-existing source, which will fail to load.
- onerror: This is an event attribute which executes JavaScript code when an error occurs while loading an external file (in this case, an image).
- alert('Cobalt'): This is a JavaScript function that displays an alert box with the message ‘Cobalt’. This function executes when the onerror event triggers due to the failure of the image load.
Step 5: Performing XSS in Reflection 2
- Input: https://abc.com/?search=test1234<img+src=x></img>"autofocus=""onfocus="alert('Cobalt')"
In this case, our attributes autofocus and onfocus are successfully injected as a valid HTML syntax.
The payload is broken down as follows:
- <input type="text" name="search" id="search" value="test1234<img src=x></img>"> is an input field of type text with the name and id “search”.
- autofocus is an HTML attribute that specifies that the input field should automatically get focus when the page loads.
- "onfocus="alert('Cobalt')"" is an HTML attribute that triggers a JavaScript alert with the message”Cobalt” when the input field gets focus.
The XSS in Reflection 2 is also successful. We have seen just two very basic cases of enumerating the XSS issue; there are numerous ways to bypass the advanced WAF. It is suggested to learn the basics of JavaScript to understand more about the XSS.
How to Prevent XSS?
1. Sanitize Input:- Ensure you remove or escape harmful characters from user input.
- For example, strip out or encode <, >, &, and ".
- Properly encode any user-supplied input before reflecting it in the HTML response.
- Convert special characters to their respective HTML entities. For instance, " should become ".
- Implement secure frameworks that handle input sanitization and output encoding automatically.
- Avoid directly inserting user input into the HTML structure.
What is <script> tag?
A <script> tag in HTML is a way to include and run JavaScript code on a webpage. You use the <script> tag to either write JavaScript code directly or link to an external JavaScript file. When the browser loads the webpage, it finds the <script> tag and executes the JavaScript code.
Examples: Inline JavaScript
<!DOCTYPE html>
<html>
<head>
<title>Example Page</title>
</head>
<body>
<h1>Hello, World!</h1>
<script>
alert('This is an inline JavaScript code!');
</script>
</body>
</html>
External JavaScript
<!DOCTYPE html>
<html>
<head>
<title>Example Page</title>
<script src="script.js"></script>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
In the first example, the JavaScript code runs directly from within the HTML file. In the second example, the JavaScript code is in an external file called script.js, and the <script> tag links to that file.
How Browsers Parse and Render HTML and JavaScript
1. HTML Parsing:
- The browser reads the HTML file from top to bottom.
- It builds a structure called the Document Object Model (DOM), which represents the elements on the page.
2. CSS Parsing
- If the HTML file includes CSS (Cascading Style Sheets), the browser reads and applies the styles.
3. JavaScript Execution
- When the browser encounters a <script> tag, it pauses HTML parsing to load and execute JavaScript.
- If the <script> tag includes src, the browser fetches the external JavaScript file and runs it.
4. Rendering
- After parsing HTML, CSS, and executing JavaScript, the browser combines the DOM and CSSOM to render the webpage visually.
Possible Reflection points inside <script> tag
Reflection points in <script> tags are places where user input can be dynamically inserted into JavaScript code. Here are some common reflection points within <script> tags:
1. Inline JavaScript
- This script assigns the value USER_INPUT to the variable userInput and then logs it to the browser's console. If USER_INPUT is derived from user input and contains malicious JavaScript, it could lead to cross-site scripting (XSS).
2. Dynamic Script Content
<script>
var message = "Welcome, " + "USER_INPUT";
alert(message);
</script>
-
In this script, a message is dynamically created by concatenating the string Welcome, with USER_INPUT. The resulting message is then displayed in an alert box. If USER_INPUT is untrusted user input, it could include harmful script content, leading to an XSS attack.
3. DOM
<script>
document.getElementById("username").innerHTML = "USER_INPUT";
</script>
-
This script targets an HTML element with the ID username and replaces its inner HTML with the value of "USER_INPUT". If "USER_INPUT" contains malicious content, it could alter the webpage's structure or behavior, leading to XSS vulnerabilities.
4. Event Handlers
<script>
document.getElementById("myButton").onclick = function() {
alert("Hello, " + "USER_INPUT");
};
</script>
- Here, an event handler is assigned to an HTML element with the ID myButton. When the button is clicked, an alert box displays a message that includes "USER_INPUT". If "USER_INPUT" is unsafe and is not properly sanitized, it could trigger XSS when the event is fired.
5. URL Parameters
<script>
var params = new URLSearchParams(window.location.search);
var user = params.get('user'); // USER_INPUT
document.write("Hello, " + user);
</script>
- This script extracts the user parameter from the URL's query string and uses it to write a greeting directly into the document. If the user parameter contains unsensitized input, it could lead to XSS by executing malicious scripts directly in the user's browser.
How to break out of JavaScript syntax?
Now, let’s try to break the context between the <script> tags using the example.
Input: https://example.com/?search=fuzz
Reflected Output:
Here, in this case, we have two reflection points: one between <h1> tags and another between <script> tags. So now, let's try to break the content in Reflection 1 by adding a < character
Input: https://example.com/?search=fuzz<>
Reflected Output:
In the above image, you can see that the application is encoding the inputs and converting the < character to < and the > character to > in both of the reflection points here. This means we cannot perform XSS in Reflection 1.
Let’s move on to Reflection 2 now. The syntax is JavaScript here, so we need to insert a payload that breaks the context and triggers the alert. First, the application is using a single quote here. Let’s try inserting a single quote (') to break the context.
Input: https://example.com/?search=fuzz<>’
Reflected Output:
Here, you can analyze Reflection 2 and observe that the application does not sanitize the ' character. Now, let’s attempt to perform XSS here. In JavaScript, you can insert a string using two hyphens (-). Therefore, our payload would be -alert(1)- in this context.
Input: https://example.com/?search=fuzz<>’-alert(1)-’
Reflected Output:
In Reflection 2 you can see that the color of syntax has been changed and it is a valid JavaScript syntax.
var searchTerms = 'fuzz<>'-alert(1)-'';
Breakdown of the Code
- 'fuzz<>'
- This is a string that contains the HTML entities < and >
- < stands for the less-than symbol (<).
- > stands for the greater-than symbol (>).
- So, 'fuzz<>' is equivalent to the string 'fuzz<>', where the < and > characters are encoded in HTML entity form.
- This part is a string that seems to be intended as an XSS payload or test input. It contains the JavaScript function alert(1) surrounded by hyphens.
- The alert(1) function would, if executed in a browser, display a popup alert box with the number "1".
Final Thoughts
Testing for XSS threats is important. Instead of just throwing a lot of random payloads into an application, take the time to check each piece of input. Instead, try enumerating what is being blocked or sanitized by the application and find alternatives to bypass the restrictions. This way, you’re sure to catch any weak spots.
Citations: Portswigger