A lightweight Java agent for preventing attacks against object deserialization like those discussed by @breenmachine and the original researchers @frohoff and @gebl, affecting WebLogic, JBoss, Jenkins and more.
This is the only way to hotpatch your application against this vulnerability. Patching the code is possible (and this package contains SafeObjectInputStream to help), but for many applications, patches will not be available for a long time. Because of the nature of the vulnerability, some applications will have to re-architect their messaging completely.
Build the agent, first:
git clone https://github.com/Contrast-Security-OSS/contrast-rO0.git
cd contrast-rO0
mvn clean package test
The agent, contrast-rO0.jar, is now in the /target directory. Now you need to copy the contrast-rO0.jar into your classpath, and copy the configuration file somewhere you'll remember.
The final step is to add the following JVM options to your server or application:
-javaagent:/path/to/contrast-rO0.jar -DrO0.reporting=false -DrO0.blacklist=true -DrOo.lists=filename
where 'filename' is the path to the default config file.
Now you're safe serializing from known dangerous classes!
If you'd prefer to spot fix your code, possibly because you need to use dangerous classes somewhere, or because you need to minimize stability risk, you can use the SafeObjectInputStream class. To use this class, you'll need to replace calls to ObjectInputStream in your code with calls to SafeObjectInputStream.
When you use SafeObjectInputStream, you can either whitelist or blacklist classes - whitelisting is safer, but blacklisting has lower stability risk. If you want to use this, you don't need the java command line options listed above. Instead, construct your SafeObejctInputStream, tell it if you want to blacklist or whitelist, and add the whitelisted/blacklisted classes to the stream. NOTE: you'll have to repeat this everywhere in your code where you need to implement safe deserialization.
SafeObjectInputStream in
= new SafeObjectInputStream(inputStream, true); // whitelisting mode
// or
// = new SafeObjectInputStream(inputStream, false); // blacklisting mode
in.addToWhitelist(ClassThatIsSafeToDeserialize.getName());
in.addToWhitelist("com.my.SafeDeserializable");
// or
// in.addToBlacklist(ClassThatIsDangerous.class);
// then just use like normal
in.readObject();
The shim can be instructed to report when serialization occurs. This allows you to determine where in your application deserialization is actually occurring - assuming that you exercise the relevant functionality.
To use this, built it just as described in "JVM-wide fix" but, use the following command line options instead:
-javaagent:/path/to/contrast-rO0.jar
When protecting your application, the JVM-wide shim or the SafeObjectInputStream spot fix will throw a SecurityException if there is an attempt to deserialize a dangerous object.
This represents the "last mile" of the exploit chain. The default blacklist contains the only publicly known proofs-of-concept, which are extremely unlikely to be used for legitimate purposes during deserialization.
- org.apache.commons.collections.functors.InvokerTransformer
- org.apache.commons.collections4.functors.InvokerTransformer
- org.apache.commons.collections.functors.InstantiateTransformer
- org.apache.commons.collections4.functors.InstantiateTransformer
- org.codehaus.groovy.runtime.ConvertedClosure
- org.codehaus.groovy.runtime.MethodClosure
- org.springframework.beans.factory.ObjectFactory
Though there likely exist other exploitable classes, they are difficult to find, and likely won't be part of any mass exploitation tool for a while.
However, when they do become available, you can update your configuration file to include these classes in your blacklist. Or, if you want to be more secure, you can use this tool in "reporting" mode to learn what you're deserializing, and then specify a whitelist of the classes that you want to allow. If you go with this approach, and you don't happen to include a dangerous class in your whitelist, then new research finding additional dangerous classes won't affect you.
-DrO0.reporting=true Enable reporting (). Reporting is enabled by
default. Intended for use if we ever use this
package for protecting instead of just reporting.
-DrOo.lists=filename Specify a configuration file. File specifies
classes to include in the whitelist, blacklist,
ignore classes list, or ignore stack trace list.
FIRST MEANING
CHAR
+ If the line starts with +, it’s included in the whitelist.
Attempts to deserialize any classes not whitelisted will
throw a SecurityException. Only has effect when
-DrO0.whitelist is enabled.
- If the line starts with -, it’s included in the blacklist.
Attempts to deserialize these classes will throw a
SecurityException. Only has effect when -DrO0.blacklist is
enabled.
$ If the line starts with $, it’s included in the “ignore
class list”. Classes in this list will not be reported.
Only has effect when -DrO0.ignoreClasses is enabled
@ If the line starts with @, it’s included in the “ignore
in stack list”. When a class is deserialized and the class
specified by this line is in the stack, don’t report. Only
has effect when -DrO0.ignoreStack is enabled.
-DrO0.whitelist=true Enable whitelisting. Classes not included in the
config file as whitelisted will not be allowed to
deserialize. You can enable this at the same time as
blacklisting. See the section on "enabling both
blacklisting and whitelisting at the same time" for
details.
-DrO0.blacklist=true Enable blacklisting. Classes included in the config
file as blacklisted will not be allowed to
deserialized; all other classes will deserialize
normally. You can enable this at the same time as
blacklisting. See the section on "enabling both
blacklisting and whitelisting at the same time" for
details.
-DrO0.ignoreClasses=true Used to quiet down the tool when it’s doing
reporting. Classes or packages listed as ignored
will not be reported upon. Only useful if reporting
is enabled. Can be used in combination with
-DrO0.ignoreStack.
-DrO0.ignoreStack=true Used to quiet down the tool when it’s doing
reporting. If the specified class or package is
in the stack during deserialization, that
deserialize attempt will not be reported upon.
For example, used to stop logging all memcached
events if @com.danga.Memcached is included in the
config file and ignoreStack is enabled., memcached
should be much less pronounced in the logs. Likely
to have noticeable performance impact. Can be
specified in combination with -DrO0.gnoreClasses.
Only useful if reporting is enabled.
-DrO0.outfile=filename Used to control where output from this utility is sent.
If no output is specified, it will go to System.out. If
filename is specified, rO0 will try to write
output to the specified location; if it cannot (for example
if permissions disallow this) then rO0 will instead
fall back to System.out.
This tool allows you to enable both blacklisting and whitelisting at the same time.
If you do so, a class will only be allowed to deserialize if it's both on the
whitelist and not on the blacklist. This is useful if you want to maintain a list
of known dangerous classes that you never want to deserialize as backup in case
someone accidentally or unknowingly adds that same class to the white list.
Although it's not tested on them all, the agent should work well on the following platforms:
- Java 5-8
- OpenJDK/HotSpot, JRockit, IBM
This project is sponsored by Contrast Security and released under the BSD license for developers. It includes contributions from Akamai Technologies.