Skip to content
This repository has been archived by the owner on Aug 10, 2022. It is now read-only.

Unsafe RMI deserialization #23

Open
artem-smotrakov opened this issue May 13, 2021 · 0 comments
Open

Unsafe RMI deserialization #23

artem-smotrakov opened this issue May 13, 2021 · 0 comments

Comments

@artem-smotrakov
Copy link

(after discussing the issue with the project maintainers, agreed to publish the details to make users aware of unsafe RMI deserialization in RemoteEngine)

I noticed that Weka offers a remote object via RMI:

The remote object has methods with not-primitive parameters:

Object executeTask(Task t) throws RemoteException;
Object checkStatus(Object taskId) throws Exception;

RMI uses the default Java serialization mechanism to pass parameters in RMI invocations. A remote attacker can send a malicious serialized object to the above RMI entries. The objects get deserialized without any check on the incoming data. In the worst case, it may let the attacker run arbitrary code remotely. You can find more details about this attack in the following articles

https://mogwailabs.de/en/blog/2019/03/attacking-java-rmi-services-after-jep-290/

https://itnext.io/java-rmi-for-pentesters-part-two-reconnaissance-attack-against-non-jmx-registries-187a6561314d

Unfortunately, I don't see any easy way to fix it because RMI does not allow restricting classes for deserialization. The only idea I have is to set a process-wide filter for deserialization, see JEP 290:

http://openjdk.java.net/jeps/290

As an example, I am attaching DeserializationConfig that helps to set the filter. The method DeserializationConfig.setDeserializationFilterIfNecessary() has to be called before accessing the default serialization mechanism in the JVM. It is important because the filter is loaded only once when the JVM loads classes which implement the default deserialization mechanism (ObjectInputStream and others). For example, you can call the method in the beginning of main(). Or, maybe you can just delete RemoteEngine.

import java.security.Security;

/**
 * This class helps to configure deserialization.
 */
public class DeserializationConfig {

    /**
     * This filter specifies classes that are allowed for deserialization in RMI communications.
     *
     * @see <a href="http://openjdk.java.net/jeps/290">JEP 290: Filter Incoming Serialization Data</a>
     */
    private static final String DEFAULT_DESERIALIZATION_FILTER = String.join(";",
            "weka.**",
            "java.lang.Boolean",  "java.lang.Byte", "java.lang.Character", "java.lang.Double", "java.lang.Enum",
            "java.lang.Float", "java.lang.Integer", "java.lang.Long", "java.lang.Number", "java.lang.Object",
            "java.lang.Short",
            "java.util.*",
            "!*"
    );

    /**
     * Returns a process-wide deserialization filter.
     */
    private static String getSerialFilter() {
        String filter = System.getProperty("jdk.serialFilter");
        if (filter != null) {
            return filter;
        }
        return Security.getProperty("jdk.serialFilter");
    }

    /**
     * Sets a process-wide deserialization filter if it is not already set.
     */
    public static void setDeserializationFilterIfNecessary() {
        String filter = getSerialFilter();
        if (filter == null) {
            System.out.printf("Use the following filter for deserialization:%n%s%n", DEFAULT_DESERIALIZATION_FILTER);
            System.setProperty("jdk.serialFilter", DEFAULT_DESERIALIZATION_FILTER);
        }
    }
}
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant