Sep 30, 2026·6 min read·6 visits
A single, deeply nested pattern object can crash a NestJS microservice utilizing TCP or RabbitMQ transport due to uncaught RangeError exceptions.
An unauthenticated remote attacker can crash NestJS microservices utilizing TCP or RabbitMQ transport layers. The vulnerability exists due to recursive serialization of deeply nested message patterns using JSON.stringify, leading to a RangeError and process termination.
The vulnerability CVE-2026-102281 is a denial of service flaw located within the NestJS framework, specifically affecting the @nestjs/microservices package. NestJS microservices use transport layers to communicate across distributed environments. When configured to use either Transmission Control Protocol (TCP) or RabbitMQ (RMQ) transports, the microservice server listens to incoming messages, deserializes them, and performs routing based on the message pattern.
This architecture exposes an attack surface where an unauthenticated remote sender can submit a maliciously crafted message with a structured pattern. The transport layer attempts to serialize this pattern to determine the appropriate handler. This serialization process is vulnerable to uncontrolled recursion, resulting in a thread-terminating error.
The flaw represents a combination of CWE-674 (Uncontrolled Recursion) and CWE-248 (Uncaught Exception). A single payload is sufficient to crash the entire application process, rendering the microservice completely unavailable to legitimate clients until it is manually or automatically restarted.
The root cause lies in how incoming message patterns are handled in the ServerTCP#handleMessage and ServerRMQ#handleMessage methods of the @nestjs/microservices package. When a client-controlled message pattern is received, the NestJS server tries to generate a lookup key to find the corresponding handler. If the pattern is not a flat string, the server automatically passes it to the native JSON.stringify utility without error encapsulation.
While the preceding JSON parsing phase typically handles nested structures efficiently, the native JSON.stringify engine in V8 utilizes recursive descent to traverse and serialize objects. If an object is nested to an extreme depth, generally exceeding 10,000 levels, the recursion exhausts the stack limit of the JavaScript engine. V8 raises a RangeError: Maximum call stack size exceeded exception to halt execution.
Because the message parsing operates in an asynchronous promise context, this synchronous RangeError is converted into an asynchronous rejection. The server lacked a proper rejection handler (.catch()) on the promise chain linked to the underlying socket event listener. Modern Node.js runtimes (version 15 and higher) default to exiting the process upon encountering an unhandled promise rejection, which causes the application daemon to terminate.
An analysis of the vulnerable source code shows how the input stringification was executed without exception wrapping. In the TCP and RMQ transport handlers, raw message payloads were deserialized and processed. The pattern extraction was implemented as follows:
// Vulnerable implementation in ServerTCP
const packet = await this.deserializer.deserialize(rawMessage);
const pattern = !isString(packet.pattern)
? JSON.stringify(packet.pattern) // Throws RangeError if nested too deeply
: packet.pattern;The patch addresses the serialization defect by routing the stringification through a new guarded helper function named getPatternAsString. This helper safely wraps the serialization logic in a try-catch block, intercepting any RangeError exceptions and returning a fallback string representation if serialization fails:
// Patched implementation in Server
protected getPatternAsString(pattern: unknown): string {
if (isString(pattern)) {
return pattern;
}
try {
return JSON.stringify(pattern);
} catch {
return '[UNSERIALIZABLE_PATTERN]'; // Fallback prevents call stack crash
}
}Additionally, the patch wraps the asynchronous event listeners in .catch() blocks to prevent unhandled promise rejections from propagating to the global runtime. In ServerTCP, the message listener was modified to gracefully handle failures via the server's internal error handler:
// Patched message binding in ServerTCP
readSocket.on('message', (msg: ReadPacket & PacketId) =>
this.handleMessage(readSocket, msg).catch(err => this.handleError(err))
);Exploitation of CVE-2026-102281 is highly reliable and requires no elevated privileges or prior authentication. To exploit a microservice using the TCP transport, the attacker must have network reachability to the exposed TCP port. For microservices utilizing RabbitMQ, the attacker must have authorization to publish messages to the specific exchange or queue from which the microservice consumes.
The attack begins with generating a JSON object containing a heavily nested structure. An attacker can write a simple generation script to construct an object with a depth of 100,000 nested properties, represented as {"nested": {"nested": ... {}}}. This structure is wrapped in the standard NestJS envelope, using the target pattern property to carry the nested object.
Upon transmitting this frame to the target service, the microservice successfully parses the inbound stream because the parser accommodates highly nested frames. However, as soon as the framework invokes the routing lookup and attempts to stringify the pattern, the call stack is exhausted. The thread throws a RangeError, which leads to an unhandled promise rejection, immediately killing the Node.js process.
The impact of this vulnerability is a complete loss of service availability (CVSS score: 7.5). Because a single, unauthenticated network message can terminate the backend application, any public-facing or internally exposed vulnerable service can be disabled on demand by an attacker.
No confidentiality or integrity impacts are associated with this flaw. The vulnerability does not permit remote code execution, file system manipulation, or unauthorized data retrieval. The entire payload is discarded as the engine crashes, meaning no malicious commands are executed.
However, the operational impact in enterprise systems is high. If the microservice is not configured with an automatic process supervisor such as PM2, systemd, or Kubernetes replication controllers, the service will remain offline until manual intervention occurs. If a process supervisor is active, an attacker can continuously send the payload to create a crash loop, exhausting system resources and causing persistent denial of service.
The primary remediation strategy is to upgrade @nestjs/microservices to a secure version. For systems running on the 11.x release line, the dependency must be updated to version 11.2.4 or higher. For systems on the 12.x release line, the package must be updated to 12.0.2 or higher.
In environments where upgrading is not immediately possible, several secondary defensive configurations are recommended. Access control lists must be configured to block access to the microservice's TCP port from untrusted networks. RabbitMQ publishing privileges must be restricted so that external, untrusted clients cannot post to queues consumed by the microservice.
As a temporary infrastructure workaround, the Node.js process can be started with the --unhandled-rejections=warn command-line flag. This configuration changes the runtime response to unhandled promise rejections, printing a warning to the console instead of terminating the process. This flag prevents the crash but may introduce memory leaks or unexpected state conditions if other unrelated rejections occur.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
NestJS Microservices NestJS | < 11.2.4 | 11.2.4 |
NestJS Microservices NestJS | >= 12.0.0, < 12.0.2 | 12.0.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-674, CWE-248 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 7.5 (High) |
| EPSS Score | 0.00376 (29.01% percentile) |
| Impact | Availability (Denial of Service) |
| Exploit Status | Proof of Concept available |
| KEV Status | Not listed |
The software direct or indirect recursion without effective limits on recursion depth, causing stack allocation space exhaustion.
CVE-2026-101894 is a critical path traversal vulnerability in @xhmikosr/decompress before versions 10.2.2 and 11.1.4, stemming from an incomplete hardening bypass of CVE-2026-53486 where static lexical containment checks fail to detect kernel-level resolution of crafted symlink chains, allowing arbitrary local file modification and execution.
A security-critical desynchronization vulnerability exists in fast-uri versions 4.1.3 and 4.1.4. Due to incorrect order-of-operations, the mailto scheme parser validates raw percent-encoded parameter keys instead of normalized keys, but subsequently decodes and writes them into a generic headers object. When the parsed URI is serialized, these keys are re-emitted literally, allowing attackers to bypass validation boundaries and smuggle unauthorized recipients, subjects, or body parameters in downstream mailing applications.
CVE-2026-86472 is a validation bypass vulnerability in fast-uri (a high-performance RFC 3986 URI toolbox heavily used by popular Node.js frameworks like Fastify and validation libraries like AJV). The vulnerability stems from improper handling of case sensitivity (CWE-178) due to an incorrect order of operations during hostname canonicalization in scheme-relative URLs. An attacker can leverage percent-encoded uppercase characters within scheme-relative URLs to bypass domain blocklists/allowlists in downstream applications. Because hostname resolution in DNS and HTTP is case-insensitive, the bypassed host representation still routes to the target destination, resulting in potential Server-Side Request Forgery (SSRF) or security control bypasses.
A resource management vulnerability in the Undici HTTP client (CWE-772) occurs when the retry interceptor receives a partial body payload followed by a non-retryable response error on a subsequent connection attempt, resulting in orphaned streams and potential Denial of Service (DoS).
A vulnerability in PyJWT's JWK Set parsing logic allows a malformed RSA key to trigger an unhandled ValueError, leading to an application-wide or request-level Denial of Service.
An uncontrolled recursion vulnerability exists in Nodemailer versions up to and including 10.0.1. When parsing recipient email addresses, recursively nested arrays bypass the parser's depth limit, resulting in V8 call stack exhaustion and immediate synchronous process termination.