Sep 30, 2026·6 min read·5 visits
A quadratic-time algorithmic complexity flaw in Jackson's forward-reference resolution mechanism allows unauthenticated remote attackers to trigger severe CPU thread starvation by submitting specialized JSON payloads containing out-of-order references.
An uncontrolled resource consumption vulnerability in FasterXML jackson-databind allows remote unauthenticated attackers to cause a Denial of Service (DoS) via crafted JSON payloads containing out-of-order forward references in identity-enabled collections or maps.
The security vulnerability CVE-2026-91777 (tracked as GHSA-cxp5-3px4-pw24) is a critical resource consumption defect residing within the FasterXML jackson-databind core deserialization pipeline. It specifically targets endpoints that handle incoming object graphs with cyclical, duplicate, or out-of-order identity-enabled relationships. When an application implements the @JsonIdentityInfo annotation on standard collection or map structures, Jackson generates internal structures to resolve forward-referencing Object IDs during deserialization.
An unauthenticated remote attacker can exploit this mechanism by preparing a specifically crafted JSON body that forces the deserializer's internal accumulator into a worst-case lookup loop. Because the affected parser iterates through arrays in sequential execution order, out-of-order definitions of referenced identifiers cause the processing engine to repeatedly traverse the unresolved queue. This behavior increases processing overhead significantly.
This vulnerability belongs to the algorithmic complexity class (CWE-400), which yields computational denial of service. Traditional defensive parameters, such as restricting nesting depth, fail to prevent this exploitation method because the attack does not rely on deep hierarchy loops or invalid schema configurations. The impact is restricted to the execution context of the JVM, though complete CPU starvation can affect adjacent application threads.
The root cause of the vulnerability lies in the structural choice of a flat list for tracking pending identity references during collection and map parsing. In legacy versions of jackson-databind, the CollectionReferringAccumulator and MapReferringAccumulator components utilize a standard java.util.ArrayList structure, named _accumulator, to cache pending CollectionReferring objects.
When Jackson parses an array with elements that refer to Object IDs before their explicit definition, it constructs placeholder CollectionReferring nodes and appends them to this array list. When the actual definition of an Object ID is encountered, the deserializer calls resolveForwardReference. This trigger prompts a linear iteration from the beginning of the _accumulator array list via an Iterator structure to locate the target ID node.
If the payload organizes these object definitions in reverse order relative to their initial referencing declarations, the iterator executes a worst-case linear scan of the outstanding list size for each resolution step. Compounding this, the accumulator executes a manual buffer-merging loop using previous.addAll(ref.next) to preserve chronological order for elements parsed after the unresolved ID. The cumulative cost of both operations scales exponentially to O(N^2) time complexity for N forward references.
To understand the technical issue, analyze the legacy implementation of resolveForwardReference within CollectionDeserializer.java. In this code path, the resolver performs a linear traversal on every invocation to find the matching unresolved ID:
// LEGACY VULNERABLE CODE
public void resolveForwardReference(Object id, Object value) throws IOException {
Iterator<CollectionReferring> iterator = _accumulator.iterator();
Collection<Object> previous = _result;
while (iterator.hasNext()) {
CollectionReferring ref = iterator.next();
if (ref.hasId(id)) {
iterator.remove();
previous.add(value);
previous.addAll(ref.next); // Merge buffers (quadratic step)
return;
}
previous = ref.next;
}
throw new IllegalArgumentException("Unresolved forward reference id not seen.");
}The official fix, introduced in commit 37ad9b81712cbb9fb62c2d2c1813593252a24b67, replaces the flat accumulator list with a composite data structure. It utilizes a HashMap named _unresolvedById that maps keys directly to a Deque of CollectionReferring references, yielding O(1) lookup performance. Additionally, it preserves ordering through a flat linear queue structure named _pending coupled with an index pointer, _pendingStart.
// PATCHED SECURE CODE
public void resolveForwardReference(Object id, Object value) throws IOException {
Deque<CollectionReferring> refs = _unresolvedById.get(id);
if (refs == null) {
throw new IllegalArgumentException("Unknown forward reference id.");
}
CollectionReferring ref = refs.removeFirst();
if (refs.isEmpty()) {
_unresolvedById.remove(id);
}
ref.resolve(value);
// Linear sliding-window evaluation
final int end = _pending.size();
int i = _pendingStart;
for (; i < end; ++i) {
Object pending = _pending.get(i);
if (pending instanceof CollectionReferring) {
CollectionReferring pendingRef = (CollectionReferring) pending;
if (!pendingRef.isResolved()) {
break; // Halted by the next unresolved reference
}
pending = pendingRef.resolvedValue();
}
_result.add(pending);
_pending.set(i, null);
}
_pendingStart = (i == end) ? 0 : i;
if (i == end) _pending.clear();
}By using this queue sliding window, every parsed and pending object is evaluated and shifted to the destination _result collection exactly once. This shifts the runtime profile from a quadratic O(N^2) scale down to a stable linear O(N) scale.
Exploitation of CVE-2026-91777 is highly reliable and requires no specialized prerequisites other than a network path to a vulnerable Jackson deserialization endpoint. The target class must utilize the @JsonIdentityInfo annotation to track references. The attacker constructs a payload consisting of two distinct sections: the list of forward references and the reverse-order declaration definitions.
For a Collection-based exploit, the input array starts with a sequence of string keys matching the generated Object IDs. The parser is forced to cache each reference inside the accumulator as it reads the array from left to right. Immediately following the keys, the attacker provides the full object definitions in reverse numeric order:
[
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
{"id": "19", "name": "node19"},
{"id": "18", "name": "node18"},
{"id": "17", "name": "node17"},
{"id": "0", "name": "node0"}
]Because each definition from 19 to 0 triggers a linear scan from the beginning of the legacy list, the JVM executes thousands of comparisons for small datasets. For larger payloads of several thousand elements, the calculation yields millions of comparison loops. This process consumes the entire CPU execution slice of the handling thread, triggering rapid denial of service.
The vulnerability carries a CVSS Base Score of 7.5, indicating a high-impact availability threat. Successful exploitation leads directly to CPU resource exhaustion on the target application node, blocking all standard requests and potentially causing orchestrators to restart the container under load-balancer stress.
Remediation consists of upgrading to the patched Jackson-databind versions. However, the fix itself introduces minor structural constraints. Since unresolved references block the sliding window, a single un-resolved reference at the beginning of an array will hold all subsequent items in heap memory within _pending. This behavior poses a minor risk of memory exhaustion if request payloads are sufficiently large.
Below is a conceptual logical comparison of the legacy vs. patched behavior inside Jackson:
While the patch is functionally complete and eliminates the quadratic lookup vector, administrators must configure maximum packet-size limits. This prevents attackers from bypassing the CPU check by abusing the residual memory retention mechanism in the sliding window buffer.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
jackson-databind FasterXML | >= 2.5.0, < 2.18.11 | 2.18.11 |
jackson-databind FasterXML | >= 2.19.0, < 2.21.7 | 2.21.7 |
jackson-databind FasterXML | >= 2.22.0, < 2.22.3 | 2.22.3 |
jackson-databind FasterXML | >= 3.0.0, < 3.1.7 | 3.1.7 |
jackson-databind FasterXML | >= 3.2.0, < 3.2.3 | 3.2.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400 |
| Attack Vector | Network |
| CVSS Score | 7.5 (High) |
| EPSS Score | 0.0045 |
| Impact | Denial of Service (CPU Exhaustion) |
| Exploit Status | Proof of Concept available |
| KEV Status | Not currently listed |
The product does not properly control the allocation and maintenance of a limited resource, enabling an actor to cause resource exhaustion.
A security vulnerability in league/commonmark versions 1.3.0 through 2.10.1 allows remote attackers to bypass Stored Cross-Site Scripting (XSS) protections in the DisallowedRawHtml extension. Due to an validation logic flaw in the regular expression parser, specifically handling bare, unclosed HTML blocks ending at the string boundary, raw HTML tags can be passed to the rendered output. When combined with browser-side parsing heuristics, an attacker can execute arbitrary JavaScript in the context of the user session.
A DOM-based Cross-Site Scripting (DOM XSS) vulnerability in DOMPurify allows attackers to bypass sanitization when using the in-place sanitization configuration (IN_PLACE: true) combined with custom hooks. If custom hooks detach elements during post-processing phases, nested malicious payloads can escape sterilization and execute in the browser's context.
A security vulnerability in serialize-javascript v7.1.1 allows Cross-Site Scripting (XSS) due to an overly greedy regular expression (SCRIPT_CLOSE_REGEXP) used during function serialization. Two secondary defects involving a spoofed toString() validation bypass and a stateful native code validator are also addressed in the fixed version v7.1.2.
A Denial of Service (DoS) vulnerability exists in the PyJWT library when parsing unverified token payloads containing deeply nested JSON structures. Because PyJWT fails to catch RecursionError during payload parsing, an unauthenticated remote attacker can crash the application thread or worker by sending a specially crafted token.
An improper output encoding and escaping vulnerability (CWE-116) in Vercel Satori allows unauthenticated remote attackers to perform markup injection in dynamic Open Graph images generated via Next.js's ImageResponse. Unsanitized parameter interpolation into SVG elements breaks XML structural boundaries. This exposes downstream parsing, rasterization, and rendering pipelines to Server-Side Request Forgery (SSRF), Local File Read, and Remote Code Execution (RCE).
An uncontrolled recursion vulnerability exists in PyJWT from version 2.13.0 to 2.14.0. The vulnerability allows remote, unauthenticated attackers to cause a Denial of Service (DoS) via crafted JWT headers that trigger stack exhaustion during JSON decoding.