Like a bouncer who drops dead the moment someone hands him a comically oversized ID card
CVE-2026-77078 is a denial-of-service flaw in multer, the dominant multipart/form-data parsing middleware for Express.js and Node.js. A remote, unauthenticated attacker can crash an entire Node.js process with a single HTTP request. The attack sends two crafted text field names in a multipart/form-data body: the first field uses a huge numeric array index (near V8's hard ceiling of 4,294,967,295 elements) to force a maximum-length sparse array allocation inside the append-field dependency, and the second field triggers a .push() past that boundary. This throws a synchronous RangeError: Invalid array length that bypasses multer's error handler and kills the process. All multer versions before 2.3.0 are affected — and multer pulls over 22 million weekly npm downloads, so the blast radius across the Node.js ecosystem is vast.
The vendor's HIGH / 7.5 rating is correctly calibrated. The CVSS vector (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) accurately reflects the zero-auth, zero-interaction, network-reachable crash. The only thing keeping this from CRITICAL is the impact ceiling: availability-only, no confidentiality or integrity exposure. In practice, production Node.js services behind PM2, cluster mode, or Kubernetes will auto-restart within seconds — but a sustained stream of these requests produces a sustained outage, and the PoC is public and trivially weaponizable. The vendor got this one right.
4 steps from start to impact.
Identify a multer-powered endpoint
multipart/form-data. No authentication is required; any publicly reachable route that triggers multer parsing is sufficient. Discovery is straightforward: send a benign multipart POST and observe the response. Common paths include /upload, /api/submit, or any endpoint documented to accept file attachments.- Target application uses multer < 2.3.0
- Endpoint is network-reachable
- Endpoint may sit behind an API gateway or CDN that strips or normalizes multipart fields, though this is uncommon
Craft the oversized array-index field
field[4294967294] — an index at the V8 array-length ceiling. When multer's append-field dependency processes this, it allocates a sparse array of maximum length. The allocation itself succeeds (sparse arrays are lazy in V8), but the internal length property is set to near 2^32 - 1. This step requires only knowledge of the CVE details, which are public.- Ability to send arbitrary multipart field names (standard HTTP)
- WAFs rarely inspect or limit numeric indices inside multipart field names
Send the overflow-triggering second field
field[] (empty brackets), which triggers append-field to call .push() on the already-at-ceiling-length array. V8 throws a synchronous RangeError: Invalid array length. Because multer does not wrap this code path in a try/catch, the exception propagates uncaught, bypassing Express error middleware and terminating the Node.js process immediately.- Both fields delivered in a single multipart request
- Process managers (PM2, cluster mode, Kubernetes) will restart the worker within seconds, but each crash still drops in-flight requests
RangeError: Invalid array length from the append-field module. APM tools (Datadog, New Relic) with crash tracking will surface the stack trace.Sustain the attack for continuous outage
curl is needed.- Network path to the endpoint remains open
- No rate-limiting or request-blocking in place
- IP-based rate limiting at the load balancer or CDN layer can throttle sustained attacks
- Geographic or IP-reputation filtering can reduce attack surface
appendField call in a try/catch and introduces limits.fieldArrayIndexLimit to reject oversized indices before allocation. Run npm update multer or pin "multer": ">=2.3.0" in package.json. As a HIGH-severity finding, the noisgate mitigation SLA requires action within 30 days, but given the trivial exploitability and public PoC, prioritize this in your next sprint.multipart/form-data requests where any field name contains a numeric index exceeding a reasonable threshold (e.g., field[999999] and above). This is a stopgap if you cannot patch immediately. ModSecurity example: SecRule ARGS_NAMES "\[\d{6,}\]" "id:100200,phase:2,deny,status:403,msg:'Oversized array index in field name'". Deploy within the 30-day noisgate mitigation SLA.cluster module, or Kubernetes with liveness probes and rapid restart policies. This doesn't prevent the crash but limits downtime per crash to <2 seconds per worker. Against sustained attacks, this converts a hard outage into intermittent request drops — still degraded, but survivable.- Helmet.js or Express security headers — these operate on HTTP response headers and do not inspect or modify incoming multipart request bodies. They provide no protection against this attack vector.
- Express error middleware (
app.use((err, req, res, next) => ...)) — the uncaughtRangeErrorinappend-fieldfires synchronously outside Express's error-handling pipeline. The middleware never gets a chance to catch it. This is precisely why the bug is dangerous. - Setting
multer({ limits: { fields: N } })field count limits — the crash occurs during field-name parsing *before* the field-count limit is evaluated. The limit won't fire in time to prevent the RangeError. - Node.js
--max-old-space-sizememory limits — the sparse array allocation is lazy and doesn't consume proportional memory. The crash is aRangeErrorfrom exceeding V8's array-length invariant, not an OOM condition.
The supporting signals.
| In-the-Wild Exploitation | Not observed. Not listed in CISA KEV. No campaigns or threat actor usage reported as of 2026-10-08. No GreyNoise or Shadowserver tags for this CVE. |
|---|---|
| Proof-of-Concept | Public PoC available. GitHub repo wtfkimi/CVE-2026-77078 provides a TypeScript-based exploit with English and Russian documentation. Also indexed on Sploitus. No nuclei template found yet. Exploitation is trivial — reproducible with a single curl command. |
| EPSS | 0.00488 (0.49%) — approximately 70th percentile. Indicates a below-average probability of exploitation in the next 30 days, though the public PoC and trivial exploitation path may push this higher over time. |
| CISA KEV | Not listed as of 2026-10-08. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — Network-reachable, zero complexity, no privileges, no interaction, availability-only impact. The vector accurately captures the threat: easy to fire, hard to miss, but no data at risk. |
| Affected Versions | All multer versions < 2.3.0, including the widely-used 1.4.x LTS line. The root cause is in the append-field dependency's array-index handling. Version 2.2.0 is also affected by a related file-descriptor leak (CVE-2026-77037). |
| Fixed Version | multer 2.3.0 (released 2026-08-31). Fix is in commit 87a584e on expressjs/multer — adds try/catch around appendField and introduces opt-in limits.fieldArrayIndexLimit configuration. No distro backports; npm is the canonical distribution channel. |
| Installed Base / Exposure | ~22 million weekly npm downloads. 6,700+ dependent packages in the npm registry. Multer is the de facto standard for file uploads in Express.js. Downstream consumers include NestJS (@nestjs/platform-express), OWASP Juice Shop, IBM OpenPages, and thousands of enterprise Node.js APIs. |
| Disclosure Timeline | Disclosed 2026-08-28. Express.js security release blog post 2026-08-31. Reported by O4FDev, fixed by UlisesGascon. |
| Related CVEs | multer 2.3.0 also fixes CVE-2026-77037 (fd leak on aborted uploads, HIGH), CVE-2026-82333 (event-loop blocking via oversized index, HIGH), and CVE-2026-77063 (file-size limit bypass, LOW). Upgrading to 2.3.0 addresses all four. |
Sources.
Why this verdict
- Zero-friction exploitation path: Unauthenticated, no user interaction, low complexity, single HTTP request. The attacker needs nothing more than
curland the target's URL. Every prerequisite in the chain is met by default in any internet-facing Express.js app using multer. No compounding downward pressure from access requirements. - Massive installed base amplifies risk: With ~22M weekly npm downloads and 6,700+ dependents, multer is ubiquitous in the Node.js ecosystem. The fraction of Express.js applications parsing multipart data with multer is enormous — this isn't a niche component.
- Public PoC lowers the bar to zero: The wtfkimi/CVE-2026-77078 repository provides a ready-made exploit. The payload is ~200 bytes. Weaponization requires no reverse engineering.
- Role multiplier: Multer runs in Node.js web application backends. (a) *Low-value roles* (dev servers, internal tools): DoS is a nuisance. (b) *Typical roles* (production API servers, SaaS backends): DoS causes customer-facing outages and SLA violations. (c) *High-value roles* (API gateways, payment or auth services on Express.js): DoS disrupts critical business functions. However, the blast radius in all cases is availability-only — no path to domain takeover, fleet compromise, data exfiltration, or supply-chain pivot. The outcome caps at 'service outage,' which does not trigger the HIGH/CRITICAL floor rule. The verdict holds at HIGH on exploitability merits alone.
- DoS-only impact is the ceiling: The CVSS vector is C:N/I:N/A:H. There is no code execution, no data leakage, no privilege escalation. This is a hard cap that prevents CRITICAL regardless of exploitability.
Why not higher?
CRITICAL requires confidentiality or integrity impact, or an availability impact that cascades to domain/fleet/supply-chain compromise. This CVE is strictly availability-only — it crashes a single Node.js process. Even against high-value Express.js services, the blast radius is bounded to service outage, not lateral movement or data breach. Process managers further limit sustained impact.
Why not lower?
Downgrading to MEDIUM would require meaningful friction in the exploitation chain, but there is none: the attack is unauthenticated, remote, low-complexity, and requires a single request against the most popular file-upload middleware in the Node.js ecosystem. The public PoC eliminates any skill barrier. The 22M-download installed base means this affects a significant fraction of all Node.js web applications. The EPSS is modest (0.49%) and KEV is empty, but the raw exploitability against an enormous attack surface justifies HIGH.
Crowdsourced verification payload.
Run this script on any host where the Node.js application is deployed, or on a CI runner that has access to the project's node_modules. No special privileges required — it reads package.json metadata. Invoke with: bash check_multer_cve_2026_77078.sh /path/to/your/project
#!/usr/bin/env bash
# check_multer_cve_2026_77078.sh
# Checks whether the installed multer version is vulnerable to CVE-2026-77078
# Usage: bash check_multer_cve_2026_77078.sh /path/to/project
# Exit codes: 1 = VULNERABLE, 0 = PATCHED, 2 = UNKNOWN
set -euo pipefail
PROJECT_DIR="${1:-.}"
PKG_JSON="${PROJECT_DIR}/node_modules/multer/package.json"
if [ ! -f "$PKG_JSON" ]; then
echo "UNKNOWN — multer not found in ${PROJECT_DIR}/node_modules/"
echo "Either multer is not installed, or the path is wrong."
exit 2
fi
# Extract version from package.json
VERSION=$(grep -o '"version": *"[^"]*"' "$PKG_JSON" | head -1 | sed 's/"version": *"//;s/"//')
if [ -z "$VERSION" ]; then
echo "UNKNOWN — could not parse version from $PKG_JSON"
exit 2
fi
echo "Detected multer version: $VERSION"
# Compare version against fixed version 2.3.0
# Split version into components
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
# Strip any pre-release suffix from PATCH for numeric comparison
PATCH_NUM=$(echo "$PATCH" | grep -o '^[0-9]*')
if [ -z "$MAJOR" ] || [ -z "$MINOR" ] || [ -z "$PATCH_NUM" ]; then
echo "UNKNOWN — could not parse semver components from version $VERSION"
exit 2
fi
# Fixed in 2.3.0. Vulnerable if < 2.3.0.
if [ "$MAJOR" -lt 2 ]; then
echo "VULNERABLE — multer $VERSION is affected by CVE-2026-77078 (all versions < 2.3.0)"
echo "Upgrade to multer >= 2.3.0: npm install multer@latest"
exit 1
elif [ "$MAJOR" -eq 2 ] && [ "$MINOR" -lt 3 ]; then
echo "VULNERABLE — multer $VERSION is affected by CVE-2026-77078 (all versions < 2.3.0)"
echo "Upgrade to multer >= 2.3.0: npm install multer@latest"
exit 1
elif [ "$MAJOR" -eq 2 ] && [ "$MINOR" -eq 3 ] && [ "$PATCH_NUM" -lt 0 ]; then
echo "VULNERABLE — multer $VERSION is affected by CVE-2026-77078 (all versions < 2.3.0)"
echo "Upgrade to multer >= 2.3.0: npm install multer@latest"
exit 1
else
echo "PATCHED — multer $VERSION is not affected by CVE-2026-77078"
exit 0
fi