Unrestricted File Upload
Summary
File upload validation that only checks a file's extension is trivially bypassed a handful of well-documented ways: manipulating the Content-Type header sent alongside the file, since some validators trust that header instead of inspecting the file itself; uploading an HTML or SVG file that gets served back with a content type the browser will render, turning a simple upload into stored XSS; and, most severely, uploading a server-side scriptable file (PHP, JSP, ASP) that the web server will execute if it lands somewhere reachable. Common bypass techniques for extension-based filters include double extensions (shell.php.jpg), null-byte injection, and case-mixing (shell.PHP) against filters that only check for lowercase matches. A less obvious but real escalation path: uploading a file that isn't executable code at all, but is a malicious driver or plugin file (a crafted SQLite JDBC driver, for instance) that a downstream service loads and executes when it processes the upload.
Top Affected Components / Targets
Profile picture / avatar uploadDocument attachment uploadTheme / template asset uploadEmail-signature uploadBug-report attachment upload
Common Attack Vectors
Upload .php with image Content-TypeUpload shell.php.png (double extension)Upload shell.php%00.png (null byte)Upload .phtml / .asp;.png / .aspx variantsUpload .html with embedded XSSUpload .svg with embedded scriptUpload SQLite database as JDBC driver source
Common Payloads
shell.php, shell.php.png, shell.php%00.pngshell.phtml, shell.asp;.png<?php system($_GET['c']); ?><svg onload=alert(1)><html><script>alert(1)</script></html>Content-Type: image/jpeg trick (Content-Type rejected but file extension allowed)Content-Type: text/html; image/png (header trick)
Detection Strategy
Extension-bypass and content-type manipulation testing covers the code-execution path, while checking whether an uploaded HTML or SVG file gets served with a content type that lets the browser render it (rather than forcing a download) covers the stored-XSS variant of the same underlying bug: both trace back to the same missing content-type enforcement on upload.
Tip: Testing tools that run these checks in parallel across every discovered endpoint can cut the time required substantially compared to fully manual testing, as long as they confirm findings with more than one signal to keep the false-positive rate down.
False-Positive Notes
Same as the parent syntheses.
How to Test
Manual Testing Methodology
Here is a systematic approach to identifying Unrestricted File Upload vulnerabilities in a target application.
Before testing, map all input vectors that could be affected. Identify parameters, headers, cookies, and request bodies that interact with the vulnerable component. A proxy such as Burp Suite or OWASP ZAP, paired with normal browsing of the target, is usually enough to build this list.
Send a legitimate request and record the normal response: status code, content length, response time, and any identifying tokens. This baseline matters because it's what you'll compare later responses against once payloads are involved.
Inject test payloads into each identified input vector one at a time. Start with benign detection payloads before escalating to anything that could actually trigger the vulnerability. For Unrestricted File Upload specifically, attempt to upload a file with a dangerous extension disguised via double extensions (shell.php.jpg), null bytes, case variation, or a mismatched declared Content-Type, and separately test whether an accepted file type (like SVG or HTML) can carry an embedded script.
Compare the response against your baseline, looking specifically for whether the uploaded file is reachable at a predictable or discoverable URL and executes as code (or renders as active content) rather than being served as inert data.
Once a potential vulnerability is detected, confirm it with at least a few independent test cases to rule out coincidence. Document the exact request and response as proof. For Unrestricted File Upload, a confirmed finding typically means showing that attacker-controlled input changes the application's behavior in a way that matters for security, not just that a payload was reflected somewhere harmless.
Real-World Impact
Real-World Impact
Unrestricted file upload lets an attacker place a file of their choosing — most dangerously, an executable script — somewhere the application will later serve or execute it, which typically leads directly to remote code execution on the server. Even where execution isn't achieved, unrestricted uploads enable stored XSS (an uploaded HTML or SVG file rendered inline), storage exhaustion, and malware distribution through a trusted domain.
The most severe outcomes come from bypassing extension- or MIME-type-based validation, since both are attacker-controlled and easy to spoof; the actual file content is what matters, and a validation check that only inspects the declared extension or Content-Type header is trivially bypassed.
Web shells planted through an unrestricted upload are a common initial-access technique in real breaches specifically because they give the attacker an ongoing, interactive foothold rather than a one-time exploit.
Prevention & Remediation
Prevention and Secure Coding
Preventing Unrestricted File Upload takes a defense-in-depth approach — no single control below is sufficient alone, but together they close off both the primary path and the most common bypasses.
Validate by content, not extension or declared type. Check the actual file content (magic bytes, and where relevant, a full parse of the file structure) rather than trusting the client-supplied filename extension or Content-Type header, both of which are trivially attacker-controlled.
Store uploads outside the webroot or in non-executable storage. Uploaded files should live somewhere the web server won't execute as a script even if a malicious file gets through validation — object storage without execute permissions, or a directory the web server config explicitly won't interpret as code.
Regenerate filenames server-side. Never reuse a client-supplied filename directly; generate a new one server-side to eliminate path-traversal and extension-confusion tricks embedded in the original name.
Restrict by allowlist, not denylist. Accept only explicitly allowed file types for the feature in question rather than trying to block a list of dangerous extensions, which is easy to bypass with alternate extensions the denylist didn't anticipate.
Scan uploads where the risk profile warrants it. For applications handling uploads from untrusted or semi-trusted users at scale, malware scanning adds a meaningful additional layer.
Source reports
A sample of the disclosed HackerOne reports this playbook was synthesized from.