A waiter takes the order, walks away with the plate, and the table never turns over
This vulnerability is in the @fastify/multipart plugin for Fastify, versions 5.3.0 through 10.1.6. When a request body ends inside a file part without a closing boundary, the plugin destroys the in-progress file stream without an error. If the application handler iterates request parts and performs asynchronous work before consuming the file, it attaches to a stream that never emits error or end; the connection hangs, and the server never responds. The issue is fixed in 10.1.7, where the parser error reaches the file stream and the delayed consumer settles with an error instead.
Vendor severity of MEDIUM (5.9) is too high for most deployments. The chain only works with an uncommon handler pattern—deferred file consumption—not the documented fast path of await request.file(). An unauthenticated client still needs many malformed sockets to exhaust resources, and the impact is availability only. For teams that do not have that specific deferred-consumer pattern, this is a LOW-priority package update. For the small subset that does, it should be treated as a HIGH-availability risk, not a fleet-level security compromise.
4 steps from start to impact.
Find a Fastify route with delayed file consumption
@fastify/multipart where the handler loops over request.parts() or request.files() and awaits unrelated async work before consuming the file stream. The common await request.file() method is not affected because the stream is consumed immediately. This is the precondition that makes the malformed body hang a connection.- Fastify app runs
@fastify/multipart5.3.0–10.1.6 - Route accepts multipart file uploads
- Handler performs async work before consuming
part.fileor stream
- Most Fastify examples consume the file immediately
- Deferred consumption is application-specific; the attacker must identify such a route
- The vulnerable route may require authentication or be internal
Send a truncated multipart body with complete Content-Length
Content-Length, the client does not abort, and the upload stays within any configured file size limit. The server sees a complete body but cannot finalize the part.- Attacker can reach the route via HTTP
- Request body ends inside a file part
- No file size limit is exceeded
- Need to construct exact MIME framing; standard HTTP clients can do it with raw socket scripts
- Reverse proxies may normalize or time out malformed uploads
Trigger parser error that never reaches the file stream
error or end; neither arrives. The request promise never settles, and the connection remains open.- Vulnerable version 5.3.0 through 10.1.6
- Delayed consumer already attached to the file stream
- This is a narrow lifecycle bug; if the stream has already been consumed or destroyed by the handler, the hang does not occur
Accumulate pending connections to exhaust server resources
- Route accepts high enough concurrent connections
- Server lacks per-IP connection limits or aggressive timeouts
- Attacker can generate many sockets/IPs
- Default OS and reverse proxy connection limits still cap total sockets
- Autoscaling or process supervisors may restart/take over
- The attack must be sustained, because a single malformed request is just one idle connection
@fastify/multipart 10.1.7 or later — This is the only complete fix. The patched release propagates the parser error to the file stream, so a delayed consumer settles with an error instead of hanging. For LOW severity, noisgate mitigation SLA is not applicable; schedule the upgrade within the 365-day remediation window, but if you have the delayed-consumer pattern, treat as high-priority and patch immediately.- File size limits /
bodyLimit— the malformed upload does not exceed the size limit; limits will not reject it. - Authentication alone — requiring auth only reduces the attacker population; any authenticated user could still hang the route if it uses the deferred pattern.
- WAF multipart filtering — many WAFs normalize or block clearly malformed uploads, but a body ending in a file part can look like ordinary multipart traffic, so WAF coverage is inconsistent.
The supporting signals.
| In-the-wild status | No KEV listing; no known active campaigns reported. |
|---|---|
| Proof-of-concept availability | No public PoC repo named after this CVE found; pocindex.io query returns no nuclei/ExploitDB match at reassessment time. |
| EPSS score / percentile | EPSS estimated 0.00043, 0.1st percentile (low immediate exploitability). |
| KEV status | Not in CISA Known Exploited Vulnerabilities catalog. |
| CVSS interpretation | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H — remote unauthenticated, high attack complexity due to rare handler pattern, availability-only. |
| Affected versions | @fastify/multipart 5.3.0 through 10.1.6 |
| Fixed versions | @fastify/multipart 10.1.7; no distro backports because npm package |
| Scanning/exposure | Not surface-scannable with Shodan/GreyNoise alone; exposure is proportional to Fastify routes with deferred file consumers. |
| Disclosure date | Not provided in authoritative record; null in structured metadata. |
| Reporting researcher/org | Not identified in authoritative record. |
Sources.
Why this verdict
- Baseline: vendor scored 5.9 because remote unauthenticated DoS against a Fastify route is plausible.
- Prerequisite audit: exploitation requires a handler that uses the parts iterator and performs asynchronous work before consuming
part.file; the common examples (await request.file()) consume immediately and do not hang. - No KEV or active exploitation: the CISA catalog does not list this CVE, and no mass scanning/exploitation has been reported.
- Role multiplier: typical deployments are app-tier endpoints; even a Fastify app fronting an identity API only experiences availability loss, not domain/fleet/supply-chain compromise, so the high-value-role floor does not apply.
- Friction compounding: malformed MIME body must exactly end inside a file part; many reverse proxies and Fastify defaults will still have connection limits/timeouts, and a single hanging request does not crash the process by itself.
Why not higher?
No privilege escalation, data confidentiality/integrity impact, or lateral movement. The attack requires accumulating many deliberately malformed sockets against a specific deferred-consumer route; an autoscaling group or watchdog can replace a saturated instance, and the attack leaves no permanent foothold. The AC:H in the CVSS vector reflects the conditional handler pattern, which knocks the real-world frequency down substantially.
Why not lower?
It is not IGNORE because an unauthenticated remote client can script the malformed requests and, if the app uses the exact deferred-consumer pattern, the route will leave sockets open until limits are hit. The availability-only impact on a public endpoint still merits a LOW remediation item rather than backlog-only review.
Crowdsourced verification payload.
Run on the target host where the Fastify app is deployed, with read access to its package.json. Example: bash check_CVE-2026-117531.sh /srv/app/package.json. No root privileges required.
#!/usr/bin/env bash
set -u
PJ="${1:-package.json}"
if [ ! -f "$PJ" ]; then
echo "UNKNOWN: no package.json at $PJ"
exit 3
fi
if command -v node >/dev/null 2>&1; then
VER=$(node -e "
const fs = require('fs');
const p = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
const deps = {...p.dependencies, ...p.devDependencies};
console.log(deps['@fastify/multipart'] || '');
" "$PJ" 2>/dev/null || true)
else
VER=$(grep -E '"@fastify/multipart"' -A2 "$PJ" | sed -n 's/.*"@fastify/multipart"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
fi
VER="${VER##^}"; VER="${VER##~}"
if [ -z "$VER" ]; then
echo "UNKNOWN: @fastify/multipart not found in dependencies"
exit 3
fi
node -e "
function cmp(a,b){const x=a.split('.').map(Number), y=b.split('.').map(Number); for(let i=0;i<3;i++){if((x[i]||0)<(y[i]||0))return -1;if((x[i]||0)>(y[i]||0))return 1;} return 0;}
const v=process.argv[1];
if (cmp(v,'5.3.0') >= 0 && cmp(v,'10.1.7') < 0) {
console.log('VULNERABLE: declared @fastify/multipart version ' + v);
process.exit(1);
} else {
console.log('PATCHED: declared @fastify/multipart version ' + v);
process.exit(0);
}
" "$VER"