Sep 30, 2026·6 min read·38 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-126300 (GHSA-ffr9-mgrr-wcvr) is an inefficient algorithmic complexity vulnerability (CWE-407) in the Natural Language Toolkit (NLTK) Python library affecting versions 3.2.5 prior to 3.10.4. The flaw resides in the quote-restoration routine of TreebankWordTokenizer.span_tokenize and NLTKWordTokenizer.span_tokenize, where repeated calls to list.pop(0) over matched quote tokens introduce quadratic O(n^2) time complexity. An unauthenticated remote attacker who supplies input containing long sequences of double-quote characters can exhaust CPU resources and degrade service availability.
The Natural Language Toolkit (NLTK) versions 3.6 through 3.10.3 contain an algorithmic complexity vulnerability in the LegalitySyllableTokenizer.tokenize method. When processing tokens composed of continuous runs of vowels, an unguarded string reversal and set membership lookup loop executes in quadratic time relative to the input token length. Remote attackers capable of submitting arbitrary text to applications utilizing this tokenizer can trigger sustained single-core CPU exhaustion and application-level denial of service.
CVE-2026-126304 is a symlink path traversal vulnerability (CWE-59) in the Natural Language Toolkit (NLTK) versions 3.10.0 prior to 3.10.4. The YCOECorpusReader class constructs internal sub-readers for the 'psd' and 'pos' subdirectories using FileSystemPathPointer.join(), which relies exclusively on lexical string-prefix validation rather than canonical path resolution. An attacker who supplies a crafted YCOE corpus containing symbolic links in place of the 'psd' or 'pos' subdirectories can bypass directory confinement and read arbitrary files from sibling corpora or sensitive directories within the NLTK data sandbox.
CVE-2026-128077 (GHSA-r99w-jh2x-3mg9) is an authorization bypass and interpretation conflict vulnerability affecting Nuxt and its underlying @nuxt/nitro-server package in versions 4.6.0 through 4.6.3. When an incoming HTTP request path begins with a double forward slash (e.g., //x/admin), the WHATWG URL parser within the Nuxt server-side page renderer interprets the initial segment as a network host/authority and resolves the remaining path (/admin) against the application's page router. Because Nitro server middleware, route-scoped serverHandlers, and Nitro routeRules evaluate the unparsed request path (//x/admin), path-based access controls, redirects, and security header policies configured for /admin fail to match and are bypassed.
Prior to version 2.10.2, the sqlite3-ruby gem explicitly cast 64-bit Ruby string lengths (long) to 32-bit signed integers (int) when invoking the underlying SQLite C API. On 64-bit platforms, providing a string of 4 GiB (2^32 bytes) or larger causes integer truncation, instructing SQLite to process only a prefix of the string. This flaw affects SQL query preparation, bound parameter values, and return values from custom Ruby-defined SQL functions and aggregates, allowing trailing SQL clauses such as WHERE restrictions to be silently dropped under specific conditions.
CVE-2026-128068 (GHSA-cpgf-5mjf-73p7) is a Regular Expression Denial of Service (ReDoS) vulnerability in the rails-html-sanitizer Ruby gem affecting versions 1.0.3 through 1.7.1. The flaw resides in the Rails::HTML::PermitScrubber#scrub_attribute method, which uses the multiline regular expression /^\s*[^#\s].*/m to enforce local-only URI fragment references on SVG elements. On Ruby runtimes prior to version 3.2 (CRuby <= 3.1 and JRuby), evaluating an attribute value composed of repeated newline and whitespace characters without a hash character triggers quadratic backtracking O(n^2). Applications are only vulnerable if their sanitization allowlist is explicitly configured to permit SVG reference elements such as <use> alongside href or xlink:href attributes.