Package eu.tneitzel.rmg.operations
Class RegistryClient
- java.lang.Object
-
- eu.tneitzel.rmg.operations.RegistryClient
-
public class RegistryClient extends Object
The RMI registry is a well known RMI object with publicly known method definitions. Loosely spoken, it can be compared to DNS, as it maps bound names to available RemoteObjects. RMI servers use the exposed remote methods bind, rebind and unbind to create, change or delete entries within the registry. Clients use the list and lookup calls to list the available bound names and to obtain RemoteObjects. As the registry exposes well known remote methods, it can be used for deserialization and codebase attacks. With JEP290, serialization filters were implemented for the RMI registry, but the filters were not that strict as for the DGC. Since the registry is a more complex service than the DGC, it is necessary to define a wider range of accepted classes, which lead to filter bypasses in the past. Concerning codebase attacks, the registry uses a SecurityManager that allows outbound connections by default. If an RMI registry is run with useCodebaseOnly set to false, classes should always be loadable from a remote endpoint. However, what can be done from there depends on the situation. Using the bind, rebind and unbind methods of the registry is usually only allowed from localhost. However, this restriction is not present in some cases and in others it may be bypassed using CVE-2019-2684. This class lets you perform all the above mentioned techniques and includes enumeration methods to identify vulnerable endpoints automatically.- Author:
- Tobias Neitzel (@qtc_de)
-
-
Constructor Summary
Constructors Constructor Description RegistryClient(RMIEndpoint rmiEndpoint)Create a new RegistryClient.
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description voidbindObject(String boundName, Object payloadObject, boolean localhostBypass)Invokes the bind method on the RMI endpoint.voidcodebaseCall(Object payloadObject, String regMethod, boolean localhostBypass)Invokes the specified registry method with a user defined payload object, annotated with a user defined codebase.voidenumCodebase(boolean marshal, String regMethod, boolean localhostBypass)Attempts to determine the setting of useCodebaseOnly.booleanenumerateStringMarshalling()Determines the String marshalling behavior of the RMI server.voidenumJEP290Bypass(String regMethod, boolean localhostBypass, boolean marshal)Determines whether the server is vulnerable to known RMI registry whitelist bypasses.voidenumLocalhostBypass()Enumerates whether the server is vulnerable to CVE-2019-268.voidgadgetCall(Object payloadObject, String regMethod, boolean localhostBypass)Invokes the specified registry method with a user defined payload object.static ObjectprepareRMIServerImpl(String host, int port)Generates an RMIServerImpl_Stub as it is usually used by JMX instances.voidrebindObject(String boundName, Object payloadObject, boolean localhostBypass)Invokes the rebind method on the RMI endpoint.voidunbindObject(String boundName, boolean localhostBypass)Invokes the unbind method on the RMI endpoint.
-
-
-
Constructor Detail
-
RegistryClient
public RegistryClient(RMIEndpoint rmiEndpoint)
Create a new RegistryClient.- Parameters:
rmiEndpoint- associated RMIEndpoint.
-
-
Method Detail
-
bindObject
public void bindObject(String boundName, Object payloadObject, boolean localhostBypass)
Invokes the bind method on the RMI endpoint. The used bound name can be specified by the user, and the bound RemoteObject is an instance of RMIServerImpl_Stub by default. This is a class that is used by JMX and should therefore be available on most RMI servers. Furthermore, JMX is a common RMI technology and binding this stub is probably most useful. The bind operation can also be performed using CVE-2019-268, which may allows bind access from remote hosts. The payload object (RMIServerImpl_Stub by default) is not created by the method itself but taken from the arguments. Users can use rmg's PluginSystem to bind different objects. The payload object is generated by a class that implements the IPayloadProvider interface.- Parameters:
boundName- the bound name that will be bound on the registrypayloadObject- the remote object to bind to the registrylocalhostBypass- whether to use CVE-2019-268 for the bind operation
-
rebindObject
public void rebindObject(String boundName, Object payloadObject, boolean localhostBypass)
Invokes the rebind method on the RMI endpoint. Basically the same as the bind method that was already described above.- Parameters:
boundName- the bound name that will be rebound on the registrypayloadObject- the remote object that is bind to the registrylocalhostBypass- whether to use CVE-2019-268 for the rebind operation
-
unbindObject
public void unbindObject(String boundName, boolean localhostBypass)
Invokes the unbind method on the RMI endpoint. If successful, the specified bound name should disappear from the registry.- Parameters:
boundName- the bound name that will be deleted from the registrylocalhostBypass- whether to use CVE-2019-268 for the unbind operation
-
enumCodebase
public void enumCodebase(boolean marshal, String regMethod, boolean localhostBypass)Attempts to determine the setting of useCodebaseOnly. This is done by sending an Integer object during a RMI call, that is annotated with a malformed location URL. When RMI servers call readObject, the corresponding MarshalInputStream always tries to obtain the location of the object. In case of useCodebaseOnly=true, the location is then simply ignored afterwards. However, when useCodebaseOnly is set to false, the location is used to construct a URLClassLoader, which throws an exception on encountering an invalid URL. The problem is, that the only registry method that can be invoked from remote and accepts arguments is the lookup method. This one is also used by default during the operation, but it has the downside of expecting a String as argument. In mid 2020, there was a RMI patch that changed the behavior how RMI servers unmarshal the String type. A patched server does no longer use readObject to unmarshal String values and the class annotation is always ignored. This makes this enumeration technique non functional for most recent RMI servers. When scanning an RMI registry on localhost, the user can use --reg-method to use a different registry method (e.g. bind) for the operation. Furthermore, using --localhost-bypass may allows using other registry methods also from remote.- Parameters:
marshal- indicates whether the registry server uses readObject() to unmarshal Strings (true)regMethod- the registry method to use for the operation (lookup|bind|rebind|unbind)localhostBypass- whether to use CVE-2019-268 for the operation
-
enumerateStringMarshalling
public boolean enumerateStringMarshalling()
Determines the String marshalling behavior of the RMI server. This function abuses the fact that RMI servers overwrite the annotateClass method of ObjectOutputStream. Whenever an RMI sever calls readObject, it also attempts to read the objects annotation, which is normally intended to be the client-side codebase (if existent, null otherwise). While the annotation is always a String in ordinary use cases, it is read via readObject again. Therefore, when readObject is used to unmarshal values, you have a second, implicit readObject call. This function passes a class that is unknown to the server as annotation for an Integer object, that is sent as the regular argument. When the server unmarshals via readObject, this should lead to a ClassNotFound exception. Otherwise, the server unmarshals via readString. Notice that sending a String as regular argument is not possible. When using writeObject with String as argument, Java implicitly calls writeString for the actual write process. The writeString method does not add annotations, as the String class is expected to be known by every Java server.- Returns:
- true if the server uses readObject to unmarshal String
-
enumLocalhostBypass
public void enumLocalhostBypass()
Enumerates whether the server is vulnerable to CVE-2019-268. To check this, the localhost bypass is performed on the unbind operation, with an definitely non existing bound name as argument. If the server is vulnerable, it will try to remove the bound name but throw an NotBoundException, as the bound name does not exist. If non vulnerable, an AccessException should occur.
-
enumJEP290Bypass
public void enumJEP290Bypass(String regMethod, boolean localhostBypass, boolean marshal)
Determines whether the server is vulnerable to known RMI registry whitelist bypasses. The method uses the currently most recent bypass technique (https://mogwailabs.de/de/blog/2020/02/an-trinhs-rmi-registry-bypass/) which causes an outbound JRMP connection on success. To avoid a real outgoing connection, the function invokes the bypass with an invalid port value of 1234567. If the bypass is successful, the invalid port number should cause an IllegalArgumentException. A patched server should answer with a RemoteException instead.- Parameters:
regMethod- registry method to use for the calllocalhostBypass- whether to use CVE-2019-268 for the operationmarshal- whether or not the server used readObject to unmarshal String
-
gadgetCall
public void gadgetCall(Object payloadObject, String regMethod, boolean localhostBypass)
Invokes the specified registry method with a user defined payload object.- Parameters:
payloadObject- object to use for the call. Most of the time a ysoserial gadgetregMethod- registry method to use for the calllocalhostBypass- whether to use CVE-2019-268 for the operation
-
codebaseCall
public void codebaseCall(Object payloadObject, String regMethod, boolean localhostBypass)
Invokes the specified registry method with a user defined payload object, annotated with a user defined codebase. The codebase annotation is implemented in the MaliciousOutputStream class and setup within the ArgumentParser.- Parameters:
payloadObject- object to use for the callregMethod- registry method to use for the calllocalhostBypass- whether to use CVE-2019-268 for the operation
-
prepareRMIServerImpl
public static Object prepareRMIServerImpl(String host, int port)
Generates an RMIServerImpl_Stub as it is usually used by JMX instances. The contained TCPEndpoint points to a user controlled address. This can be used for binding a malicious bound name to the RMI registry. Once it is looked up, a JRMP connection is created to the specified TCPEndpoint.- Parameters:
host- listener host for the outgoing JRMP connectionport- listener port for the outgoing JRMP connection- Returns:
- RMIServerImpl_Stub object as used by JMX
-
-