6 scenarios
for systematic file upload testing: extension bypass (double extension, null byte), MIME type bypass (Content-Type header manipulation), content validation bypass (polyglot files), path traversal via filename, stored XSS via SVG and HTML upload, and server-side processing vulnerabilities (ImageMagick, LibreOffice)
Content-Type
header is the most commonly misused validation -- trusting the Content-Type header sent by the client for upload validation is equivalent to trusting the attacker to self-report the file type, and produces no security benefit
magic bytes
file signature validation (checking the first bytes of the file against known signatures) is stronger than extension or MIME type checking but is still bypassable via polyglot files that have valid magic bytes for one format but execute as another
separate origin
for serving uploaded files is the highest-impact single control: serving user-uploaded files from a separate domain (uploads.example.com or a CDN) prevents stored XSS from executing in the main application context and prevents web shells from executing in the application server context

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

File upload functionality is present in nearly every modern web application -- profile photos, document management, content publishing, ticket attachments, and data import features all require accepting files from users. The attack surface is correspondingly wide, and the impact when upload validation fails ranges from persistent cross-site scripting to complete server compromise.

The challenge with file upload security is that naive validation approaches are all individually bypassable. Checking the file extension is bypassable via double extensions and null bytes. Checking the Content-Type header is bypassable because the client sends it. Checking the magic bytes signature is bypassable via polyglot files. Even checking the file content with a purpose-built parser is bypassable if the parser has its own vulnerabilities (ImageMagick, LibreOffice, and PDF parsers all have a history of exploitable vulnerabilities triggered by malformed input).

Secure file upload requires validation at multiple layers combined with an architecture that separates uploaded file storage from the application execution environment. The validation layers catch the majority of malicious files. The architecture limits the impact of the files that bypass validation.

This guide covers the six test scenarios for systematic file upload testing, the bypass techniques that defeat common validation approaches, the server-side validation requirements that actually work, and the upload architecture that provides defense in depth when individual validation controls are bypassed.

Understanding the File Upload Attack Surface

File upload vulnerabilities cluster around three attack goals: executing code on the server, persisting malicious content that affects other users, and writing files to unintended locations.

Remote code execution via web shell. An attacker uploads a file containing server-side executable code (PHP, JSP, ASP, ASPX) and then accesses the uploaded file via HTTP to trigger execution. This requires: the application accepts a file with an executable extension, the file is stored in a location that the web server serves, and the web server is configured to execute that file type. When all three conditions are met, the attacker has a web shell -- remote command execution on the server.

Stored XSS via content upload. An attacker uploads a file containing HTML or JavaScript that executes when other users view the uploaded content. SVG files are particularly effective for this because SVG is an XML format that supports inline JavaScript and is rendered natively by browsers. An application that accepts SVG files and serves them with a content type of image/svg+xml (rather than application/octet-stream) executes any embedded script when a user's browser renders the SVG.

Path traversal via filename. An attacker submits a filename containing path traversal sequences (../../../etc/cron.d/malicious) in the upload request, causing the application to write the file to a location outside the intended upload directory. If the application does not sanitize the filename before using it to determine the storage path, a carefully crafted filename can write to configuration directories, cron directories, or other sensitive filesystem locations.

Server-side processing vulnerabilities. Many applications process uploaded files server-side: thumbnail generation, document conversion, virus scanning, metadata extraction. These processing pipelines use libraries (ImageMagick, LibreOffice, Ghostscript, pdftotext) that have their own vulnerability histories. A file crafted to exploit a specific processing library vulnerability triggers code execution when the library processes the file -- not when the file is served to a browser.

Six Test Scenarios for Systematic Assessment

Systematic file upload testing covers six distinct scenarios. Test all six for every upload endpoint found in the application.

Scenario 1: Extension bypass. Submit a file with an executable extension (.php, .jsp, .asp, .aspx, .cgi, .pl) that should be rejected. If rejected, try:

  • Double extension: shell.php.jpg -- some servers execute based on the first extension
  • Null byte: shell.php�.jpg -- some servers truncate at the null byte and treat the file as PHP
  • Capitalization: shell.PHP, shell.Php -- if validation is case-sensitive but execution is not
  • Alternative extensions: shell.phtml, shell.php5, shell.phar -- alternative PHP-executable extensions
  • Backup extensions: shell.php.bak, shell.php~ -- some configurations execute backup extensions

Scenario 2: MIME type bypass. Most upload forms include a Content-Type header in the request. Intercept the upload request in Burp Suite and change the Content-Type header from application/x-php to image/jpeg. If the application accepts the file based on the spoofed Content-Type, the validation is client-supplied and bypassable.

Scenario 3: Content validation bypass (polyglot files). A polyglot file is simultaneously valid for two file formats. For example, a file that has valid JPEG magic bytes at the start (FFD8FF) but contains PHP code after the image header passes signature-based validation but may execute as PHP if the extension or server configuration allows it. Tools like exiftool can embed payloads in image metadata. Submit files with valid image signatures containing executable payloads.

Scenario 4: Path traversal via filename. Submit a multipart upload request with a filename containing path traversal: ../../malicious.php, ../../../etc/cron.d/backdoor. Use URL encoding (%2F, %2E) and double encoding. Observe whether the application sanitizes the filename before using it for storage.

Scenario 5: Stored XSS via SVG and HTML. Upload an SVG file containing embedded script:

<svg xmlns="http://www.w3.org/2000/svg">
  <script>alert(document.cookie)</script>
</svg>

If the application serves the SVG with content type image/svg+xml and the file is accessible via a URL in the same origin as the application, the script executes when a user views the SVG. Also test uploading HTML files with the same payload.

Scenario 6: Server-side processing. If the application generates thumbnails or processes uploaded images, test with malformed image files that trigger processing library vulnerabilities. Use publicly known POC files for ImageMagick (ImageTragick), LibreOffice macro execution, and PDF processing vulnerabilities appropriate to the versions in use. Also test with ZIP files containing path traversal (ZIP slip) if the application extracts archives.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Content-Type and Extension Bypass Techniques

Understanding the bypass techniques helps both testers (to confirm whether controls work) and developers (to understand why their chosen control is insufficient).

Why extension validation is insufficient alone. Extension validation checks the filename suffix provided by the client. Bypass techniques include null byte injection (file.php�.jpg) which truncates the filename at the null byte in some server configurations, double extensions (file.jpg.php) where some servers execute the leftmost executable extension, and alternative extensions (file.phtml, file.php5) that the server executes but the validation denylist does not include.

Why Content-Type validation is insufficient alone. The Content-Type header in a multipart upload request is sent by the client. The attacker controls it. Changing Content-Type: application/x-php to Content-Type: image/jpeg takes one Burp Suite interception. Any validation logic that trusts the client-supplied Content-Type provides no security.

Why magic bytes validation is insufficient alone. The magic bytes are the first few bytes of a file that identify its format (JPEG files start with FFD8FF, PNG files start with 89504E47). Polyglot files satisfy magic byte checks while also containing executable payloads. A JPEG file with valid magic bytes followed by PHP code between image data sections passes signature validation and may execute as PHP if served with a .php extension or if the server is configured to execute PHP in mixed-content files.

The combination that is harder to bypass. Server-side validation that:

  1. Allowlists only permitted extensions (not denylist)
  2. Ignores the client-supplied Content-Type entirely
  3. Validates file content using a format-specific parser (not just magic bytes)
  4. Strips or replaces the filename with a random server-generated name

Removing the filename prevents path traversal and extension-based execution. Using a format-specific parser (PIL/Pillow for images, python-docx for Word documents) confirms the file is actually the format it claims to be, not just that it has the right magic bytes. Allowlisting extensions prevents newly discovered executable extensions from bypassing a denylist that has not been updated.

Server-Side Validation Requirements

Server-side validation that works requires validation at the content layer, not just the metadata layer.

Step 1: Replace the filename immediately. On receipt, generate a random server-assigned filename (UUID or cryptographically random string) and use that for storage. Never use the client-provided filename for the storage path. Store the original filename separately in a database if display is required. This eliminates path traversal via filename and prevents extension-based execution.

Step 2: Validate content with a format-specific parser. For image uploads, run the uploaded file through a format-specific library:

from PIL import Image
import io

def validate_image(file_bytes: bytes) -> bool:
    try:
        img = Image.open(io.BytesIO(file_bytes))
        img.verify()  # Verify it is a valid image
        return True
    except Exception:
        return False

The verify() call attempts to decode the image data using the Pillow library's parser. A file that has valid JPEG magic bytes but is not a parseable JPEG image fails this check. A polyglot file that passes basic magic byte checks may still fail format-specific parsing.

Step 3: Re-encode images after validation. Even after validation, image processing libraries can have vulnerabilities that are triggered during rendering. Re-encoding the image through the library neutralizes embedded payloads that survive format validation:

from PIL import Image
import io

def sanitize_image(file_bytes: bytes, format: str = "JPEG") -> bytes:
    img = Image.open(io.BytesIO(file_bytes))
    output = io.BytesIO()
    img.save(output, format=format)
    return output.getvalue()

Re-encoding strips metadata (including EXIF data that can contain payloads) and produces a clean image file regardless of what was in the original.

Step 4: Size and dimension limits. Enforce maximum file size and, for images, maximum pixel dimensions. ZIP bombs and gigapixel images that consume server memory during processing are denial-of-service vectors.

Step 5: Antivirus scanning for document uploads. For application types that accept documents (PDF, Word, Excel, ZIP), integrate antivirus scanning before storage. ClamAV is open-source and suitable for this use case. Antivirus is not a complete control (it misses novel malware) but catches known malicious document templates.

Secure Upload Architecture: Storage, Processing, and Delivery

The upload architecture provides defense in depth that limits impact when individual validation controls are bypassed.

Separate storage from execution. Uploaded files should never be stored in a location that the web server serves as executable code. Store uploaded files in a blob storage service (AWS S3, Azure Blob Storage, GCP Cloud Storage) separate from the application server's filesystem. Even if a PHP web shell is uploaded successfully, it cannot be executed because blob storage does not process PHP scripts.

Serve from a separate origin. Serve uploaded files from a different domain than the application: uploads.example.com or a CDN domain. This prevents stored XSS in uploaded HTML or SVG files from executing in the main application context, because JavaScript running in uploads.example.com cannot access cookies or local storage for app.example.com (same-origin policy). Set the separate origin's Content-Security-Policy to restrict script execution.

Set correct Content-Type for delivery. When delivering uploaded files, set the Content-Type header based on the server-side validation result, not the client-supplied value. SVG files that must be served for display should use Content-Type: image/svg+xml; charset=utf-8 with a Content-Security-Policy that blocks script. HTML files uploaded by users should be served with Content-Type: text/plain or application/octet-stream to prevent browser rendering.

Add Content-Disposition: attachment. For all user-uploaded files that do not need to be rendered inline by the browser, set Content-Disposition: attachment; filename="[safe-filename]" to force download rather than rendering. This prevents the browser from attempting to execute or render any content in the file.

Process in a sandboxed environment. If document processing (thumbnail generation, format conversion) is required, run processing in an isolated container or serverless function with no network access, limited filesystem access, and a strict execution timeout. Vulnerabilities in processing libraries cannot be pivoted into broader system access if the processing environment is isolated.

Regression Testing and Ongoing Monitoring

File upload security is not a one-time fix. New upload endpoints are added as features are developed, processing libraries are updated with new vulnerability classes, and bypass techniques evolve.

Add upload security to code review checklists. Every new file upload feature should require code review that confirms: filename is replaced server-side, content is validated with a format-specific parser, files are stored in blob storage rather than the web server filesystem, and delivery uses the separate origin pattern. Make these requirements explicit in the code review template rather than relying on reviewers to remember them.

Automated scanning in CI/CD. DAST tools (Burp Suite Enterprise, OWASP ZAP, Nuclei templates) can test file upload endpoints automatically as part of the deployment pipeline. Configure upload-specific test cases that attempt extension bypass, MIME type bypass, and path traversal as automated regression tests.

Dependency monitoring for processing libraries. Track CVEs for image and document processing libraries used in the upload pipeline. ImageMagick, LibreOffice, Ghostscript, and PDF parsing libraries have recurring vulnerability disclosures. Subscribe to security advisories for each library and establish a patching SLA for critical upload pipeline vulnerabilities (48 hours for critical, 7 days for high).

Log and alert on upload anomalies. Log every upload event with: user identifier, file size, detected MIME type, validation result, and storage path. Alert on: validation failures above a threshold per user (potential attacker probing), files exceeding the expected size range for the upload feature, uploads from IP addresses with no prior authentication history, and any upload that triggers an error in the processing pipeline.

The bottom line

File upload vulnerabilities are reliably exploited because naive validation approaches (extension checking, Content-Type header checking, magic byte checking) are each individually bypassable via extension bypass, header spoofing, and polyglot files respectively. Secure file upload requires four controls working together: replacing the client-provided filename with a server-generated random name on receipt, validating file content with a format-specific parser (not just magic bytes), storing files in blob storage separate from the web server's execution environment, and serving uploaded files from a separate origin to prevent stored XSS from executing in the application context. Regression testing via automated DAST scans for upload endpoints and dependency monitoring for processing libraries maintains the control set as new upload features are added and new bypass techniques are discovered.

Frequently asked questions

What is the most dangerous type of file upload vulnerability?

Remote code execution via web shell upload is typically the highest severity: the attacker uploads a PHP, JSP, or ASPX file and accesses it via HTTP to execute arbitrary commands on the server. This requires the file to be stored in a web-accessible location and the server to execute the file type. The next most impactful are: server-side processing vulnerabilities (ImageMagick exploits trigger RCE when the server processes the upload, without requiring the file to be served), and path traversal via filename (can write files to arbitrary locations including cron directories, SSH authorized_keys, and configuration files).

Why is the Content-Type header not a reliable validation method?

The Content-Type header in a multipart file upload request is sent by the client and can be set to any value by the attacker. Intercepting the upload request in any HTTP proxy and changing Content-Type from application/x-php to image/jpeg takes two seconds. Applications that validate only the Content-Type header are validating attacker-controlled data. Server-side validation must examine the actual file content using a format-specific parser rather than trusting the client to accurately report the file type.

What is a polyglot file and how does it bypass magic byte checking?

A polyglot file is a single file that is simultaneously valid for two different file formats. For example, a file can have valid JPEG magic bytes (FFD8FF) at the beginning -- passing signature validation -- while also containing PHP code after the image header. If the server validates the magic bytes, considers the file a JPEG, but stores it with a .php extension or in a PHP-executable context, the PHP code in the file body can be executed. Polyglot files defeat magic byte validation alone; format-specific parser validation (attempting to decode the file as an image through the actual image library) is stronger because a valid polyglot must also satisfy the parser's structural requirements.

How does storing uploads in cloud blob storage prevent web shell execution?

Cloud blob storage services (S3, Azure Blob, GCP Cloud Storage) store files as objects and deliver them via HTTP. They do not process PHP, JSP, or any other server-side code -- they serve files as static content. A PHP web shell uploaded to S3 and accessed via an S3 URL returns the PHP source code as plain text, not the result of executing the code. Because the blob storage has no PHP interpreter, the web shell cannot execute regardless of the file content. This architectural separation eliminates the execution vector even if all other validation controls are bypassed.

What is the ZIP slip vulnerability and how does it relate to file upload?

ZIP slip is a path traversal vulnerability that occurs when an application extracts a ZIP archive without validating the filenames inside. A malicious ZIP archive can contain entries with path traversal sequences in their names (../../etc/cron.d/backdoor). When the application extracts the archive, it writes files to paths outside the intended extraction directory. Applications that accept ZIP uploads and automatically extract them are vulnerable if they do not sanitize filenames before extraction. The fix: validate that all filenames in the archive, after normalization, resolve to paths within the intended extraction directory.

How should uploaded SVG files be handled securely?

SVG files are XML that supports inline JavaScript. If an SVG is served with Content-Type: image/svg+xml from the same origin as the application, any embedded script executes in the application's origin context. Three options in increasing security: (1) Reject SVG uploads entirely if they are not required. (2) Sanitize SVG content by parsing and removing script elements, event handlers, and external references -- use a purpose-built SVG sanitizer library rather than regex. (3) Serve SVG from a separate origin with a Content-Security-Policy that disallows scripts. Option 1 is simplest; option 3 allows SVG while mitigating script execution.

Sources & references

  1. OWASP: Unrestricted File Upload
  2. PortSwigger: File Upload Vulnerabilities
  3. OWASP: File Upload Cheat Sheet
  4. HackerOne: File Upload Vulnerability Reports

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.