This repository generates a security-testing corpus and runs payloads against deliberately vulnerable Docker services. Use it only on systems you own or are explicitly authorized to test.
The generated manifest contains 186 files across 18 categories and maps them to all 33 available testbed stacks. That is corpus inventory, not a claim that every payload fired in the latest environment.
Entries are deliberately separated into three kinds:
exploit: validation requires a concrete security effect such as computed output, sensitive file content, a correlated callback, a baseline-adjusted delay, an out-of-root write, or an executable response context.diagnostic: malformed or stress input that proves a parser was exercised, but does not claim a crash or exploit.fixture: useful format or platform-specific input that the included environment cannot fully prove, such as Windows filename behavior or desktop active content.
Every manifest entry also declares testbed_fidelity. Most included services are purpose-built vulnerable sinks. A passing result proves the stated effect in that sink; it does not establish that an unrelated product is vulnerable.
The checked-in validation results are intentionally marked not-run after corpus changes. Run the full testbed to produce evidence for your machine and image set.
Docker ports bind to 127.0.0.1 by default through a minimal TCP relay. Vulnerable processors remain on an internal network; only the non-parsing relay joins a no-masquerade ingress network. Containers also use resource ceilings, PID limits, and no-new-privileges. Link-local metadata requests are routed to the included fake IMDS service.
# Deterministically regenerate files and manifest metadata.
./tools/uploadctl generate
# Prepare callbacks for the included catcher.
./tools/uploadctl prepare oob-catcher
# Start the callback catcher and only the stacks you need.
./testbed/testbed oob-up
./testbed/testbed up upload-php-apache
./testbed/testbed up filename-handler
# Validate a narrow category, or omit --category for all running stacks.
./tools/uploadctl validate --category filenamesfiles/sources/ contains canonical payloads with {domain} placeholders. files/ready/ is an atomic prepared copy with metadata tying it and each prepared artifact hash to the current manifest. Validation ignores a stale or modified prepared tree and skips callback-dependent entries until prepare succeeds again.
External callback domains require outbound network access. Egress is off by default; enable it explicitly only in an isolated lab:
TESTBED_ALLOW_EGRESS=1 ./testbed/testbed up <stack>
./tools/uploadctl prepare your-authorized-callback.exampleExternal listeners are for manual correlation. Automated uploadctl validate
OOB assertions currently require the included catcher API on local port 9990.
The catcher payload listeners remain internal to the lab network; only its
correlation API is published to loopback.
The manager will not silently reuse an externally connected file-uploads-testbed-net when safe mode is requested.
For a complete local run, start the catcher and every stack before validating. Building all legacy images requires substantial Docker disk space and memory.
./testbed/testbed oob-up
./testbed/testbed up all
./tools/uploadctl validate
./testbed/testbed down all
./testbed/testbed oob-down| Pillar | Required evidence |
|---|---|
math |
Standalone 1337 returned after evaluating 7*191; artifacts containing literal 1337 are rejected for this pillar. |
file-read |
Actual sensitive content such as a valid /etc/passwd record or fake IMDS credentials. |
oob |
A callback whose path matches the current manifest entry, after the callback log was cleared. |
timing |
A 5-second-class delay above a just-measured endpoint baseline. |
path-write |
A resolved file path outside the service-declared upload root. |
reflected |
Evaluated/leaked server data, or active content served with an executable MIME type and without attachment disposition. |
error |
Explicit crash/resource exhaustion, a signal exit code, measured memory delta, or extreme decompression expansion—not an ordinary parser error. |
diagnostic |
The target accepted and exercised the fixture; this is reported separately from exploits. |
injection / prototype-pollution / redirect |
Effect-specific server evidence rather than echoed attacker text. |
Results are reported both per file and per file-target pair. A file succeeding on one target no longer hides failures on another. Runs with unavailable stacks or unprepared callback files have status: incomplete and preserve the skip reason.
| Category | Files | Main surfaces |
|---|---|---|
| shells | 30 | PHP, JSP/WAR, Python, CGI, SSI execution and blind variants |
| images | 21 | ExifTool, ImageMagick/Ghostscript, Pillow, SVG/XXE/XSS |
| xml-serialization | 21 | XXE, XSLT, unsafe YAML, pickle, prototype pollution |
| stored-xss | 13 | HTML active content plus negative MIME-control fixtures |
| documents | 12 | PDF actions/XXE, DOCX/XLSX/PPTX relationships and fixtures |
| data | 12 | CSV evaluation, LaTeX, SQLite schema injection and stress |
| archives | 10 | ZIP/TAR traversal, symlinks, nested archives, WAR deployment |
| templates-ext | 10 | EJS and ERB rendering sinks |
| deserialization | 8 | PHP, Node, and Ruby unsafe object loading |
| wkhtmltopdf | 8 | HTTP and HTTPS SSRF variants |
| media | 7 | FFmpeg HLS/concat SSRF and malformed-format diagnostics |
| server-config | 7 | .htaccess, .user.ini, policy and dotfile exposure |
| polyglots | 6 | PHP/image execution and multi-format fixtures |
| cicd | 6 | Makefile and shell build-runner execution |
| templates | 5 | Jinja2 template injection |
| filenames | 5 | traversal, absolute paths, shell metacharacters, NTFS/Unicode fixtures |
| fonts | 3 | TrueType/OpenType/WOFF parser diagnostics |
| cloud | 2 | DOCX/PDF requests to a locally routed fake IMDS |
Use HOWTOUSE.md to select payloads by processing pipeline and COVERAGE.md for the explicit coverage and gap matrix.
- Generator output is staged and committed atomically; removed generator files do not linger.
- Manifest size, magic bytes, SHA-256, kind, targets, pillars, and OOB paths are derived or checked during generation.
- Base images are pinned by digest. Direct Python and Node dependencies are version-pinned; Node lockfiles are used with
npm ci. Downloaded ExifTool and Tomcat JAR artifacts are checksum-verified. - Loopback access crosses a byte-only relay, so Docker Desktop remains usable without attaching vulnerable processors to an egress-capable network.
- CI regenerates the corpus, prepares callbacks, runs invariant/unit tests, checks every Compose file, and fails on nondeterministic output.
Run local checks with:
./tools/uploadctl generate
./tools/uploadctl prepare oob-catcher
python3 -m pip install -r requirements-test.txt
python3 -m unittest discover -v
ruff check tools testbed tests
python3 -m compileall -q tools testbed tests
python3 -m py_compile tools/uploadctl testbed/shared/port-relay/relay.py
for script in testbed/testbed testbed/shared/oob-catcher/entrypoint.sh; do
bash -n "$script"
done
for file in testbed/docker-compose.oob.yml testbed/stacks/*/docker-compose.yml; do
docker compose -f "$file" config -q
doneWhen adding a payload, set an honest kind, choose evidence that cannot be satisfied by the payload's own bytes, and add or update a test. Do not classify parser rejection, structural extraction, or a testbed label as successful exploitation.
This project is a lab corpus, not a scanner and not a claim of universal product coverage. It does not currently include a live Windows/IIS runtime, a real S3-compatible object store, antivirus/CDR workflow bypasses, HTTP request-smuggling/parser differential tests, or full browser JavaScript execution. Desktop-document and platform-specific cases are labeled fixtures where the included services can only inspect structure. See COVERAGE.md before drawing coverage conclusions.
Maintained by gromhacks, with techniques drawn from the security research community and the primary research referenced by individual payload metadata.
License: MIT; see LICENSE.