Sep 9, 2026·5 min read·4 visits
Unauthenticated remote attackers can exhaust server file descriptors by repeatedly initiating and aborting multipart file uploads in multer v2.2.0, causing a complete denial of service.
A resource consumption vulnerability exists in the multer library version 2.2.0 when utilizing the disk storage engine. When a remote client aborts or truncates an in-progress file upload, multer removes the partial file from the disk but fails to properly close the active write stream. This behavior leaves the underlying file descriptor open in the operating system, allowing a remote attacker to systematically exhaust the server's file descriptor limits and trigger a Denial of Service.
The library multer is an extremely common Node.js middleware designed to handle multipart/form-data uploads. When dealing with file uploads, applications rely on storage engines to persist the incoming data stream to either memory or disk. The default disk storage engine (diskStorage) writes incoming files directly to the host filesystem as they are parsed from the network socket.
This architecture creates a direct channel between untrusted network inputs and host filesystem operations. Because parsing occurs asynchronously, the middleware must handle unexpected interruptions gracefully. If a client terminates the network connection during an upload, the application must immediately release all associated OS resources, including memory allocations and open file handles.
In version 2.2.0, the cleanup logic fails to manage the lifecycle of the write streams properly. This results in an incomplete cleanup flaw classified under CWE-459. By repeatedly interrupting the upload process, an unauthenticated attacker can exhaust the host's file descriptor table, resulting in a persistent Denial of Service.
The underlying flaw stems from the way Node.js handles stream piping. In multer version 2.2.0, the multipart parser streams the incoming request body directly into a write stream using the standard .pipe() method:
file.stream.pipe(outStream);In standard Node.js stream architecture, piping transfers data from a readable source to a writable destination. However, if the source stream emits an error, is destroyed, or is abruptly truncated due to a disconnected client, the .pipe() method does not automatically propagate the closure or destruction to the destination write stream. The destination stream remains in an open state, holding its assigned file descriptor.
When a network socket disconnects prematurely, multer registers the failure and attempts to clean up by invoking fs.unlink() on the partial file path. On POSIX-compliant systems like Linux, the fs.unlink() system call removes the directory entry for the file, which makes it disappear from standard listings. However, because the process still holds an open file descriptor pointing to the deleted file, the operating system retains the physical block pointers and keeps the file descriptor active in /proc/self/fd. On Windows, the OS blocks the deletion entirely, throwing EPERM or EBUSY because the file has an active stream lock.
To resolve the vulnerability, the development team updated the codebase in commit eef74440b491ef0f976f3d25895e8089ae495df0. The key change replaces the manual .pipe() operation with Node.js's native stream.pipeline utility, which manages the entire lifecycle of piped streams.
// Vulnerable Implementation (storage/disk.js)
file.stream.pipe(outStream)
outStream.on('error', cb)
outStream.on('finish', function () {
cb(null, { ... })
})// Patched Implementation (storage/disk.js)
pipeline(file.stream, outStream, function (err) {
if (err) return cb(err)
cb(null, {
destination: destination,
filename: filename,
path: finalPath,
size: outStream.bytesWritten
})
})The stream.pipeline implementation ensures that if either the incoming request stream or the write stream encounters an error or aborts, the utility automatically invokes .destroy() on both streams, closing the file descriptor immediately. The patch also introduces a global WeakMap named openStreams to link file objects to their active write streams, allowing safe deletion only after the stream is fully closed and the descriptor released.
Exploiting this vulnerability does not require complex payloads or bypassed authentication mechanisms. An attacker only needs network access to an endpoint that uses the multer disk storage engine. The attack vector targets resource consumption limit constraints in the operating system, such as ulimit -n (the limit of open files permitted per process).
An attacker begins the attack by establishing a TCP connection and sending a standard HTTP POST request with Content-Type: multipart/form-data. As the server begins processing the upload and initializes a write stream, the attacker waits a fraction of a second and then sends a TCP RST (Reset) packet or forcefully destroys the client socket. This causes the underlying socket stream to emit an error, terminating the request mid-stream.
Because the destination write stream is never closed by the vulnerable multer code, one file descriptor remains leaked. By scripting a simple concurrent loop to repeat this cycle several hundred times, the attacker can quickly reach the host operating system's default file descriptor limits (typically 1024 on standard Linux distributions). Once exhausted, the Node.js process cannot accept new TCP sockets or perform critical network and filesystem calls, resulting in a persistent Denial of Service.
The primary remediation for this vulnerability is upgrading the application's dependencies to ensure multer version 2.3.0 or later is installed. The package manager command npm install multer@latest will retrieve the patched version, replacing the vulnerable stream handling structure.
If upgrading the library is not immediately possible due to production freeze rules, developers can implement temporary workarounds. One effective method is configuring a reverse proxy such as Nginx or HAProxy in front of the Node.js application. The proxy can enforce strict request timeout policies, buffer request bodies fully before passing them to Node.js, or limit the maximum rate of concurrent connections from single source IPs.
Alternatively, applications can transition from disk storage to memory storage by configuring multer.memoryStorage(). While this prevents the file descriptor leak, developers must exercise caution as memory-backed storage is susceptible to heap memory exhaustion when processing large file uploads. Process monitoring solutions like pm2 or Kubernetes liveness probes can also be configured to automatically restart the container or process if file descriptor consumption surpasses safety limits.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
multer expressjs | = 2.2.0 | 2.3.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400 (Uncontrolled Resource Consumption) / CWE-459 (Incomplete Cleanup) |
| Attack Vector | Network / Unauthenticated |
| CVSS v3.1 | 7.5 (High) |
| EPSS Score | 0.00347 (Percentile: 27.68%) |
| Impact | Denial of Service (DoS) via File Descriptor Exhaustion |
| Exploit Status | Proof of Concept (PoC) available |
| KEV Status | Not Listed |
The software does not control the allocation and maintenance of a limited resource, enabling an actor to exhaust that resource.
CVE-2026-77063 details a security flaw in multer, the standard multipart/form-data handler for Node.js, where asynchronous file filters introduce a race condition. This condition causes the library to miss file size limitation events, resulting in the silent acceptance of truncated files.
CVE-2026-77078 is a critical denial of service vulnerability in the multer Node.js package, allowing unauthenticated remote attackers to crash the runtime process using a single crafted multipart/form-data HTTP payload.
A path traversal vulnerability (CWE-22) in the Microsoft TypeSpec compiler core and associated emitter packages permits unvalidated user input to escape the designated output directory, resulting in arbitrary JSON and YAML file creation or modification on the host system.
A critical parser differential vulnerability exists in Nodemailer prior to version 9.1.0. An attacker can bypass recipient-domain validation checks by utilizing RFC 5322 comments, leading to unauthorized email routing.
An algorithmic complexity vulnerability in Nodemailer before version 9.1.0 allows remote attackers to block the Node.js event loop. This denial of service is triggered by processing large or complex lists of email addresses, leading to quadratic resource consumption.
Nodemailer (prior to version 9.1.0) is vulnerable to an IDN/Punycode domain allow-list bypass due to an interpretation conflict between legacy RFC-3492 codecs and modern UTS-46 Unicode parsers.