Safeguard
Vulnerability Management

CVE-2022-1471: Inside the SnakeYaml Deserialization RCE

CVE-2022-1471 scored 9.8 CRITICAL under NIST's CVSS calculation — a single YAML tag could hand attackers remote code execution in any Java app parsing untrusted input.

Safeguard Research Team
Research
5 min read

CVE-2022-1471 is a deserialization vulnerability in SnakeYaml, the default YAML parser bundled into a huge share of the Java ecosystem, and it carries one of the more contentious CVSS scores in recent memory. NIST's National Vulnerability Database assigns it a base score of 9.8 CRITICAL under CVSS v3.1 (vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H), while Google, as the CNA that filed the advisory, scored the same flaw 8.3 HIGH (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L) — a split rooted in disagreement over how much privilege and complexity a real exploit actually requires. The bug itself is simple to state: SnakeYaml's default Constructor class, used by every call to new Yaml(), will happily instantiate arbitrary Java types named inside a YAML document, with no allowlist and no restriction. Feed it untrusted YAML and you can hand it a class name that triggers code execution during deserialization. It's tracked under CWE-502 (Deserialization of Untrusted Data) and CWE-20 (Improper Input Validation), was published to NVD on December 1, 2022, and affects every SnakeYaml release before version 2.0, where the fix finally landed. NVD's own references list Kubernetes and PyTorch's Model Server among the downstream projects flagged as affected through the SnakeYaml dependency chain. Here's how the exploit actually works, and what changed in 2.0.

What makes SnakeYaml's default Constructor dangerous?

SnakeYaml ships two ways to parse YAML: new Yaml(), which uses the default Constructor class, and new Yaml(new SafeConstructor()), which restricts deserialization to a fixed set of safe Java types like strings, maps, and lists. The default Constructor was built for convenience — YAML's tag syntax (!!some.java.ClassName) lets a document declare exactly which Java class a node should be deserialized into, and the default Constructor will instantiate essentially any class on the classpath to honor that tag, calling its no-argument constructor and setter methods along the way. NVD's advisory description is direct about the consequence: "SnakeYaml's Constructor() class does not restrict types which can be instantiated during deserialization. Deserializing yaml content provided by an attacker can lead to remote code execution." The danger isn't that Constructor is buggy — it's that it does exactly what it was designed to do, without knowing whether the YAML it's given came from a trusted config file or an attacker-controlled API request.

How does the actual exploit chain work?

The exploit relies on gadget classes already present on the application's classpath — types whose constructors or property setters have side effects useful to an attacker, the same underlying pattern behind well-known Java deserialization gadget chains like those catalogued in the ysoserial project. A YAML payload tagged with a class such as javax.script.ScriptEngineManager can be crafted so that, as SnakeYaml instantiates it and populates its properties, the object's setters trigger a chain ending in script execution or a network callback — all before the application code does anything with the "data" it thought it was just parsing. Because the vulnerable call is new Yaml().load(untrustedInput), the vulnerability doesn't require a special YAML feature flag or misconfiguration; it requires only that an application pass attacker-reachable input into the default, unsafe constructor. That's why NVD's remediation guidance is blunt: use SafeConstructor for any YAML content that isn't fully trusted, or upgrade the library outright.

Why did this affect so many Java projects at once?

SnakeYaml isn't a niche library — it's the transitive default YAML engine pulled in by Spring, Jackson's jackson-dataformat-yaml module, and dozens of other frameworks that most application teams never explicitly declare as a direct dependency. NVD's reference list for CVE-2022-1471 points to advisories from Atlassian, NetApp, and GitHub Security Advisories, along with a Google security research writeup, reflecting how many independent vendors had to separately audit and patch products that bundled SnakeYaml several layers deep. That's the recurring shape of transitive dependency risk: a fix has to propagate through every intermediate library before it reaches the applications actually shipping to users, and a scan of top-level pom.xml or build.gradle files alone won't surface SnakeYaml at all if it only shows up in a dependency's dependency.

What changed in SnakeYaml 2.0's SafeConstructor fix?

SnakeYaml 2.0 changed the safe path from opt-in to the default. Instead of requiring every application to remember to instantiate new Yaml(new SafeConstructor()), the library restructured its constructor hierarchy so that safe, type-restricted behavior is what new Yaml() gives you out of the box, closing the gap where a developer simply reaching for the obvious constructor call ended up with the dangerous one. Upgrading to 2.0 is the fix NVD recommends outright — but it isn't a purely mechanical version bump for every consumer. Any code that intentionally relied on the old default Constructor to deserialize custom Java types from YAML — a legitimate, if risky, pattern in some config-loading code — needs to be reviewed and explicitly migrated to declare which types it actually needs, rather than getting that behavior for free. That migration cost is a large part of why CVE-2022-1471 stayed unpatched in so many dependency trees well after the fixed version shipped.

How Safeguard Helps

A CVE like this is exactly the case where CVSS score alone is a poor triage signal — NIST's 9.8 and Google's 8.3 for the same flaw show even the scoring authorities didn't agree on exploitability, and neither number tells you whether your own code ever calls the unsafe Constructor path in the first place. Safeguard's vulnerability management in ESSCM tracks CVE, CVSS, and EPSS data continuously from NVD, GitHub Security Advisories, and CISA KEV, and layers reachability analysis on top so a finding like this is prioritized based on whether your call graph actually reaches new Yaml() with attacker-influenced input — not just whether a vulnerable SnakeYaml jar sits somewhere in the dependency tree. For cases where an immediate upgrade to 2.0 isn't feasible because of the SafeConstructor migration work involved, Safeguard's mitigation tracking lets teams document a time-boxed risk acceptance with business justification, so the exposure stays visible and owned rather than silently accepted.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.