Skip to content

File Upload Security Testing (DAST)

Escape discovers every file upload endpoint your application exposes and probes each one against the OWASP Top 10 categories that matter for upload surfaces (A01:2021 access control, A03:2021 injection, A04:2021 insecure design, A05:2021 misconfiguration, A10:2021 SSRF). The module runs automatically inside your existing DAST scan with no separate profile or infrastructure.

The module hooks into the DAST scan types you already run:

Surface Scanner kind What we look at
WebApp DAST Web application Live multipart traffic captured by the agentic crawler + JS
REST API DAST REST API Recorded API exchanges with Content-Disposition: filename=
GraphQL API DAST GraphQL API Same recorded exchanges, GraphQL multipart spec aware

Coverage

Seven active checks plus an informational ISSUE_FILE_UPLOAD_DETECTED per detected endpoint. Every check confirms on a deterministic signal that customers can audit from the event log.

Check Issue Severity Compliance How we confirm it
Unrestricted Upload ISSUE_FILE_UPLOAD_UNRESTRICTED High CWE-434 + OWASP A04:2021 Canary file uploaded under a dangerous extension; confirms only on 2xx upload AND canary readable through the inferred URL.
Stored XSS via Upload ISSUE_FILE_UPLOAD_STORED_XSS High CWE-79 + OWASP A03:2021 SVG / HTML / PDF / polyglot canaries; confirms only when retrieval Content-Type is image/svg+xml or text/html AND canary is reflected verbatim.
RCE Polyglot ISSUE_FILE_UPLOAD_RCE High CWE-94 + CWE-434 + A03 Idempotent GIF+PHP / .htaccess / web.config / .user.ini / ZIP polyglot; confirms via canary token echo or OOB callback at ssrf.tools.escape.tech.
Path Traversal via Upload ISSUE_FILE_UPLOAD_PATH_TRAVERSAL High CWE-22 + OWASP A01:2021 Canary-suffixed traversal filenames (../escape_canary_<uuid>.txt, null-byte, Windows backslash, RTLO); confirms only on canary read-back.
XXE via Upload ISSUE_FILE_UPLOAD_XXE High CWE-611 + OWASP A05:2021 SVG / OOXML / xlsx-style XML with OOB-only external entities; confirms via tagged callback at the OOB collector.
SSRF via Upload ISSUE_FILE_UPLOAD_SSRF High CWE-918 + OWASP A10:2021 SVG xlink:href / <image href> / XML SYSTEM references to the OOB collector; confirms via tagged callback with fu- prefix.
Zip Slip ISSUE_FILE_UPLOAD_ZIP_SLIP High CWE-22 Archive entries with canary-suffixed traversal paths; ZIP bomb capped at 5x ratio and 1 MiB on the wire; confirms only on canary read-back.

How tests are executed

flowchart LR
    HAR[Live traffic / recorded exchanges / JS source] --> DET[Endpoint discovery]
    DET --> Reduce[Endpoint consolidation]
    Reduce --> Profile[Endpoint profiling]
    Profile --> Checks[7 active checks in parallel]
    Checks --> Issues[(SecurityIssues)]

Detection methodology

Escape's detection layer reads signals already collected during your DAST scan and emits one FileUploadEndpointCandidate per upload-shaped exchange. The reducer dedupes them by (transport, url, GraphQL operation, method), profiles each replayable endpoint, and emits ISSUE_FILE_UPLOAD_DETECTED so you can see the surface even when no active check confirms.

Network-layer signals (any one is enough to short-list a candidate):

  • Content-Type: multipart/form-data; boundary=... with at least one part header Content-Disposition: form-data; name=...; filename="...".
  • GraphQL multipart spec: operations + map + numbered file parts (the standard apollo-upload-client shape).
  • Presigned cloud upload: POST or PUT against a recognised S3 / GCS / R2 / Azure host carrying X-Amz-Signature=, X-Goog-Signature=, or sig=.
  • Two-step presigned negotiation: a JSON response that contains uploadUrl, presignedUrl, signedUrl, upload_url, or url.
  • Resumable tus protocol: Tus-Resumable / Upload-Length headers.
  • AWS S3 multipart: POST ?uploads, PUT ?partNumber=.

Path heuristics corroborate the above: /upload, /files, /attachment, /media, /import, /avatar, /image, /photo, /document, /asset, /picture, /profile/picture, /api/Complaints. A path keyword on its own is never enough; a multipart, presigned, or two-step signal must also be present.

JavaScript-source signals catch endpoints the crawler has not exercised yet: FormData, <input type="file">, imports of apollo-upload-client, react-dropzone, tus-js-client, @uppy/core, filepond, and fineuploader. JS-only endpoints produce ISSUE_FILE_UPLOAD_DETECTED from deterministic evidence and are not actively probed.

When endpoint signals are ambiguous, Escape uses an AI-assisted classifier to decide whether the surface is likely to accept uploads. Confirmed upload endpoints are summarized in the inventory so you can review their role even when no vulnerability is found.

Customer safety

The default catalogues are designed to surface the vulnerability without polluting your environment:

  • Every payload filename carries a per-scan escape_canary_<uuid> suffix, so resolved paths cannot collide with real customer files.
  • Path traversal and zip slip variants assert on canary read-back only. We never write to /etc, ~/.ssh, cron directories, or any real-looking filename.
  • ZIP bomb canaries are capped at 5x compression ratio and 1 MiB on the wire.
  • XXE and SSRF default catalogues are OOB-only. Local file exfiltration (file:///etc/passwd, ...) is intentionally out of scope in the first release.
  • RCE polyglot canaries are idempotent echoes of the canary token. No shells, no filesystem writes, no privileged operations.
  • Uploaded artefacts remain in your storage until your normal retention sweeps. Cross-link to 35-out-of-band-testing.md for OOB collector behaviour shared with the LLM module's SSRF check.

Evidence

When a file upload issue is raised, the finding explains which endpoint was tested, which check confirmed the behavior, and the non-destructive canary evidence used for confirmation.

Opt-in

The module runs automatically as part of your DAST scan and requires no configuration. All seven active checks are enabled by default.