Like a faucet that nobody remembers to shut off — each aborted upload drips one file descriptor until the sink overflows
CVE-2026-77037 affects only multer 2.2.0 using the built-in diskStorage engine. When a multipart upload is aborted or truncated mid-stream, multer dutifully calls fs.unlink to remove the partial file — but never calls .close() on the write stream's underlying file descriptor. The deleted inode stays open, pinning disk blocks and consuming a slot in the process's FD table. An unauthenticated attacker who can reach any upload route can repeat this hundreds of times per second, eventually exhausting the ulimit -n ceiling (typically 1024–65536) and starving the Node.js process of the ability to open sockets, files, or pipes. The result is a denial-of-service of the affected Express/Koa/Fastify application. There is no confidentiality or integrity impact — no code execution, no data exfiltration, no privilege escalation.
The vendor's HIGH / 7.5 score is mechanically correct per the CVSS vector (unauthenticated, network, low complexity, high availability impact) but overstates real-world urgency. The affected version window is a single semver release (2.2.0 only), the prerequisite is that the app uses diskStorage on an externally reachable upload route, and the worst-case outcome is a process restart. In an ecosystem where multer sees 20M+ weekly npm downloads, the fraction actually running 2.2.0 with disk storage exposed externally is small. A MEDIUM rating better reflects the operational risk.
3 steps from start to impact.
Identify an upload endpoint
multipart/form-data and is backed by multer with diskStorage. This is typically a file-upload form, avatar endpoint, or document-ingest API. No authentication is required if the route is public, but many upload routes sit behind auth or CSRF tokens.- Target runs multer 2.2.0
- Route uses built-in
diskStorageengine - Route is network-reachable to attacker
- Many upload routes require authentication or a valid session
- Rate limiters, WAFs, or reverse-proxy request-size caps may throttle repeated POSTs
- Apps using
memoryStorageor a custom storage engine (S3, GCS) are unaffected
Send aborted multipart uploads in a loop
multipart/form-data POST with a file part, then resets the connection (RST or simply closes the socket) before the upload completes. Each aborted request triggers the bug path: multer deletes the temp file but leaves the write stream's FD open. A simple curl one-liner or Python script can automate this at high volume.- Ability to send and abort HTTP requests rapidly
- Reverse proxies (nginx, HAProxy) may buffer the upload and not propagate the abort to Node
- Connection rate limits or SYN cookies on the load balancer slow the attack
- Container orchestrators restart crashed pods automatically, limiting sustained impact
lsof -p <pid> | grep deleted) will show a growing count of deleted-but-open FDs. Node.js process metrics (open handles count) will trend upward.Exhaust file descriptors → DoS
ulimit -n ceiling, every subsequent open(), socket(), or accept() call fails with EMFILE. The Node.js event loop can no longer accept new connections or write logs. The application becomes fully unresponsive until the process is restarted (which closes all leaked FDs). In a single-process deployment without a process manager, this is a sustained outage; in a clustered or Kubernetes deployment the pod is restarted within seconds.- Sufficient aborted requests to exhaust the FD limit (typically 1024–65536 iterations)
- Kubernetes liveness probes or PM2/forever will restart the process, limiting downtime to seconds
- A high
ulimit -n(e.g., 65536) means the attacker needs tens of thousands of aborted uploads - Each leaked FD also holds disk blocks, but modern SSDs and large volumes make disk exhaustion unlikely before FD exhaustion
process_open_fds metric crossing threshold. Health-check failures triggering alerts.The supporting signals.
| In-the-Wild Exploitation | No evidence. Not listed in CISA KEV. No reports of active exploitation as of 2026-08-29. |
|---|---|
| Proof-of-Concept | Not publicly available. No named PoC repos found on GitHub. The attack is trivially reproducible with curl --max-time 0.1 -F [email protected] <url> in a loop, so weaponization is straightforward. |
| EPSS Score | Not yet scored — CVE was published 2026-08-28, EPSS lag is typical for newly disclosed CVEs. Expected to be low given DoS-only impact. |
| KEV Status | Not listed. DoS-only CVEs rarely enter KEV. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — Network-accessible, no auth, no user interaction, availability-only impact. Scope unchanged. |
| Affected Versions | multer 2.2.0 only (semver exact match). Versions < 2.2.0 and ≥ 2.3.0 are not affected. |
| Fixed Version | multer 2.3.0 — fix closes the destination write stream on abnormal source termination and defers unlink until after stream close. |
| Scanning / Exposure | multer has ~20M weekly npm downloads, but the affected version 2.2.0 is a single release. Actual exposure is a fraction of that install base. No Shodan/GreyNoise/Censys signatures applicable (this is an application-layer library, not a listening service). |
| Disclosure Timeline | Reserved 2026-08-20, published 2026-08-28. Assigned by OpenJS Foundation. |
| Reporter / Credits | Reported via OpenJS Foundation security process. Advisory GHSA-qfvm-cv95-jqjf. |
noisgate verdict.
The single most decisive factor is the extremely narrow affected version range: only multer 2.2.0 is vulnerable, making the reachable population a small fraction of multer's 20M-weekly-download install base. Combined with DoS-only impact (no code execution, no data access) and automatic recovery via process restart, the operational risk does not warrant a HIGH classification.
Why this verdict
- Single-version scope: Only multer 2.2.0 is affected — not a range, not 'all versions before X'. Any team that installed multer before 2.2.0 or has already moved to 2.3.0 is unaffected, drastically shrinking the exposed population.
- DoS-only, self-healing blast radius: The worst outcome is an unresponsive Node.js process. A
kill -9or pod restart clears all leaked FDs instantly. No data is exposed, no code is executed, no persistence is gained. In Kubernetes or PM2-managed deployments the outage window is seconds. - Friction from upload-route prerequisites: The attacker must reach a route that uses
diskStoragespecifically. Apps usingmemoryStorage, S3 storage engines, or routes behind authentication are not exploitable. Rate limiters and reverse-proxy buffering further narrow the practical attack surface. - Role multiplier: Multer is an application-tier middleware — it does not run on domain controllers, hypervisors, identity providers, or backup servers. Even a successful DoS affects a single application process, not fleet-wide infrastructure. The blast radius is *host* at most (single process, really), never *domain* or *fleet*. No high-value-role floor applies.
Why not higher?
There is no code execution, no data exfiltration, and no privilege escalation path. The impact ceiling is availability loss of a single Node.js process, recoverable by restart. The affected component (a file-upload middleware) does not occupy a high-value infrastructure role where DoS translates to cascading failures. Upgrading to HIGH would require either active exploitation evidence or a broader blast radius.
Why not lower?
The vulnerability is unauthenticated and network-reachable with zero user interaction — the CVSS access vector is legitimately easy. Weaponization is trivial (a shell loop with curl), so any exposed instance can be targeted by an unsophisticated attacker. While the version window is narrow, organizations that *are* on 2.2.0 face a real availability risk until they patch. Dropping to LOW would understate the ease of exploitation for affected instances.
What to do — in priority order.
- Upgrade multer to 2.3.0 immediately — This is a one-line
npm install [email protected]change. Given the MEDIUM verdict, target remediation within the noisgate 365-day remediation window, but the fix is trivial enough to deploy this sprint. - Set process-level file descriptor limits — Ensure
ulimit -nor the container'snofilerlimit is set high (e.g., 65536+). This does not fix the leak but raises the threshold an attacker must cross, buying time. Deploy as general hardening. - Rate-limit upload endpoints — Apply per-IP rate limiting on multipart upload routes at the reverse proxy or WAF layer (e.g., nginx
limit_reqat 10 req/s per IP). This slows FD exhaustion to an impractical pace. - Enable health-check-driven restarts — Ensure Kubernetes liveness probes, PM2
max_restarts, or systemdRestart=alwaysare configured for the Node.js process. A restart clears all leaked FDs and restores service in seconds. - Switch to memoryStorage or cloud storage engine — If the application does not require disk-backed temp files, switching to
multer.memoryStorage()or an S3/GCS storage engine sidesteps the vulnerable code path entirely.
- WAF signature blocking — there is no malicious payload to match; the attack uses a normal multipart POST that is simply aborted early. WAF content inspection cannot distinguish this from a user with a flaky connection.
- Network-level DDoS mitigation (e.g., Cloudflare, AWS Shield) — the attack is low-bandwidth (small partial uploads) and does not trigger volumetric thresholds. It looks like normal upload traffic at the network layer.
Crowdsourced verification payload.
Run this on any host where a Node.js application with multer is deployed, or from a CI pipeline that has access to the project's node_modules. Requires read access to node_modules/multer/package.json. No elevated privileges needed. Example: bash check_cve_2026_77037.sh /app where /app is the project root.
#!/usr/bin/env bash
# check_cve_2026_77037.sh — Detect CVE-2026-77037 (multer FD leak DoS)
# Usage: bash check_cve_2026_77037.sh <project_root>
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
PROJECT_ROOT="${1:-.}"
PKG="${PROJECT_ROOT}/node_modules/multer/package.json"
if [ ! -f "$PKG" ]; then
echo "UNKNOWN — multer not found in ${PROJECT_ROOT}/node_modules"
exit 2
fi
VERSION=$(grep '"version"' "$PKG" | head -1 | sed 's/.*"version": *"\([^"]*\)".*/\1/')
if [ -z "$VERSION" ]; then
echo "UNKNOWN — could not parse multer version from $PKG"
exit 2
fi
echo "Detected multer version: $VERSION"
if [ "$VERSION" = "2.2.0" ]; then
echo "VULNERABLE — multer $VERSION is affected by CVE-2026-77037 (FD leak DoS)"
echo "Remediation: npm install [email protected]"
exit 1
else
echo "PATCHED — multer $VERSION is not affected by CVE-2026-77037"
exit 0
fiIf you remember one thing.
npm install [email protected] — the fix is a clean semver-minor bump with no breaking changes. There is no noisgate mitigation SLA for MEDIUM — go straight to the 365-day noisgate remediation SLA. That said, this is a one-command fix; there is no reason to wait. If you cannot patch immediately, rate-limit your upload endpoints and ensure your process manager auto-restarts on health-check failure. If you are on any other multer version (< 2.2.0 or ≥ 2.3.0), you are not affected — document and move on.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.