Press enter or click to view image in full size
A seemingly harmless feature — file uploading — became the entry point for a complete workspace takeover in a recent vulnerability found in the Dust platform. This case serves as a critical reminder of how a classic Stored Cross-Site Scripting (XSS) flaw, when combined with architectural oversights, can lead to devastating consequences, including privilege escalation and full administrative control.
The attack’s brilliance lay in its simplicity and ability to chain together several seemingly minor flaws into a high-impact exploit. It didn’t require complex code or password theft; it only needed one user to click one link.
Press enter or click to view image in full size
Here’s a step-by-step breakdown of how it worked:
xss_poc.png
. However, they manipulated the request to set the file'sContent-Type
to text/html
. The system's validation checks failed to catch this mismatch, prioritizing the declared content type over the file extension.dust.tt
). This was the pivotal mistake. By serving user-generated content from the same origin as the core application, the platform allowed any script within that file to operate within the application's trusted security context.text/html
content type and executed the JavaScript embedded in the file. Because this script ran on thedust.tt
domain, it automatically inherited the administrator's authenticated session.POST
request to the API endpoint responsible for managing members. This request promoted the attacker's own account to an "admin" role. The action was performed seamlessly in the background, using the victim's active session, with no indication that a compromise had occurred.Once the attacker gained admin privileges, it was game over for the workspace’s security. They could:
The attack didn’t require stealing the victim’s session cookie; it leveraged the active session directly, making it highly efficient and difficult to detect through traditional means.
This incident underscores several fundamental principles for securing modern web applications, especially those handling user-generated content.
The most effective countermeasure is to serve all user-uploaded files from a separate, cookie-less domain. For example, if your application runs on app.com
, user content should be served from a sandboxed domain like app-usercontent.com
. This isolates the content, ensuring that even if a malicious script executes, it cannot access the main application's cookies or make authenticated API calls.
Content-Disposition: attachment
To prevent browsers from rendering files inline, always send the Content-Disposition: attachment; filename="..."
header. This forces the browser to trigger a download prompt instead of trying to display the content, neutralizing XSS vectors embedded in files like HTML or SVG.
Never trust user-provided metadata like filenames or Content-Type
headers. Implement server-side validation that inspects a file's actual contents (e.g., using magic bytes) to determine its true type. A file ending in .png
should contain a valid PNG data structure, not HTML tags.
A well-configured CSP acts as a vital layer of defense. It can be used to block inline scripts (script-src 'self'
) and restrict where resources can be loaded from, significantly reducing the attack surface for XSS even if a malicious file is uploaded and rendered.
This case is a powerful illustration of why security must be approached in layers. A single point of failure — in this case, trusting and rendering a user-uploaded file in the same security context as the application — can unravel an entire system’s defenses.