Coder Social home page Coder Social logo

serialkiller's Introduction

SerialKiller SerialKiller Logo

SerialKiller is an easy-to-use look-ahead Java deserialization library to secure application from untrusted input.

When Java serialization is used to exchange information between a client and a server, attackers can replace the legitimate serialized stream with malicious data. Inspired by this article, SerialKiller inspects Java classes during naming resolution and allows a combination of blacklisting/whitelisting to secure your application.

SerialKiller in action

Disclaimer: This library may (or may not) be 100% production ready yet. Use at your own risk!

How to protect your application with SerialKiller

  1. Download the latest version of the SerialKiller's Jar. Alternatively, this library is also available on Maven Central
  2. Import SerialKiller's Jar in your project
  3. Replace your deserialization ObjectInputStream with SerialKiller
  4. Tune the configuration file, based on your application requirements

Easy, isn't it? Let's look at a few details...

Changes required in your code (step 3)

In your original code, you'll probably have something similar to:

ObjectInputStream ois = new ObjectInputStream(is);
String msg = (String) ois.readObject();

In order to detect malicious payloads or allow your application's classes only, we need to use SerialKiller instead of the standard java.io.ObjectInputStream. This can be done with a one-line change:

ObjectInputStream ois = new SerialKiller(is, "/etc/serialkiller.conf");
String msg = (String) ois.readObject();

The second argument is the location of SerialKiller's configuration file.

Finally, you may want to catch InvalidClassException exceptions to gracefully handle insecure object deserializations. Please note that this library does require Java 8.

Tuning SerialKiller's configuration file (step 4)

SerialKiller config supports the following settings:

  • Refresh: The refresh delay in milliseconds, used to hot-reload the configuration file. Good news! You don't need to restart your application if you change the config file
  • BlackList: A Java regex to define malicious classes. The default configuration file already includes several known payloads so that your application is protected by default against known attacks
  • WhiteList: A Java regex to define classes used by your application. If you can quickly identify a list of trusted classes, this is the best way to secure your application. For instance, you could allow classes in your own package only
  • Profiling: Starting from v0.4, SerialKiller introduces a profiling mode to enumerate classes deserialized by the application. In this mode, the deserialization is not blocked. To protect your application, make sure to use 'false' for this setting in production (default value)
  • Logging: Logging support compatible to native LogManager using the java.util.logging.config.file system property or lib/logging.properties. See Java8 LogManager for more details.

Example of serialkiller.conf

<?xml version="1.0" encoding="UTF-8"?>
<!-- serialkiller.conf -->
<config>
  <refresh>6000</refresh>
  <mode>
    <!-- set to 'false' for blocking mode -->
    <profiling>false</profiling>
  </mode>
  <blacklist>
  <!--Section for Regular Expressions-->
    <regexps>
        <!-- ysoserial's BeanShell1 payload  -->
        <regexp>bsh\.XThis$</regexp>
        <regexp>bsh\.Interpreter$</regexp>
        <!-- ysoserial's C3P0 payload  -->
        <regexp>com\.mchange\.v2\.c3p0\.impl\.PoolBackedDataSourceBase$</regexp>
	    <!-- ysoserial's MozillaRhino1 payload -->
	    <regexp>org\.mozilla\.javascript\..*$</regexp>
        [...]
    </regexps>
    <!--Section for full-package name-->
    <list>
        <!-- ysoserial's CommonsCollections1,3,5,6 payload  -->
        <name>org.apache.commons.collections.functors.InstantiateTransformer</name>
        <name>org.apache.commons.collections.functors.ConstantTransformer</name>
        <name>org.apache.commons.collections.functors.ChainedTransformer</name>
        <name>org.apache.commons.collections.functors.InvokerTransformer</name>
        [...]
    </list>
  </blacklist>
  <whitelist>
    <regexps>
        <regexp>.*</regexp>
    </regexps>
  </whitelist>
</config>

Credits

  • Ironically, SerialKiller uses some Apache Commons libraries (configuration, logging, lang, collections)
  • Thanks to @frohoff and @gebl for their work on unsafe Java object deserialization payloads. Ysoserial is awesome!
  • Pierre Ernst for the original idea around look-ahead java deserialization filters

License

This library has been dual-licensed to Apache License, Version 2.0 and GNU General Public License.

Contributing

  • If you've discovered a bug, please open an issue in Github.
  • Submit a new RB, especially if you're aware of Java gadgets that can be abused by vulnerable applications. Providing a safe default configuration is extremely useful for less security-oriented users.

serialkiller's People

Contributors

ikkisoft avatar mvsavkin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

serialkiller's Issues

Request for source code merging

Hi Luca,

I'd like to incorporate some of the code in https://github.com/wsargent/paranoid-java-serialization/ (the blacklist and whitelist). It won't be a straight mapping -- right now it looks like:

    protected Class<?> resolveClass(ObjectStreamClass desc)
            throws IOException, ClassNotFoundException
    {
        String name = desc.getName();
        logger.fine("resolveClass: resolving " + name);

        // From https://github.com/ikkisoft/SerialKiller by [email protected]

        //Enforce blacklist
        for (Pattern blackPattern : blacklistPatterns) {
            Matcher blackMatcher = blackPattern.matcher(name);
            if (blackMatcher.find()) {
                logger.warning("resolveClass: rejecting blacklisted class " + name);
                String msg = "[!] Blocked by blacklist '"
                        + blackPattern.pattern() + "'. Match found for '" + name + "'";
                throw new InvalidClassException(msg);
            }
        }

        //Enforce whitelist if it exists.
        if (! whitelistPatterns.isEmpty()) {
            boolean safeClass = false;
            for (Pattern whitePattern: whitelistPatterns) {
                Matcher whiteMatcher = whitePattern.matcher(name);
                if (whiteMatcher.find()) {
                    safeClass = true;
                    break;
                }
            }

            if (!safeClass) {
                logger.warning("resolveClass: rejecting class " + name + " not found in whitelist.");
                String msg = "[!] Blocked by whitelist. No match found for '" + name + "'";
                throw new InvalidClassException(msg);
            }
        }

        try {
            logger.fine("resolveClass: accepting class " + name);
            return Class.forName(name, false, latestUserDefinedLoader());
        } catch (ClassNotFoundException ex) {
            Class<?> cl = primClasses.get(name);
            if (cl != null) {
                return cl;
            } else {
                throw ex;
            }
        }
    }

It's static, and doesn't use any additional classes (since it has to be in a hacked java.io.ObjectInputStream itself).

Is this okay? Are there any changes or attribution you'd like?

Package as agent or Byteman

Should be fine if it would be provided as a Java agent or Byteman package so as to inject functionality into the JDK ObjectInputStream

Exception does not distinguish between blacklist and non-whitelist blocking

It is sometimes useful for client code to be able to distinguish between a blacklist match and a white-list non-match, as they may require quite different actions (ie. edit the whitelist or rewrite code to not rely on unsafe classes, or worse...).

I suggest making the words "blacklist" and "non-whitelist" part of the exception message, as a low impact solution.

Which License applies?

As a fix for #1 you changed the licence to Dual-License GPL & Apache License 2.0.

What is unclear (at least to me) is which license applies under which circumstances?

  • Do all licenses apply equally?
  • Can the user/developer choose which license should apply?
  • What are the conditions that allow me to use the software under Apache License?

Could you please clearify this? Right now the unclear license is a major obstacle for using SerialKiller.

Add the ability to specify the expected class in the code

Currently when instantiating the SerialKiller you need to specify the configuration file.
NameOfExpectedClass expectedClass = (NameOfExpectedClass) new SerialKiller(inputstream, "/location/to/serialkiller.conf", NameOfExpectedClass.class ).readObject()

Also if the previously requested feature was added to include a default blacklist you could even have
NameOfExpectedClass expectedClass = (NameOfExpectedClass) new SerialKiller(inputstream, NameOfExpectedClass.class ).readObject()

This would add the "NameOfExpectedClass" to the whitelist so if the developer tried to use this to read a blacklisted class it would still fail ( as blacklist takes priority over whitelist ).

Adding the default blacklist and giving the developer the ability to specify in code the expected class. I think would make the library easier to use and increase adoption.
Currently modifying an xml configuration file that needs to be kept on the filesystem adds extra overhead and limits adoption for environments where ensuring the file is on the filesystem or modifying it is difficult or impossible.

I'm happy to raise a pull request to add the above functionality.

Cache for RegExp to improve performance

With the current implementation, each regexp lookup involves filesystem access

$ inotifywait -mr ./
./ OPEN serialkiller.conf
./ ACCESS serialkiller.conf
./ CLOSE_NOWRITE,CLOSE serialkiller.conf

To improve performances, we could create a cache for all blacklists/whitelists - dropped whenever configuration reloads. Potentially, we could also make that cache static and/or thread-local and dependent on the filename, so that two SerialKillers with the same config share the cache

Wrong/missing blacklist entries

Not that I would count on the blacklist, but I think your entry for the spring vector is wrong.

The piece of code that gives the arbitrary method invocation that is needed is

org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider

and not the ObjectFactory (that just conveniently supplies the instance to call upon). Certainly does not hurt to forbid that, too, but the MethodInvokeTypeProvider alone can ruin your day.

InvalidClassException thrown does not include the class name of the blocked class

It is useful for client code to be able to log extra information when a class is blocked from deserialization, and thus needs to know the class name that was blocked.

The InvalidClassException class has a (public...) field classname that can be used for this purpose, but it is currently not initialized (though it is part of the message string).

Threading issue/race condition

Hi!

Thanks for creating this!

However, during testing I've found some bugs. The static variables in SerialKiller that are assigned in constructor may cause serious race condition and confusing results, if using multiple configurations and multiple threads.

Consider the following test case:

@Test(expected = InvalidClassException.class)
public void testThreadIssue() throws Exception {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();

    try (ObjectOutputStream stream = new ObjectOutputStream(bytes)) {
        stream.writeObject(42);
    }

    try (ObjectInputStream stream = new SerialKiller(new ByteArrayInputStream(bytes.toByteArray()), "src/test/resources/blacklist-all.conf")) { // Blacklist: .*
        // Create a dummy SK with different config
        new SerialKiller(new ByteArrayInputStream(bytes.toByteArray()), "src/test/resources/whitelist-all.conf"); // No blacklist and whitelist: .*

        stream.readObject(); // Now passing...

        fail("All should be blacklisted");
    }
}

PS: I'll be back with a PR later, fixing this. :-)

Best regards,

--
Harald K

Extend black-listing/white-listing check to support Interfaces

It may be interesting to allow users to specify interfaces and block/allow classes implementing those interfaces.

It would require to modify the black-listing/white-listing check, adding some logic around public Class<?>[] getInterfaces().

Not sure if we want to add a specific notation for interfaces in the config file, or simply add those items as the current class names.

Also, a class may implement multiple interfaces thus we need to figure out a good strategy.

New Ideas for features

Hi,

I've recently written a similar library. Since it does not make a lot of sense to have two similar open source libraries I've ported the additional features to SerialKiller.

These features are:

  • More flexibility for blacklist and whitelist rules (AND-, NOT-, OR-expressions, etc.). The API is extensible to allow additional rule types (I have a few ideas for future development: e.g. the class to be deserialized must implement Interface x, ...). This makes it a lot easier to implement a restrictive whitelist.
  • Allow creation of a deserialization policy at runtime. IMHO this makes code more readable since the policy is not in a separate file. Also, currently SerialKiller seems to not accept file paths relative to the classpath - that may be a problem for many developers/organizations.
  • More flexibility when handling deserialization policy violations: log the violation, throw an exception or implement a custom handler.

I've pushed the draft version here: https://github.com/ettisan/SerialKiller/tree/runtime_configuration

I'd appreciate feedback regarding the API, etc. If you think that the features should be part of the mainline SerialKiller let me know - I would then continue to develop this branch towards release quality.

Include Blacklist in Library

Though the blacklist is in the Git repo it is not included in the release JARs. Right now, a project that wants to include SerialKiller has to:

  • include the Jar (e.g. Maven)
  • copy the default configuration file and customize it

This is problematic since when the blacklist in the git repo is changed to include more vulnerable classes they are most likely not transferred to the config file.

I think it would therefore be better to include the blacklist into the JARs. By default the blacklist should be applied to all SerialKiller instances. This way, when the blacklist changes only the Jar has to be updated - the custom configuration file does not have to be modified.

I'm willing to implement this. Please give me a heads up as if you would want to accept such a pull request.

java.lang.IllegalAccessError when instantiating SerialKiller

hi there,

when executing next code I get an error when instantiating SerialKiller class., do you know which is the problem ?

buffer = new ByteArrayInputStream(Base64.decodeBase64(coded.getBytes())); // ObjectInputStream ois = new SerialKiller(buffer, "serialkiller.conf");

the stacktrace:
ERROR [io.undertow.request] (default task-34) UT005023: Exception handling request to /RemoteProcessorWeb/: java.lang.BootstrapMethodError: java.lang.IllegalAccessError: at org.nibblesec.tools.SerialKiller.<init>(SerialKiller.java:59) at com.nte.anthema.framework.text.ObjectToStringCodec.decode(ObjectToStringCodec.java:68) at com.nte.anthema.framework.command.RemoteCommandProcessServlet.decodeString(RemoteCommandProcessServlet.java:198) at com.nte.anthema.framework.command.RemoteCommandProcessServlet.doPost(RemoteCommandProcessServlet.java:59) at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129) at com.nte.anthema.filter.AuthFilter.doFilter(AuthFilter.java:130) at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84) at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131) at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) at io.undertow.server.handlers.DisableCacheHandler.handleRequest(DisableCacheHandler.java:33) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.security.handlers.AuthenticationConstraintHandler.handleRequest(AuthenticationConstraintHandler.java:53) at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64) at io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler.handleRequest(ServletSecurityConstraintHandler.java:59) at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60) at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77) at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50) at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292) at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81) at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138) at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135) at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44) at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44) at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44) at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44) at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44) at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272) at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81) at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104) at io.undertow.server.Connectors.executeRootHandler(Connectors.java:202) at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:805) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) Caused by: java.lang.IllegalAccessError: ... 51 more

Was GPL license intentional?

Seems like a potentially interesting and helpful library, but I'd expect this to be Apache or at least LGPL licensed. Do you really intend this to be GPL? This will prevent a vast majority of developers from being able to use this

SerialKiller creates excessive logfiles if logging enabled

The following code in SerailKiller's constructor is problematic, as it will create a new FileHandler (along with a new file) for each instance. It should probably be noted in the readme that this option is only recommended for single instances, and when profiling is enabled.

        if (config.isLogging()) {
            Handler fileHandler = new FileHandler(config.logFile(), true);
            LOGGER.addHandler(fileHandler);
            LOGGER.setLevel(Level.ALL);
        }

I'm thinking this code may be dropped entirely, and instead be left to user configuration, using the java.util.logging.config.file system property or lib/logging.propertis as described in the LogManager API doc.

Best regards,

--
Harald K

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.