Marshalsec
Marshalsec
Moritz Bechler
<mbechler@eenterphace.org>
May 22, 2017
It’s been more than two years since Chris Frohoff and Garbriel Lawrence
have presented their research into Java object deserialization vulnerabilities
ultimately resulting in what most probably is the biggest wave of remote
code execution bugs in Java history. Research into that matter indicated
that these vulnerabilities are not exclusive to mechanisms as expressive as
Java Serialization or XStream, but some could possibly be applied to other
mechanisms as well. This paper presents an analysis, including exploitation
details, of various open-source Java marshalling libraries that allow(ed)
unmarshalling of arbitrary, attacker-supplied types. It shows that no matter
how this process is performed and what implicit constraints are in place it is
prone to similar exploitation techniques. Most of the described mechanisms,
despite almost all being less expressive than Java Serialization, turned
out to be even more easily exploitable – in several cases JDK standard
library code is sufficient to achieve code execution during the unmarshalling
process.
Disclaimer: All information herein is provided solely for educational
purposes. All referenced vulnerabilities have been disclosed to their re-
spective vendors responsibly. Although all vendors were given plenty of
time, some vulnerabilities might still be unfixed at this time. The author
of this paper, however, believes that it is for the greater good to make this
information more widely available.
1
Contents
1 Introduction 4
2 Tooling 5
3 Marshalling libraries 6
3.1 Bean property based marshallers . . . . . . . . . . . . . . . . . . . . . . 6
3.1.1 SnakeYAML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3.1.2 jYAML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.1.3 YamlBeans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.1.4 Apache Flex BlazeDS . . . . . . . . . . . . . . . . . . . . . . . . 8
3.1.5 Red5 IO AMF . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.1.6 Jackson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.1.7 Castor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.1.8 Java XMLDecoder . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.2 Field based marshallers . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2.1 Java Serialization . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2.2 Kryo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.2.3 Hessian/Burlap . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2.4 json-io . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.2.5 XStream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4 Gadgets/Payloads 16
4.1 Common . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.1.1 Xalan TemplatesImpl . . . . . . . . . . . . . . . . . . . . . . . . 16
4.1.2 Code execution via JNDI references . . . . . . . . . . . . . . . . 17
4.2 com.sun.rowset.JdbcRowSetImpl . . . . . . . . . . . . . . . . . . . . . 18
4.3 java.util.ServiceLoader$LazyIterator . . . . . . . . . . . . . . . . 18
4.4 com.sun.jndi.rmi.registry.BindingEnumeration . . . . . . . . . . . 19
4.5 com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl . . . . . . . 20
4.6 javax.imageio.ImageIO$ContainsFilter . . . . . . . . . . . . . . . . 20
4.7 Commons Configuration JNDIConfiguration . . . . . . . . . . . . . . . 21
4.8 C3P0 JndiRefForwardingDataSource . . . . . . . . . . . . . . . . . . . 21
4.9 C3P0 WrapperConnectionPoolDataSource . . . . . . . . . . . . . . . . 21
4.10 Spring Beans PropertyPathFactoryBean . . . . . . . . . . . . . . . . . 22
4.11 Spring AOP PartiallyComparableAdvisorHolder . . . . . . . . . . . . 22
4.12 Spring AOP AbstractBeanFactoryPointcutAdvisor . . . . . . . . . . 23
4.13 Spring DefaultListableBeanFactory . . . . . . . . . . . . . . . . . . . 23
4.14 Apache XBean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.15 Caucho Resin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.16 javax.script.ScriptEngineManager . . . . . . . . . . . . . . . . . . . 25
4.17 Commons Beanutils BeanComparator . . . . . . . . . . . . . . . . . . . 25
4.18 ROME EqualsBean/ToStringBean . . . . . . . . . . . . . . . . . . . . . 26
4.19 Groovy Expando/MethodClosure . . . . . . . . . . . . . . . . . . . . . . 26
2
4.20 sun.rmi.server.UnicastRef(2) . . . . . . . . . . . . . . . . . . . . . . 27
4.21 java.rmi.server.UnicastRemoteObject . . . . . . . . . . . . . . . . . 27
5 Further interactions 29
5.1 Marshalling Getter Calls . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.2 Java “re”serialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
6 Conclusions 30
3
1 Introduction
With only few exceptions Java marshallers1 provide means to convert their respective
target format into an object graph.2 This allows users to work with structured and
properly typed data and certainly is the most natural way to do so in Java.
Both during marshalling and unmarshalling the marshaller needs to interact with
the source/target objects to retrieve/set their properties. This interaction is most
commonly based on JavaBean conventions meaning that object properties are accessed
through getter (getXyz(), possibly isXyz() for boolean values) and setter methods
(setXyz()). Other mechanisms access the actual Java fields directly. There may also
be a mechanism for an object to produce a custom representation of itself and typically,
for improved space efficiency and/or increased representation capabilities, there are
some type conversions built-in that do not follow these rules.
The clear focus of this paper is the unmarshalling part of the process, as it is much
more likely that an attacker is able to control the input to this process. In section 5
some possible exploitation scenarios for marshalling are shown.
In the majority of cases, during unmarshalling, the expected root object type is known
– after all one might want to do something with the data received. This can be used
to recursively determine property types by using reflection.3 Many implementations,
however, have chosen not to or ignore that expected type altogether. With Java
supporting inheritance and interfaces there is also the desire to allow polymorphism
which means that some kind of type information will need to be embedded in the
representation so that the correct one can be restored.
Giving an attacker the opportunity to specify an arbitrary type to unmarshal into
enables him to invoke a certain set of methods on an object of that type. Clearly the
expectation is that these will be well-behaved – what could possibly go wrong?
Open-source Java marshalling libraries that do – or used to in some cases – allow
arbitrary types by default, either directly or in collections, are:
• SnakeYAML (YAML)
• jYAML (YAML)
• YamlBeans (YAML)
• Apache Flex BlazeDS (AMF4 )
• Red5 IO AMF (AMF)
• json-io (JSON)
• Castor (XML)
1 To spare the confusion with Java’s built-in serialization mechanism, throughout this document
“marshalling” refers to any mechanism to convert from an internal representation to one that can
be transferred or stored.
2 or possibly tree, if the mechanism does not allow references
3 putting aside some type erasure related peculiarities, and that this wasn’t possible at all for collections
4
• Java XMLDecoder (XML)
• Java Serialization (binary)
• Kryo (binary)
• Hessian/Burlap (binary/XML)
• XStream (XML/various)
Jackson is an example of an implementation that normally does honor the actual prop-
erty types. However, its polymorphic unmarshalling support has a mode of operation
that does allow arbitrary types.
2 Tooling
Most of the gadget search has been done using a slightly enhanced version of Seriana-
lyzer.7 Serianalyzer, originally developed for Java deserialization analysis, is a static
bytecode analyzer that traces the (potential) reachability of native methods8 starting
from a set of initial methods. Adjusting these sets to match the interactions that can
be achieved during unmarshalling9 it can be applied to other mechanisms as well.
5 That obviously does not prevent anyone from building a vulnerable mechanism on top of
it: http://stackoverflow.com/questions/17049684/convert-from-json-to-multiple-unknown-
java-object-types-using-gson
6 That’s a bit of an unfair comparison because its compiler has the luxury of knowing the client code.
7 https://github.com/mbechler/serianalyzer/
8 Any actual system interaction has to go through a native method in Java.
9 and possibly eliminating Serializable type checks, as well as the heuristics that are tuned for Java
Serialization
5
3 Marshalling libraries
The different marshalling mechanisms described here all differ in how exactly they
interact with and what checks they perform on the objects to be unmarshalled. The
most fundamental distinction to be made is how they set values on objects, therefore
the following distinguishes between mechanisms that use bean property access and
ones that exclusively use direct field access.
3.1.1 SnakeYAML
10 and possibly many more, this seems like an incredible attack surface
6
3.1.2 jYAML
3.1.3 YamlBeans
YamlBeans does allow to specify a root type but that is not used/checked at all.
YamlBeans has a couple of configuration options, e.g. non-public constructors can be
disallowed or direct field access can be used.
Mitigation
7
3.1.4 Apache Flex BlazeDS
11 the implementation requires getters during marshalling; however, payloads without them can be
constructed using a custom BeanProxy implementation – which is also required to get the proper
property ordering for some types
12 found by Markus Wulftange, who independently discovered the RMIRef (4.20) vector and also
published some details, including application to some other AMF implementations (http://
codewhitesec.blogspot.de/2017/04/amf.html)
8
3.1.6 Jackson
That, however, won’t work with Jackson versions lower than 2.7.0, as Jackson checks
whether there are multiple conflicting setter methods defined, and JdbcRowSetImpl
has three for the ’matchColumn’ property. Jackson version 2.7.0 added some resolution
logic for these scenarios. Unfortunately that resolution logic is buggy: Depending on
the Class->getMethods() order, which is pretty random,19 the check won’t fail as it
should.
Apart from that Jackson can also be reliably exploited using SpringPropFac, SpringB-
FAdv, C3P0RefDS, C3P0WrapDS, as well as RMIRemoteObj if that is applicable in
13 only reported recently, also most of the payloads described here do not apply because of custom
visibility settings
14 This is a special case as it allowed specifying an arbitrary root type via a property.
15 http://wiki.fasterxml.com/JacksonPolymorphicDeserialization
16 This may also be the case when generics are used and type erasure applies.
17 via wrapper objects or through an additional property, using Id.CLASS and Id.MINIMAL_CLASS
18 @JsonTypeInfo on a class is usually harmless as this already implies restriction to subclasses.
19 but cached using a SoftReference, so one might not get another chance, as long as the process is
running
9
the target environment.
Mitigation
3.1.7 Castor
Just for the sake of completeness. This one is known to be extremely dangerous as it
allows arbitrary method as well as constructor calls on arbitrary types:
< new class = " java . lang . ProcessBuilder " >
< string >/ usr / bin / gedit </ string > < method name = " start " / >
</ new >
Mitigation
20 That looks like a bug: a property will be ignored if the declared non-abstract type does not have
a public default constructor even though a subtype might have one. While Castor allows to
construct an URLClassLoader through javax.management.loading.MLet, it is not possible to inject
an instance into a property as the supertypes don’t have public default constructors. If this was
possible, there would even be an exploitable instance in Castor itself.
21 There would be an alternative route through com.sun.rowset.CachedRowSetImpl->addRowSet()
10
3.2 Field based marshallers
Field based marshallers typically offer much less of an attack surface in terms of
method calls made on the objects – some even manage to unmarshal non-collection
objects without calling one at all. As there are almost no objects that could be re-
stored without setting private fields, they do directly mess with the object internals,
which can have undesired side effects. In addition, many types – first and foremost
collections – could not be transported/stored efficently using their runtime represen-
tation. That means that all field based marshallers bundle custom converters22 for
certain types. These converters, or their target types respectively, will often have to
invoke methods on the attacker-provided objects. For example collection insertions
lead to calls of java.lang.Object->hashCode(), java.lang.Object->equals(), and
java.lang.Comparable->compareTo() for the sorted variants. Depending on the im-
plementation, there may be others that can be triggered.
A JRE standard type filtering mechanism was introduced with Java 8u121. Various
user-space filter implementations supporting whitelisting are available.
22 Inthe case of Java Serialization the custom logic is provided by the types themselves.
23 orrather since their publication finally got the attention that it deserved
24 https://github.com/frohoff/ysoserial/
11
3.2.2 Kryo
Mitigation
25 https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-1-
kryo
26 and also things like java.util.zip.ZipFile’s finalizer – (possibly further exploitable) memory
corruption
27 However, that’s not really meant for security purposes and has some side effects, e.g. the types have
12
3.2.3 Hessian/Burlap
Mitigation
these issues, instead they happily provide their users with an instant remote code execution flaw
13
3.2.4 json-io
14
3.2.5 XStream
31 targeting java.beans.EventHandler – published 2013 by Dinis Cruz, Abraham Kang and Alvaro
Muñoz: http://blog.diniscruz.com/2013/12/xstream-remote-code-execution-exploit.html
32 targeting Groovy – published 2016 by Arshan Dabirsiaghi: https://www.contrastsecurity.com/
security-influencers/serialization-must-die-act-2-xstream
33 which also means that finalizers won’t be registered
34 there are some slight differences introduced by additional converters
35 Tested: Commons Beanutils, Hibernate, C3P0, ROME – all published in ysoserial by Chris Frohoff
15
4 Gadgets/Payloads
For testing purposes, generators for all of the described gadget payloads are published
at https://github.com/mbechler/marshalsec/.
4.1 Common
The are a couple of ways to ultimately gain arbitrary code execution in Java. Apart from
system command execution through Runtime->exec(), java.lang.ProcessBuilder
and scripting runtimes, these usually involve defining a class from attacker-supplied
bytecode and at least initializing it. One such scenario would be constructing a
java.net.URLClassLoader with an attacker-provided codebase and initializing a class
from it. Triggering these mechanisms generally requires the ability to perform arbitrary
method calls, so intermediaries are usually required that make the calls triggered by
some interaction.
This class was first used by Adam Gowdiak in 2013 for a sandbox escape and provides
the very rare ability to directly define and initialize classes through supplied Java
bytecode when certain methods are called. Oracle/OpenJDK is bundling a modified
copy of Xalan, so this comes in two flavors com.sun.org.apache.xalan.internal.
xsltc.trax.TemplatesImpl36 and the upstream implementation org.apache.xalan.
xsltc.trax.TemplatesImpl.37
There are some slight, but in this case important, differences between the two. Since
Java 8u45 the JDK version dereferences the transient _tfactory field before reaching
the code that achieves code execution. That means that in order to restore an object
that is usable for our purposes, we either need the ability to set transient fields, to call
an arbitrary constructor, or an unmarshaller that calls readObject(). The original
Xalan implementation does not have that limitation.
Setters for the other required fields are private/protected, so in any case it can be used
only with field based unmarshallers or or bean property based unmarshallers that allow
invoking non-public setters..
To actually trigger the class initialization / code execution most commonly the method
newTransformer() has been used. But it can be triggered through the public get
OutputProperties() (see 5.1 for how that might be exploitable) or the private
getTransletInstance() getter as well.
16
4.1.2 Code execution via JNDI references
38 http://zerothoughts.tumblr.com/post/137769010389/fun-with-jndi-remote-code-injection
39 https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-
Manipulation-To-RCE.pdf
40 potentially also others but these seem way more uncommon
41 or rather com.sun.jndi.rmi.registry.ReferenceWrapper in the RMI case
42 the relevant code can be found in javax.naming.spi.NamingManager->getObjectInstance()
43 see javax.naming.spi.NamingManager->setObjectFactoryBuilder()
17
4.2 com.sun.rowset.JdbcRowSetImpl
Applies to
4.3 java.util.ServiceLoader$LazyIterator
Applies to
18
If the unmarshaller is capable of restoring all the components,51 there even is a standard
library–only chain that leads to direct Iterator dereference without the use of any
proxies:
1. hashCode() on jdk.nashorn.internal.objects.NativeString triggers Native
String->getStringValue().
2. getStringValue() calls java.lang.CharSequence->toString().
3. The toString() of com.sun.xml.internal.bind.v2.runtime.unmarshaller.
Base64Data invokes Base64Data->get().
4. Base64Data->get() triggers a read() from a java.io.InputStream supplied by
a javax.activation.DataSource, com.sun.xml.internal.ws.encoding.xml.
XMLMessage$XmlDataSource is an implementation that supplies a preexisting
one.
5. read()s on javax.crypto.CipherInputStream ultimately call javax.crypto.
Cipher->update().
6. javax.crypto.Cipher->update() leads to chooseFirstProvider() which trig-
gers an arbitrary supplied Iterator.
4.4 com.sun.jndi.rmi.registry.BindingEnumeration
Applies to
19
4.5 com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl
Applies to
4.6 javax.imageio.ImageIO$ContainsFilter
Applies to
20
4.7 Commons Configuration JNDIConfiguration
Applies to
Applies to
Applies to
21
2. The listener calls C3P0ImplUtils->parseUserOverridesAsString() with the
property value. Part of that is hex decoded (stripping the first 22 characters as
well the last) and deserialized (Java).52
3. com.mchange.v2.ser.IndirectlySerialized->getObject() is called if the de-
serialized object implements that interface.
4. com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized is such
an implementation. It will instantiate a class from a remote class path as JNDI
ObjectFactory.53
Applies to
Requires spring-beans and spring-context on the class path. Both types involved
have a default constructor. SimpleJndiBeanFactory does not implement java.io.
Serializable and properties do not have respective getter methods. Spring AOP
provides at least two more types that can replace the PropertyPathFactoryBean.
1. Set the ’targetBeanName’ property of the PropertyPathFactoryBean to the
JNDI URI (see 4.1.2) and ’propertyPath’ to something non-null.
2. Set the ’beanFactory’ property to an object of type SimpleJndiBeanFactory
with the ’shareableResources’ property set to an array containing the JNDI URI.
3. setBeanFactory() will check whether the target bean is a singleton, which it is
as we made it a shareable resource, and call BeanFactory->getBean() with the
bean name.
4. That will call JndiTemplate->lookup(), triggering InitialContext->lookup().
Applies to
Requires spring-aop and aspectj on the class path. Requires no or arbitrary con-
structor call as well as the ability to restore non-java.io.Serializable.
1. Trigger toString() on PartiallyComparableAdvisorHolder.
2. On PartiallyComparableAdvisorHolder->toString() (Advisor & Ordered)
->getOrder() is called.
52 of course you can use a Java deserialization gadget of your liking here, but we don’t need it
53 on its own, not using the default JNDI reference mechanism
22
3. AspectJPointcutAdvisor->getOrder() then invokes AbstractAspectJAdvice
->getOrder().
4. That calls AspectInstanceFactory->getOrder().
5. BeanFactoryAspectInstanceFactory->getOrder() finally calls BeanFactory
->getType().
6. SimpleJndiBeanFactory->getType() triggers the JNDI lookup.
Getting the toString() invocation is not as straightforward as it is with Java deserializa-
tion,54 but still possible. com.sun.org.apache.xpath.internal.objects.XObject
will invoke toString() on the argument to its equals() method. The standard library
collections will, however, only check for equality if the objects’ hash codes match. And
while XObject’s hash code can be set to an arbitrary value by properly choosing its
string value, PartiallyComparableAdvisorHolder does not have a hashCode() imple-
mentation and is producing unpredictable ones. HotSwappableTargetSource comes to
the rescue: it has a fixed hash code and provided another HotSwappableTargetSource
to its equals() method will check their Object-valued ’target’ fields for equality.
Requires spring-aop on the class path. Requires default constructor call or the ability
to restore transient fields as well as the ability to restore non-java.io.Serializable.
1. AbstractPointcutAdvisor->equals() invokes AbstractBeanFactoryPointcut
Advisor->getAdvice().
2. AbstractBeanFactoryPointcutAdvisor->getAdvice() then calls BeanFactory
->getBean().
3. SimpleJndiBeanFactory->getBean() triggers the JNDI lookup.
spring-rce/
23
However, his approach required the use of proxies. Spring object construction can be
triggered using the SpringBFAdv (4.12) or SpringPropFac (4.10) chain described above.
Applies to
24
4.16 javax.script.ScriptEngineManager
Applies to
SnakeYAML (3.1.1)
From the Oracle/OpenJDK standard library. Requires the ability to call arbitrary con-
structors with provided data. Involved types do not implement java.io.Serializable.
1. Construct a java.net.URL object pointing to a remote class path.
2. Construct a java.net.URLClassLoader with that URL.
3. Construct a javax.script.ScriptEngineManager with that ClassLoader.
4. The constructor call invokes the ServiceLoader mechanism for javax.script.
ScriptEngineFactory on the remote class path, ultimately instantiating an
arbitrary remote class implementing that interface.
Applies to
25
4.18 ROME EqualsBean/ToStringBean
Applies to
Applies to
This one has already been used in exploiting XStream (3.2.5).60 Both types do not
implement java.io.Serializable. There are no setters for the properties an attacker
needs to control for successful exploitation. MethodClosure does not have a default
constructor and has a readResolve() and/or readObject() method that throws an
exception (must not be called).61
1. Create a MethodClosure, setting the ’delegate’ and ’owner’ properties to a
java.lang.ProcessBuilder instance set up with command and arguments, set
’method’ to “start”.
2. Create an Expando instance and add the MethodClosure to the ’expandoProper-
ties’ map for the ’hashCode’ key.62
59 or a superclass/interface that contains the getter method that should be called, this can be helpful
as exceptions from a getter will stop execution
60 https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-2-
26
3. Insert into a collection calling hashCode(). Expando invokes the MethodClosure
for hashCode() which invokes ProcessBuilder->start() executing the com-
mand.
4.20 sun.rmi.server.UnicastRef(2)
Applies to
4.21 java.rmi.server.UnicastRemoteObject
Applies to
27
randomized.66 There are three well known object IDs – DGC (2), RMI Activator (1)
and RMI Registry (0). The only one that will always be available is the DGC, the
RMI Registry might if the application is using RMI/JMX, Activator is probably rare.
Depending on the target application’s class loader architecture one might not be able
to directly exploit this as the well-known objects use AppClassLoader and that might
not suffice.
The exported object, however, will be using the thread’s context class loader which was
active when the object was created. In the case of web applications this will usually be
the interesting one. If the attacker is able to leak that object’s identifier, access to that
object and its class loader is possible and can be further exploited.67
28
5 Further interactions
So far we have only been looking at things that happen during unmarshalling, which
means before the control flow returns to the user application. But why would you
unmarshal at all, if you weren’t going to use the data afterwards. Assuming that the
unmarshalled object graph passes potential type checks and other validations, there is
also room for exploits that happen afterwards. An obvious example would be if the
application directly called some method causing unwanted side effects based on the
attacker-supplied object’s (unexpected) state. The ability to inject proxies can break
almost any assumption one might make about an object’s behavior. There are a couple
of more general scenarios that do not require the application to directly interact with a
“bad” type.
68 the
restrictions regarding its transient fields mentioned in 4.1.1 apply, also the required setters are
not public
29
6 Conclusions
The good news is that these mechanisms, more or less obviously – by transporting Java
type information – expose implementation details and therefore are not really suited
for public APIs and only rarely used in these. Some of the described marshallers are
pretty obscure, some seem abandoned, yet for almost all of them exploitable uses could
be found in major projects, in many cases resulting in critical vulnerabilities.
Is this problem limited to Java? Most certainly not.69 Java’s flat class path architec-
ture70 and the sheer size of usual class paths, including but not limited to the standard
library, just offer a lot of surface for exploitation. The availability of “enterprise”
features (JNDI) in the standard library offering remote code execution capabilities
through seemingly innocuous APIs do the rest. Many of the gadgets presented here rely
on JNDI for the remote code execution step. JNDI/RMI has already been hardened by
disallowing remote codebases by default and the author expects JNDI/LDAP to follow
sometime soon. However, this won’t fix the actual issue, which is that this code is reach-
able in the first place, and also will still allow to escalate to Java Serialization (3.2.1)
which might be more expressive/exploitable than the primary mechanism.
When comparing the different mechanisms presented here, it becomes clear that while
there isn’t much of an overlap between gadgets for field- and property-based marshallers,
both can be equally exploitable and their degree of vulnerability depends mostly on
the amount of types being available for an exploit. Restrictions on those can come
from technical requirements, e.g. visibility constraints or constructor requirements,71
the use of runtime type information and, in some cases, a declaration of intent by the
target object. Apart from the faulty implementation in Hessian/Burlap (3.2.3), Java
Serialization (3.2.1) really seems to be the only mechanism that checks for such intent
in the form of java.io.Serializable.
While it might seem a good idea to allow types to declare whether or not they should be
allowed in (un)marshalling, and lifting any such limitation – as most of the mechanism
described here do – is even worse,72 we’ve already seen this horribly go wrong for
Java Serialization (3.2.1). There are multiple reasons for this failure, for one java.io.
Serializable serves two purposes with conflicting needs. Usage in passivation, e.g.
storing some object in a web session, would like to restore things as transparently as
possible, while usage in data transport should minimize side effects.73 Another problem
arises from the fact that this intent is declared through an interface and therefore
inherited, forcing the declaration upon all subtypes.74 Finally it puts the burden of
deciding whether something is safe in general upon somebody who might not see the
69 e.g. Json.NET polymorphic TypeNameHandling would seem like a prime target for doing similar
things in C# – there is a warning in the documentation not to use it carelessly but, as usual,
almost nobody seems to care.
70 modularization techniques like OSGI, JBoss/Wildfly modules and the upcoming Java 9 modules
do indeed make it much more unlikely that an instance is exploitable by implicitly limiting the
accessible types quite dramatically, but are no silver bullet either
71 Kryo (3.2.2) nicely shows how much difference a default constructor requirement can make
72 as then not even people considering the effects of some piece of code can prevent it from being used
in such scenarios
73 an example of this is commons-fileupload’s DiskFileItem
74 Groovy’s MethodClosure could be considered an example of this
30
complete picture – something that might seem safe in one codebase can suddenly, by
complex interactions, become exploitable in another. Coming up with a clean set of
rules what might be safe behavior and what not may not be as easy as it sounds.
Except for cases where one actually would use a class with undesirable behavior,
full type checking of fields/properties/collections from a root type is a quite effective
mitigation for these problems but in many cases would require architectural changes
to user software75 and cannot fully account for polymorphism. Combining this with
the registration of polymorphic types, like e.g. GSON or Jackson with Id.NAME
polymorphism do, seems like a good balance between safety and convenience for many
use cases.
People tend to be looking for someone to blame. Should a hashCode()/equals() or a
property accessor implementation invoke any code with possible side effects?76 While
that might be bad style in general, it may be necessary to get correct behavior in
others. Should an unmarshaller assume that all types follow some implicit contract?
Probably not, but without any form of contract there is not much they could do.
Should developers be more concerned with the security implications of technologies
they use, including actually reading warnings in the documentation, and less with their
convenience? Certainly.
For Java Serialization (3.2.1) we have seen some libraries, namely commons-collections
and groovy, “fixing” gadgets in their code. It’s the author’s opinion that this was a bad
choice, leaving many exploitable instances as the fundamental problem often remained
unfixed. Also, in many cases, it is not even possible to implement such mitigations for
the mechanisms described in this paper as there is no way a type could prevent its own
unmarshalling.
What one should take away is that unmarshalling into objects always77 is a form of
code execution. As soon as you allow an attacker to call into code you don’t even know
what it is going to do, it is very likely that it will take you places you will not like.
No matter, how an unmarshalling mechanism interacts with objects or how “powerful”
it is,78 if it allows unmarshalling into object types which have not explicitly been
selected for such purposes, it is almost certainly exploitable.
The only proper way to fix, is to restrict the availability of types – may that be in
the form of an explicit whitelist, using runtime type information starting from a root
type, or some other indicator – to known good ones. These have to follow the desired
contract of not having any side effects when unmarshalled, in the best case scenario
these would be data objects not containing any logic at all. For practical purposes a
restriction to the types that are actually used seems good enough, usually it’s the ton
of code that you don’t even care about that will get you pwned.
31