Mar 1, 2026·6 min read·10 visits
Multer < 2.1.0 fails to delete temporary files if a request errors out while an asynchronous file filter is running. Attackers can flood the server with requests that trigger this condition, filling the disk and crashing the application.
A critical resource exhaustion vulnerability exists in the Multer Node.js middleware versions prior to 2.1.0. The issue arises from a race condition between asynchronous file filtering and stream error handling. When a request triggers an error during the processing of a multipart stream, files that were pending validation in an asynchronous `fileFilter` are not properly cleaned up from the disk. This allows remote attackers to exhaust the server's storage capacity by repeatedly sending crafted requests, leading to a Denial of Service (DoS).
Multer is a popular middleware for handling multipart/form-data in Node.js applications, primarily used for file uploads. It processes incoming file streams, writes them to a destination (disk or memory), and provides hooks for validation via the fileFilter option. CVE-2026-3304 identifies a logic flaw in how Multer manages resource lifecycles when these validation hooks operate asynchronously.
The core of the vulnerability is a desynchronization between the global request state and individual file processing. When Multer receives a file, it may write it to a temporary location before deciding whether to keep it (based on fileFilter). If the fileFilter is asynchronous—common for lookups like malware scanning or quota checking—the underlying multipart parser (Busboy) continues processing the rest of the request stream.
If a fatal error occurs in the stream (e.g., a malformed subsequent part) while the fileFilter is still pending, the global error handler is triggered. However, in vulnerable versions, the callback for the pending fileFilter does not check this global error state upon completion. Consequently, the temporary file written to disk is effectively "orphaned." It is neither passed to the application nor deleted, remaining on the filesystem indefinitely. Repeated exploitation leads to complete disk exhaustion and service unavailability.
The vulnerability stems from the implementation of the make-middleware.js module in Multer. Specifically, it involves the interaction between the appender (responsible for writing data) and the fileFilter (responsible for validation).
When a file header is parsed, Multer invokes appender.insertPlaceholder. If the configured storage engine is DiskStorage, this creates a file on disk immediately. Multer then invokes the user-provided fileFilter. The logic flaw resides in the callback wrapper for this filter.
Prior to version 2.1.0, the callback only handled errors explicitly passed to it or the boolean decision to include the file. It failed to inspect the errorOccured flag, which tracks the health of the overall request stream. If the multipart parser encountered a protocol violation (such as a part missing the Content-Disposition name) after the file processing started but before the asynchronous filter completed, the errorOccured flag would be set to true.
However, when the asynchronous filter eventually resolved, the code proceeded as if the stream were healthy. Crucially, it bypassed the appender.removePlaceholder(placeholder) call that is necessary to clean up files when an operation is aborted. This specific execution path—async wait combined with stream failure—creates a zombie file on the filesystem.
The fix was applied in lib/make-middleware.js by introducing a check for the errorOccured state within the fileFilter callback. This ensures that if the request has already failed, the pending file is immediately cleaned up.
Vulnerable Code (Simplified):
// Inside make-middleware.js
fileFilter(req, file, function (err, includeFile) {
if (err) {
appender.removePlaceholder(placeholder)
return next(err)
}
if (!includeFile) {
appender.removePlaceholder(placeholder)
return fileStream.resume()
}
// ... proceed to save file ...
// CRITICAL MISSING CHECK: Does not verify if the request crashed elsewhere
})Patched Code:
The patch introduces a guard clause at the very beginning of the callback. This logic acknowledges that if the request stream has already error-ed out (due to malformed data later in the stream), the current file is irrelevant and must be removed.
// Inside make-middleware.js (Fixed)
fileFilter(req, file, function (err, includeFile) {
// NEW: Check global error state before proceeding
if (errorOccured) {
appender.removePlaceholder(placeholder)
return fileStream.resume()
}
if (err) {
appender.removePlaceholder(placeholder)
return next(err)
}
if (!includeFile) {
appender.removePlaceholder(placeholder)
return fileStream.resume()
}
// ... proceed to save file ...
})This simple addition ensures that the removePlaceholder method is called even when the error originated outside the scope of the current file's filter function.
To exploit CVE-2026-3304, an attacker does not need authentication or special privileges. They simply need to send a crafted multipart/form-data request to an endpoint utilizing Multer with an asynchronous fileFilter. The attack relies on timing and stream ordering.
Attack Steps:
file1.png).file1.png, writes it to the temporary directory, and calls the asynchronous fileFilter (e.g., checking if the file signature is allowed). This pauses processing for this specific file while the Node.js event loop continues.Content-Disposition header but lack the required name parameter.errorOccured flag is set to true.fileFilter for file1.png completes. Because the vulnerable code doesn't check errorOccured, it assumes success or simply ignores the cleanup path. The request terminates, but file1.png remains in the temporary folder.By scripting this process to occur in a loop, an attacker can rapidly fill the server's disk space with orphaned temporary files.
The primary impact of this vulnerability is Denial of Service (DoS) via disk resource exhaustion. In a cloud environment or containerized deployment with ephemeral storage, the impact may be temporary until the container restarts. However, in persistent hosting environments, this can lead to prolonged downtime.
Consequences:
dest directory is on the root partition, the entire operating system may become unstable, affecting other services running on the same host.CVSS 4.0 Assessment:
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
multer expressjs | < 2.1.0 | 2.1.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-459 |
| Vulnerability Type | Resource Exhaustion (DoS) |
| CVSS 4.0 | 8.7 (High) |
| Attack Vector | Network |
| Affected Component | make-middleware.js |
| Prerequisites | Asynchronous fileFilter configuration |