Learn Pentesting: A Deep Dive into Manual Cross-Site Scripting (XSS) Testing
Welcome to this installment of the Learn Pentesting series by Numorian. In this post, we will explore manual testing techniques for identifying Cross-Site Scripting (XSS) vulnerabilities during web application penetration tests. This deep-dive is intended for highly technical audiences—penetration testers or security professionals who want a comprehensive reference for manual XSS testing.
1. Understanding Cross-Site Scripting (XSS)
Cross-Site Scripting (XSS) is a vulnerability that allows attackers to inject malicious scripts into web pages viewed by other users. There are three primary types of XSS:
- Reflected XSS: The malicious payload is embedded in a URL or request parameter and is immediately echoed back by the server in the response.
- Stored XSS: The payload is permanently stored on the target server (e.g., in a database) and then delivered to users when they request the affected page.
- DOM-based XSS: The vulnerability exists in client-side code rather than the server-side, with the attack executed as a result of modifying the DOM in the victim’s browser.
A robust understanding of these types is crucial, as each requires a different testing approach and exploitation technique.
2. Manual Testing Techniques for XSS
Manual testing is a fundamental skill for identifying XSS vulnerabilities that automated tools might miss. Here’s how to perform a systematic test:
2.1 Identifying Input Vectors
Begin by identifying all points where user input is accepted and subsequently reflected in the application output. Common input vectors include:
- Query parameters (e.g.,
?search=
) - Form fields (GET and POST)
- HTTP headers (e.g.,
User-Agent
,Referer
) - URL fragments (after the
#
)
2.2 Initial Payload Testing
A simple yet effective test payload is:
<script>alert('XSS');</script>
Insert this payload into various input vectors and observe if the script is executed. For example, if you have a search page:
https://example.com/search?q=<script>alert('XSS');</script>
Check the rendered HTML using your browser’s developer tools (Inspect Element) to verify if the script tag is present and executed.
2.3 Testing in Different Contexts
Different contexts in a web page require different payloads. For example:
- HTML Context:
Basic script injection may work directly:<script>alert('XSS');</script>
- Attribute Context:
If the input is rendered within an attribute (e.g.,<img src="...">
), you might try:Consider an image tag injection:" onerror="alert('XSS')"
<img src=x onerror=alert('XSS')>
- JavaScript Context:
When user input is embedded within a script block, consider breaking out of the current string or context:'); alert('XSS'); //
- URL/Parameter Context:
Ensure you test for payloads that might bypass URL encoding, using:%3Cscript%3Ealert('XSS')%3C%2Fscript%3E
3. Crafting Payloads and Evasion Techniques
When testing manually, the goal is to identify not only the obvious injection points but also bypass common filters. Consider the following techniques:
3.1 Encoding and Obfuscation
Filters may block common keywords (like <script>
), so try alternative representations:
- HTML Entity Encoding:
<script>alert('XSS')</script>
- Unicode Encoding:
\u003Cscript\u003Ealert('XSS')\u003C/script\u003E
3.2 Using Event Handlers
Event handlers in HTML tags can serve as alternative vectors:
<img src="nonexistent.jpg" onerror="alert('XSS')">
3.3 Breaking Out of Context
If the application sanitizes quotes or specific characters, test payloads that break out of the current context. For instance, if the user input is embedded inside a JavaScript string:
'); alert('XSS');//
This technique closes the existing string and injects a new JavaScript command.
4. Practical Example: Testing for Reflected XSS
Let’s walk through a practical example for a reflected XSS test.
4.1 The Scenario
Imagine a simple search form on a web application:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Search Page</title>
</head>
<body>
<form method="GET" action="/search">
<input type="text" name="q" placeholder="Search...">
<input type="submit" value="Go">
</form>
<div>
<!-- The search query is echoed back unsanitized -->
You searched for: <?php echo $_GET['q']; ?>
</div>
</body>
</html>
4.2 Injecting the Payload
Identify the Input:
Theq
parameter is reflected back to the page.Insert a Basic Payload:
Use the following URL:https://example.com/search?q=<script>alert('XSS');</script>
Observation:
If the payload is executed, the browser will display an alert box. Use browser developer tools to verify if the payload appears in the HTML output.Refine the Test:
If the initial test does not work, modify the payload for context. For instance, try:"><script>alert('XSS')</script>
This payload attempts to break out of any attribute context and directly insert the script tag.
5. Leveraging Developer Tools
Modern browsers provide powerful tools that are indispensable during manual testing:
Inspect Element:
Use this to view the DOM and check if your payload is present or if it has been modified by the application.Console Tab:
Observe JavaScript errors or unexpected behavior that might indicate an attempted XSS injection.Network Tab:
Inspect HTTP requests and responses to see how user inputs are being transmitted and rendered.
6. Best Practices for Manual XSS Testing
6.1 Always Validate Context
Before crafting your payload, understand the context in which the input is reflected:
- Is it part of an HTML element?
- Is it within an attribute value?
- Is it inside a JavaScript block?
6.2 Use Incremental Payloads
Start with simple payloads and then escalate complexity as needed. This helps in isolating which filters are applied and where they might be bypassed.
6.3 Document Your Findings
Take screenshots and document the payloads that trigger XSS. This documentation will be crucial when communicating findings with development teams or preparing remediation strategies.
6.4 Stay Up-to-Date
XSS testing techniques evolve over time. Regularly review the latest research and payload repositories (like the XSS Cheat Sheet) to stay ahead of emerging bypass techniques.
7. Conclusion
Manual testing for Cross-Site Scripting (XSS) is both an art and a science. While automated tools are helpful, they often miss nuances that manual testing can reveal. By understanding the context, leveraging developer tools, and crafting clever payloads, penetration testers can uncover XSS vulnerabilities that might otherwise remain hidden.
This article has provided a technical deep-dive into manual XSS testing techniques—complete with practical examples and code snippets. As part of the Learn Pentesting series, we hope this serves as a valuable reference in your security testing toolkit. Keep experimenting, documenting, and refining your approach to stay effective in the ever-evolving landscape of web application security.
Happy testing!