Package eu.tneitzel.rmg.operations
Class ActivationClient
- java.lang.Object
-
- eu.tneitzel.rmg.operations.ActivationClient
-
public class ActivationClient extends Object
In the old days, it was pretty common for RMI endpoints to use an Activator. An Activator is basically another well known RemoteObject like the registry or the distributed garbage collector. Its purpose was the activation of RemoteObjects, which were not constantly available, but created on demand. Apart from a remote reference, RMI clients also obtained an ActivationID during the lookup. When the RemoteObject was currently available, it was just used in a regular way. However, when the remote object was not available, clients could contact the Activator specifying their ActivationID. The Activator would then take care of the creation of the RemoteObject. The description above is at least what we could collect from the few available documentation. Activator endpoints are pretty uncommon today, but they are still supported by Java RMI. In 2020, there was a removal request started, but up to now, all the required code is still there. From the offensive point of view, an Activator endpoint is interesting, as it is a well known RemoteObject with publicly known remote methods. Whereas JEP290 introduced serialization filters for the registry and the DGC, the Activator was not patched and still accepts arbitrary Java objects during deserialization. This makes it a valuable target for an attacker.- Author:
- Tobias Neitzel (@qtc_de)
-
-
Constructor Summary
Constructors Constructor Description ActivationClient(RMIEndpoint rmiEndpoint)Create a new ActivationClient.
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description voidactivateCall(MethodArguments callArguments, boolean maliciousStream)Implementation of the activate call.voidcodebaseCall(Object payloadObject)Dispatches an activate call with an user specified payload object and a user controlled codebase value.voidenumActivator()Checks whether an activator endpoint is present.voidenumCodebase()Dispatches an activate call using an Integer instead of the actually expected ActivationID.voidgadgetCall(Object payloadObject)Dispatches an activate call using a user specified payload object instead of the expected ActivationID.voidregularActivateCall(Object activationID, boolean force, RemoteRef ref)This function is used for performing regular calls to the RMI Activator.
-
-
-
Constructor Detail
-
ActivationClient
public ActivationClient(RMIEndpoint rmiEndpoint)
Create a new ActivationClient.- Parameters:
rmiEndpoint- associated RMIEndpoint
-
-
Method Detail
-
enumActivator
public void enumActivator()
Checks whether an activator endpoint is present. The Activator has a well known ObjID of 0x01 and supports the method activate. This method just invokes the method on the corresponding ObjID. If no Activator is available (which should be the case in 99% of the cases), the RMI endpoint throws an NoSuchObjectException. Since the presence of an Activator endpoint is that rare, it would unnecessary lengthen the output to also create separate checks for deserialization and codebase vulnerabilities. Therefore, both are also included into this function. By default, the activate call is made with in java.util.HashMap instead of the actually expected ActivationID. It is not a full proof, but if this class is not rejected deserialization filters should not be in place. Afterwards, another call is made that contains an invalid URL as codebase for an Integer object. If this causes a malformed URL exception, useCodebaseOnly is false on the server.
-
enumCodebase
public void enumCodebase()
Dispatches an activate call using an Integer instead of the actually expected ActivationID. Furthermore, then Integer is annotated with an invalid URL as codebase String. If the remote server parses the codebase this will lead to a MalformedURLException.
-
gadgetCall
public void gadgetCall(Object payloadObject)
Dispatches an activate call using a user specified payload object instead of the expected ActivationID.- Parameters:
payloadObject- object that is used during the activate call
-
codebaseCall
public void codebaseCall(Object payloadObject)
Dispatches an activate call with an user specified payload object and a user controlled codebase value. The codebase is actually set by the ArgumentParser during the start of the program.- Parameters:
payloadObject- object that is used during the activate call
-
activateCall
public void activateCall(MethodArguments callArguments, boolean maliciousStream) throws Exception
Implementation of the activate call. Just uses the genericCall function of the RMIEndpoint class, which allows to perform raw RMI calls. The activator is not implemented as a skeleton and already uses the new calling convention. As it only supports a single method, we can hardcode the methodHash into the class.- Parameters:
callArguments- argument array to use for the callmaliciousStream- whether or not to use MaliciousOutputStream, which activates a custom codebase- Throws:
Exception- connection related exceptions are caught, but anything other is thrown
-
regularActivateCall
public void regularActivateCall(Object activationID, boolean force, RemoteRef ref) throws Exception
This function is used for performing regular calls to the RMI Activator. It is used when the RMI server returns an ActivatableRef that needs to be activated. Callers need to obtain the return value (MarshalledObject<? extends Remote>) by registering a ResponseHandler. Notice that the ActivationID is passed as a generic Object argument. This is required, since remote-method-guesser should stay compatible with Java distributions that already removed the activation system. Therefore, we should not use activation system related classed directly.- Parameters:
activationID- the ActivationID of the reference to activateforce- whether to force the activation (do not return cached references)ref- RemoteRef for the Activator remote object- Throws:
Exception- connection related exceptions are caught, but anything other is thrown
-
-