Helpdeskz v2.0.2 - Stored XSS
# Exploit Title: Stored XSS Vulnerability via File Name
# Google Dork: N/A
# Date: 08 Aug 2024
# Exploit Author: Md. Sadikul Islam
# Vendor Homepage: https://www.helpdeskz.com/
# Software Link:
https://github.com/helpdesk-z/helpdeskz-dev/archive/2.0.2.zip
# Version: v2.0.2
# Tested on: Kali Linux / Firefox 115.1.0esr (64-bit)
# CVE : N/A
Payload: "><img src=x onerror=alert(1);>
Filename can be Payload: "><img src=x onerror=alert(1);>.jpg
VIdeo PoC:
https://drive.google.com/file/d/1_yh0UsX8h7YcSU1kFvg_bBwk9T7kx1K1/view?usp=drive_link
Steps to Reproduce:
1. Log in as a regular user and create a new ticket.
2. Fill out all the required fields with the necessary information.
3. Attach an image file with a malicious payload embedded in the
filename.
4. Submit the ticket.
5. Access the ticket from the administration panel to trigger the
payload execution.
Cross-Site Scripting (XSS) exploits can compromise the administration
panel, directly affecting administrators by allowing malicious scripts to
execute within their privileged environment. Helpdeskz v2.0.2 — Stored XSS via Uploaded File Name
This article analyzes a stored Cross-Site Scripting (XSS) vulnerability discovered in Helpdeskz v2.0.2 where the file name of an uploaded attachment is not correctly escaped before being rendered in the administration panel. The flaw allows an authenticated regular user to craft a malicious filename (for example: ">.jpg) that executes when an administrator views the ticket, allowing script execution in the admin's privileged context.
Why this matters
Stored XSS in an administration panel is high-impact: an attacker can run arbitrary JavaScript with administrator privileges, potentially stealing session cookies, performing actions as the admin, or pivoting to other parts of the application or internal network.
Summary of the vulnerability
- Product: Helpdeskz
- Version: 2.0.2
- Vulnerability type: Stored Cross-Site Scripting (XSS)
- Vector: Malicious filename inside an uploaded attachment
- Trigger: Administrator views a ticket containing the malicious-named attachment in the admin panel
- Public PoC payload (filename): "><img src=x onerror=alert(1);>.jpg
Reproduction steps (concise)
- Log in as a standard (non-admin) user.
- Create a new ticket and attach a file whose name contains script code (e.g. "><img src=x onerror=alert(1);>.jpg).
- Submit the ticket and wait for an administrator to open the ticket in the admin panel.
- When the admin views the ticket, the malicious filename is rendered without proper escaping, executing the payload.
Technical root cause
The application stores/display the uploaded file name into the ticket view without HTML-encoding or sanitizing the filename on output. When the admin panel outputs the raw filename into the HTML page, a crafted filename containing markup or event handlers becomes executable code in the admin's browser context.
Impact and severity
| Impact | Example consequence |
|---|---|
| Confidentiality | The attacker may steal admin cookies, CSRF tokens, or other sensitive DOM-held data. |
| Integrity | Scripts can perform actions as the admin (e.g., modify tickets, change settings) if the admin interface allows it. |
| Availability | Less likely direct, but could be used to lock out accounts or cause admin disruption. |
Estimated severity: High. CVSS-like considerations: this is a stored XSS with admin-level impact; treat as urgent to patch.
Proof-of-Concept (PoC) example
The following demonstrates the malicious filename used in real testing:
">
.jpgExplanation: The leading double quote closes an HTML attribute (or breaks out of surrounding text), then the <img> tag with an onerror handler runs JavaScript when the browser fails to load the image. When this filename is injected into a page without encoding, it executes.
Detection and auditing techniques
- Search template/view source for places where filenames are printed without escaping (e.g.,
echo $file['name'],, or template helpers that don't auto-escape). - Look for usages of functions like
print,echo, or raw template placeholders rendering user-controlled values. - Scan uploads: upload filenames containing special characters and see if they appear encoded in admin pages.
- Automated scanners for stored XSS can help, but many will miss file-name based injection if filenames are filtered by scanners; manual tests are recommended.
Fixes and recommended mitigations
A secure remediation strategy consists of several layers:
- Normalize and sanitize uploaded filenames server-side before storage.
- Never trust client-provided names for display; always HTML-encode filenames on output.
- Store a server-side generated internal filename (unique ID) and optionally keep the original name in sanitized form for display only after strict cleaning.
- Validate file types by MIME type checking and limit allowed file extensions.
- Harden response headers and implement a Content-Security-Policy as a defense-in-depth measure.
Example patch for filename output escaping (PHP)
<?php
// Unsafe: raw output of filename
// echo $attachment['name'];
// Safe: HTML-encode the filename when rendering
echo htmlspecialchars($attachment['name'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
?>Explanation: htmlspecialchars converts characters like &, <, >, and quotes to their HTML-entity equivalents, preventing inserted markup from being interpreted by the browser. Use ENT_QUOTES to encode both single and double quotes and ENT_SUBSTITUTE to safely handle invalid byte sequences.
Better: sanitize uploaded filename on receipt (server-side)
<?php
function sanitize_filename($name) {
// Remove directory traversal and null bytes
$name = basename($name);
$name = str_replace("\0", '', $name);
// Allow only a strict subset of characters for display: letters, numbers, dot, dash, underscore
$name = preg_replace('/[^A-Za-z0-9._-]/', '_', $name);
// Optionally limit length
$name = mb_substr($name, 0, 200, 'UTF-8');
return $name;
}
// When processing an uploaded file:
$origName = $_FILES['attachment']['name'];
$safeName = sanitize_filename($origName);
// Generate a server-side unique filename to store on disk
$storageName = bin2hex(random_bytes(16)) . '.' . pathinfo($safeName, PATHINFO_EXTENSION);
// Move uploaded file to safe storage location
move_uploaded_file($_FILES['attachment']['tmp_name'], '/var/www/uploads/' . $storageName);
// Store $storageName in disk field and $safeName in database for display (optional)
// When displaying, always escape: echo htmlspecialchars($safeName, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
?>Explanation: sanitize_filename removes dangerous characters, replaces them with underscores, and limits length. The file is stored under a random internal name (no user-controlled characters in stored filenames), preventing accidental execution when filenames are used as part of URLs or file paths. Always apply escaping on output even after sanitization.
Content-Security-Policy (CSP) example (defense-in-depth)
Header set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self';"
Explanation: A restrictive CSP helps prevent inline script execution and loading of scripts from untrusted origins. CSP should not be a replacement for proper output encoding; it's an additional mitigation layer that reduces impact if an XSS slip occurs.
Additional server-side hardening
- Whitelist allowed file extensions (e.g., jpg, png, gif, pdf) and verify MIME type using fileinfo (finfo_open) or getimagesize for images.
- Serve uploaded files from a dedicated domain or subdomain (e.g., static.example.com) with separate origin to limit cookie exposure.
- When serving attachments that might be rendered inside browsers, set
Content-Disposition: attachmentto force download and reduce inline rendering. - Set secure headers:
X-Content-Type-Options: nosniff,Referrer-Policy, and properSameSitecookie attributes.
Suggested code to force safe download (PHP)
<?php
// Assume $storageName is internal file name, $displayName is sanitized
$filepath = '/var/www/uploads/' . $storageName;
if (is_file($filepath)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . str_replace('"', '', $displayName) . '"');
header('Content-Length: ' . filesize($filepath));
readfile($filepath);
exit;
}
?>Explanation: Serving attachments as "attachment" prevents many browsers from rendering user-controlled content inline. Note that the filename in Content-Disposition should also be sanitized to avoid injection in headers or breaking the header format.
Testing and verification after patch
- Upload files with filenames containing HTML/JS payloads and confirm they do not execute when a ticket is viewed.
- Confirm filenames are shown encoded (e.g., <img> appears as text in DOM, not as elements).
- Verify server stores internal filenames with no user-controlled characters and disallows directory traversal.
- Run automated security scans and manual proof-of-concept tests against the admin interface.
Practical detection grep/scan hints for developers
- Search repo for raw echoes:
grep -R "echo \$.*name" -nand review each occurrence for proper encoding. - Look for template engine usage — ensure auto-escaping is enabled (e.g., Twig autoescape).
- Review upload handling: check that
$_FILES['...']['name']is sanitized before persisting or reuse.
Responsible disclosure and patching advice
Vendors should:
- Patch all locations where filenames or other user-supplied metadata are output to HTML — enforce output encoding by default.
- Apply filename sanitization and store attachments using non-user-controlled storage names.
- Release security advisories, provide upgrade paths, and notify administrators to update to patched versions quickly.
Conclusion
File name-based stored XSS is subtle but highly dangerous, especially when it affects privileged admin pages. Fixing it requires both server-side sanitization and consistent output encoding, plus defense-in-depth measures such as CSP and proper file serving behavior. Applying the code patterns and practices in this article will mitigate this class of vulnerability and improve overall upload safety.