Skip to content
Security

jackson-databind CVE-2026-110879: the fix you applied in June did not close it

·9 min read

In June, jackson-databind fixed a way to smuggle a forbidden class into a deserialiser by nesting it inside a generic type. Teams upgraded. On 20 October the project published a bypass of that fix, CVE-2026-110879, rated 9.8. If you upgraded in June and stopped, the versions you moved to are still affected.

This note is for you if you run Java or Kotlin services that use Jackson, which is most of them. Everything is as of 28 October 2026 and every claim links to its source at the end.

The short version

  • CVE-2026-110879 (CWE-502, deserialisation of untrusted data) is an incomplete fix for CVE-2026-54512. Generic type arguments, and the element type of array type IDs, can still carry a class the validator should have refused.
  • Patched: 2.18.12, 2.21.8, 2.22.4 on the 2.x line and 3.1.8 and 3.2.4 on the 3.x line (the 3.x artifact is tools.jackson.core). Affected: 2.10.0 to 2.18.11, 2.19.0 to 2.21.7, 2.22.0 to 2.22.3, 3.0.0 to 3.1.7 and 3.2.0 to 3.2.3.
  • There is no 2.19.x fix. Services on 2.19.x need to move to 2.21.8 or later.
  • The versions that fixed the earlier CVE (2.18.8, 2.21.4 and 3.1.4) are still affected.
  • The 9.8 is the worst case. Whether you are in it depends on how your code and your frameworks use polymorphism, and on what classes are available on the classpath.
Cover page of Jackson: fix incomplete: Assessment & Action PlanFree PDF · 6 pagesJackson: fix incomplete: Assessment & Action PlanThe full version as a PDF for Java service owners: the mechanism and its relationship to CVE-2026-54512, severity decoded, the fixed version per line, exposure by configuration, search and dependency-tree commands, workarounds, hunting and a definition of done.Download the PDF →

How the original bug worked, and how this one gets around it

Jackson can deserialise JSON into a type named inside the JSON itself, a feature called polymorphic typing. Because that lets whoever writes the JSON pick the class, Jackson puts a validator in front of it: a PolymorphicTypeValidator that allows or refuses class names.

In CVE-2026-54512 the validator checked the outer class of a generic type, the part before the angle bracket, and then returned the whole parsed type without checking the type arguments. An attacker could put a refused class inside an allowed container. The June fix added validation of the type parameters.

CVE-2026-110879 is the next gap. The advisory says the new check validated each argument against the outer base type rather than the argument's own declared binding. Under the default validator, an unrelated class inside a generic subtype is accepted even though the same class is refused as a direct type ID. Where a rule such as allowIfBaseType approves the outer type, Jackson swaps in a permissive validator and the embedded arguments are not restricted at all. The element type of a plain array type ID is likewise unchecked once the array's base type is approved.

Who is actually exposed

Not every use of Jackson. The advisory lists the configurations where the bypass applies:

  • Class-name default typing, in Jackson 2.x and 3.x.
  • Annotation-based class-name polymorphism, using the class-name or minimal-class-name type ID on a type, with a default Jackson 3.x JsonMapper.
  • Allow rules that approve an outer base type, which then replace the validator with a permissive one.
  • Array type IDs whose base type is approved, for example under a rule that allows Serializable.
  • Spring Security's legacy Jackson 2 module, and Spring Data Redis 4.0.7's JSON serialiser and the legacy generic Jackson 2 serialiser, which the advisory names.

A service that only maps JSON to plain, fixed types with no polymorphism is not in these cases. Exploitation also needs something useful to instantiate: the advisory's own demonstrations with a standard JDK showed file creation or truncation and outbound requests, and a path to running a process when Spring was on the classpath and the attacker could get the application to load attacker-controlled XML. The more libraries you ship, the more such classes exist.

Things people will get wrong

  • "We patched CVE-2026-54512." You did, and you are still affected.
  • "We are on 2.19." No 2.19.x fix exists; move to 2.21.8 or later.
  • "Our services do not accept untrusted JSON." Data from a cache, a queue, a session store or another service counts if an attacker can write to it. The advisory's workarounds mention preventing attackers from writing serialised cache or session values for that reason.
  • "Disable default typing and we are done." Annotation-based polymorphism on 3.x with the default mapper is also listed.

How we would triage it

This is our judgement. The facts come from the advisory.

  • Find your Jackson versions first. It is quick, and in a large estate it will find services nobody thought used Jackson.
  • Then find the polymorphism. A service with no class-name typing is a lower priority; one with it, taking input from outside your control, is the emergency.
  • Upgrade everywhere anyway, because the fix is a version bump within a line and carrying a known critical in a core library is a bad default. But sequence by exposure.

Step 1: what does each service actually resolve

# Maven
mvn dependency:tree -Dincludes=com.fasterxml.jackson.core:jackson-databind
mvn dependency:tree -Dincludes=tools.jackson.core:jackson-databind

# Gradle
./gradlew dependencies --configuration runtimeClasspath | grep -iE 'jackson-databind'

# A built artifact
unzip -p app.jar META-INF/maven/com.fasterxml.jackson.core/jackson-databind/pom.properties

Resolve from the running build or the deployed artifact, not from the build file. A transitive dependency can pin an older version than the one you declared.

Step 2: find the polymorphism

grep -rnE 'activateDefaultTyping|enableDefaultTyping|Id\.CLASS|Id\.MINIMAL_CLASS|allowIfBaseType|GenericJackson2JsonRedisSerializer' \
  --include=*.java --include=*.kt --include=*.xml --include=*.yml .

Check configuration and framework settings too, not just code. Anything that serialises objects into Redis, a message queue or a session store with Jackson deserves a look.

Step 3: upgrade

  • 2.18.x to 2.18.12. 2.19.x to 2.21.8 or later. 2.21.x to 2.21.8. 2.22.x to 2.22.4.
  • 3.0.x and 3.1.x to 3.1.8. 3.2.x to 3.2.4.
  • Use your build tool's dependency management to force a single version across every module so that a transitive dependency cannot pull an older one back in.

If you cannot upgrade this week

These are the advisory's own suggestions. They reduce exposure; they do not close the flaw.

  • Disable class-name polymorphism for untrusted data.
  • Use narrow subtype rules rather than broad base-type approvals.
  • Stop attackers writing serialised cache or session values.
  • Remove dependencies that carry classes an attacker could use.

What to look for

  • Deserialisation errors that name classes your application does not use, in logs for services that accept JSON from outside.
  • Unexpected files being created or truncated by the application account, and unexpected outbound connections from services that normally make none.
  • New child processes started by a Java service that does not normally start any.

What we do not know yet

  • Whether anyone is exploiting it. The advisory does not say, and we have not read any exploit code.
  • Which of your dependencies bundle their own copy of Jackson. Shaded copies do not appear in a dependency tree.
  • Whether a further bypass follows. The bug has been fixed twice in five months.

The advisory credits the researchers who found and reported the bypass, and the project maintainer who wrote the fix. If you take one thing from this: a validator is an allow-list you are trusting to be complete. Where you can, do not deserialise attacker-chosen types at all.

Read next

Also worth reading

Start with one piece of work.

Give us one piece of work and read access to one system. We map it and come back with what we found, what we would build and what it would take — before any commitment. You decide using our output, not our pitch.

Talk to us