CVE-2026-91777
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
jackson-databind is the data-binding package of the FasterXML Jackson suite. It provides the ObjectMapper API that converts JSON and other supported formats to and from Java objects, together with the tree model and the standard serializers and deserializers for common JDK types, and it builds on the low-level streaming parser and generator supplied by jackson-core.
A Denial of Service (DoS) vulnerability (CVE-2026-91777) has been identified in the Collection and Map deserializers of jackson-databind, which allows attackers to force quadratic CPU work during deserialization by submitting a shallow, syntactically ordinary JSON document whose object identity references resolve in reverse order.
Per OWASP: The Denial of Service (DoS) attack is focused on making a resource (site, application, server) unavailable for the purpose it was designed. There are many ways to make a service unavailable for legitimate users by manipulating network packets, programming, logical, or resources handling vulnerabilities, among others. If a service receives a very large number of requests, it may cease to be available to legitimate users. In the same way, a service may stop if a programming vulnerability is exploited, or the way the service handles resources it uses.
This issue affects the forward-reference resolution path of the Collection and Map deserializers of Jackson.
Details
Module Info
- Product: Jackson
- Affected packages:
com.fasterxml.jackson.core:jackson-databind,tools.jackson.core:jackson-databind - Affected versions: com.fasterxml.jackson.core:jackson-databind >=2.5.0 <=2.18.10, >=2.19.0 <=2.21.6, >=2.22.0 <=2.22.2; tools.jackson.core:jackson-databind >=3.0.0 <=3.1.6, >=3.2.0 <=3.2.2
- GitHub repository: https://github.com/FasterXML/jackson-databind
- Published packages: https://central.sonatype.com/artifact/com.fasterxml.jackson.core/jackson-databind
- Package manager: Maven
- Fixed in:
- NES for Jackson 2.13.x, 2.14.x, and 2.15.x
- OSS com.fasterxml.jackson.core:jackson-databind 2.18.11, 2.21.7, and 2.22.3
- OSS tools.jackson.core:jackson-databind 3.1.7 and 3.2.3
Vulnerability Info
This High-severity vulnerability is found in the com.fasterxml.jackson.core:jackson-databind package in the Collection and Map deserializers of Jackson.
When a collection or map is deserialized into a value type annotated with @JsonIdentityInfo, and an element refers to an object id whose definition has not been read yet, the deserializer parks the element as a pending forward reference. CollectionDeserializer.CollectionReferringAccumulator keeps those pending references in an ArrayList, and every value read after an unresolved reference is buffered on that reference's own next list rather than added to the result collection:
public static class CollectionReferringAccumulator {
private final Class<?> _elementType;
private final Collection<Object> _result;
/**
* A list of {@link CollectionReferring} to maintain ordering.
*/
private List<CollectionReferring> _accumulator = new ArrayList<CollectionReferring>();
public void add(Object value)
{
if (_accumulator.isEmpty()) {
_result.add(value);
} else {
CollectionReferring ref = _accumulator.get(_accumulator.size() - 1);
ref.next.add(value);
}
}
public Referring handleUnresolvedReference(UnresolvedForwardReference reference)
{
CollectionReferring id = new CollectionReferring(this, reference, _elementType);
_accumulator.add(id);
return id;
}
public void resolveForwardReference(Object id, Object value) throws IOException
{
Iterator<CollectionReferring> iterator = _accumulator.iterator();
// Resolve ordering after resolution of an id. This mean either:
// 1- adding to the result collection in case of the first unresolved id.
// 2- merge the content of the resolved id with its previous unresolved id.
Collection<Object> previous = _result;
while (iterator.hasNext()) {
CollectionReferring ref = iterator.next();
if (ref.hasId(id)) {
iterator.remove();
previous.add(value);
previous.addAll(ref.next);
return;
}
previous = ref.next;
}
throw new IllegalArgumentException("Trying to resolve a forward reference with id [" + id
+ "] that wasn't previously seen as unresolved.");
}
}
Resolution is a linear search: resolveForwardReference walks the pending list from the front, comparing the resolved id against every pending entry until it finds a match, then merges that entry's buffered next list into its predecessor with previous.addAll(ref.next). Both the search and the merge are unbounded by anything except the number of references the document contains, and neither is memoized. A document that first emits N references and then defines those same ids in reverse order makes each resolution match the last pending entry, so the scan traverses the whole remaining list every time and the buffered values are copied repeatedly. The total cost is roughly N * (N + 1) / 2 id comparisons for an input that grows only linearly. At 2,000 references a run performs 2,003,000 id comparisons, and the work grows fourfold each time the attacker doubles the document size, while the JSON itself stays flat, small and free of deep nesting or unusual syntax.
MapDeserializer carries the same design for map values in its own MapReferringAccumulator, with an identical _accumulator list and the same linear scan in its resolveForwardReference method, so identity-enabled maps are affected in exactly the same way.
Exploitation requires an application that deserializes attacker-influenced JSON into a collection or map whose value type enables object identity. No authentication, no deep nesting and no oversized payload are needed: the request is an ordinary flat array or object, and the cost is paid by the worker thread handling it, so a modest number of concurrent requests can exhaust a request-time or worker-capacity budget. Confidentiality and integrity are not affected.
This vulnerability was introduced in 2014 with jackson-databind 2.4.0.
Steps to Reproduce
1. Add jackson-databind 2.15.4 (or 2.13.5, or 2.14.3) to a test project.
2. Define a value type that enables object identity on a property, and give the id type an equals implementation that counts calls so the work is measured deterministically rather than by timing:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public static class Node {
public CountingId id;
public String name;
}
3. Build a shallow JSON array that first lists N bare object ids as references, then defines those same N objects in reverse order, for example ["0","1","2", ... ,{"id":"2", ...},{"id":"1", ...},{"id":"0", ...}].
4. Deserialize it into a list of the value type:
List<Node> result = new ObjectMapper()
.readValue(json, new TypeReference<List<Node>>() { });
5. Read the equals counter. With N of 2,000 the run reports 2,003,000 id comparisons; doubling N to 4,000 quadruples the count, confirming quadratic growth against a linearly growing input. A control document in which every reference is already resolved performs no comparisons in this path at all.
Mitigation
The affected 2.13.x, 2.14.x, and 2.15.x release lines are End-of-Life and will not receive community updates addressing this issue. Branch status is published on the project's own Jackson Releases page.
Users of the affected components should apply one of the following mitigations:
- Upgrade jackson-databind to a currently supported release containing the fix, such as 2.18.11 or later.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.
Credits
- Daniel Birtwhistle (finder)
