From ef233a75920f29087263e391ee60f9fbcb55c213 Mon Sep 17 00:00:00 2001 From: Tobias Neitzel Date: Thu, 16 May 2024 22:33:41 +0200 Subject: [PATCH] Add support for --objid during guess operation --- CHANGELOG.md | 1 + .../rmg/networking/RMIRegistryEndpoint.java | 6 ++ .../tneitzel/rmg/operations/Dispatcher.java | 59 +++++++++++++------ src/eu/tneitzel/rmg/utils/IUnknown.java | 16 +++++ src/eu/tneitzel/rmg/utils/UnicastWrapper.java | 4 +- 5 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 src/eu/tneitzel/rmg/utils/IUnknown.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ddd409..8dfbac9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Add [plugin template](/plugins/template) * Add [Quartz Scheduler plugin](/plugins/quartz-scheduler) * Add [Quartz Scheduler container](/docker/quartz-server) +* Add `--objid` support for guess operation ### Changed diff --git a/src/eu/tneitzel/rmg/networking/RMIRegistryEndpoint.java b/src/eu/tneitzel/rmg/networking/RMIRegistryEndpoint.java index e3290ad..c841bd8 100644 --- a/src/eu/tneitzel/rmg/networking/RMIRegistryEndpoint.java +++ b/src/eu/tneitzel/rmg/networking/RMIRegistryEndpoint.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.io.InvalidClassException; +import java.io.StreamCorruptedException; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.UnmarshalException; @@ -254,6 +255,11 @@ else if (e instanceof UnmarshalException && e.getMessage().contains("Transport r throw (UnmarshalException)e; } + else if (e instanceof UnmarshalException && ExceptionHandler.getCause(e) instanceof StreamCorruptedException) + { + throw (UnmarshalException)e; + } + if( cause instanceof ClassNotFoundException ) { ExceptionHandler.lookupClassNotFoundException(e, cause.getMessage()); diff --git a/src/eu/tneitzel/rmg/operations/Dispatcher.java b/src/eu/tneitzel/rmg/operations/Dispatcher.java index 30a8162..c450985 100644 --- a/src/eu/tneitzel/rmg/operations/Dispatcher.java +++ b/src/eu/tneitzel/rmg/operations/Dispatcher.java @@ -26,6 +26,7 @@ import eu.tneitzel.rmg.io.WordlistHandler; import eu.tneitzel.rmg.networking.RMIEndpoint; import eu.tneitzel.rmg.networking.RMIRegistryEndpoint; +import eu.tneitzel.rmg.utils.IUnknown; import eu.tneitzel.rmg.utils.RMGUtils; import eu.tneitzel.rmg.utils.RemoteObjectWrapper; import eu.tneitzel.rmg.utils.RogueJMX; @@ -33,6 +34,7 @@ import eu.tneitzel.rmg.utils.YsoIntegration; import javassist.CannotCompileException; import javassist.NotFoundException; +import sun.rmi.server.UnicastRef; /** * The dispatcher class contains all method definitions for the different rmg actions. It obtains a reference @@ -46,6 +48,7 @@ * * @author Tobias Neitzel (@qtc_de) */ +@SuppressWarnings("restriction") public class Dispatcher { private ArgumentHandler p; @@ -86,37 +89,59 @@ private void obtainBoundNames() throws NoSuchObjectException * The result is stored within an object attribute. * * It was observed that using --serial-version-uid option can cause an invalid transport return code - * exception. This seems to be some kind of race condition and cannot be reproduced reliably. The lookup - * operation on the RMI registry does pass only this UnmarshalException to the caller. If this is the case, - * we just retry a few times. + * exception. This seems to be some kind of race condition and cannot be reproduced reliably. It seems + * that RMI / Java does not clear the ObjectInput stream when reading an unknown class from it. The remaining + * bytes are left within the stream. Since RMI uses connection pooling, the next operation encounteres the + * invalid bytes and fails. If this is the case, we just retry a few times. * * @throws java.rmi.NoSuchObjectException is thrown when the specified RMI endpoint is not an RMI registry */ private void obtainBoundObjects() throws NoSuchObjectException { - int retryCount = 0; - - if (boundNames == null) + if (RMGOption.TARGET_OBJID.notNull()) { - obtainBoundNames(); - } + RMIEndpoint ep = getRMIEndpoint(); + ObjID objid = RMGUtils.parseObjID(RMGOption.TARGET_OBJID.getValue()); - while (retryCount < 5) - { try { - remoteObjects = getRegistry().lookupWrappers(boundNames); - return; + UnicastRef ref = ep.getRemoteRef(objid); + remoteObjects = new RemoteObjectWrapper[] { UnicastWrapper.fromRef(ref, IUnknown.class) }; } - catch (java.rmi.UnmarshalException e) + catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { - retryCount += 1; + ExceptionHandler.internalError("obtainBoundObjects", "reflection"); } - catch (Exception e) + } + + else + { + int retryCount = 0; + + if (boundNames == null) { - ExceptionHandler.unexpectedException(e, "lookup", "operation", true); + obtainBoundNames(); + } + + while (retryCount < 5) + { + try + { + remoteObjects = getRegistry().lookupWrappers(boundNames); + return; + } + + catch (java.rmi.UnmarshalException e) + { + retryCount += 1; + } + + catch (Exception e) + { + ExceptionHandler.unexpectedException(e, "lookup", "operation", true); + } } } } @@ -157,7 +182,7 @@ public RMIEndpoint getRMIEndpoint() int port = RMGOption.TARGET_PORT.require(); String host = RMGOption.TARGET_HOST.require(); - this.createMethodCandidate(); + createMethodCandidate(); return new RMIEndpoint(host, port); } diff --git a/src/eu/tneitzel/rmg/utils/IUnknown.java b/src/eu/tneitzel/rmg/utils/IUnknown.java new file mode 100644 index 0000000..05cb1ee --- /dev/null +++ b/src/eu/tneitzel/rmg/utils/IUnknown.java @@ -0,0 +1,16 @@ +package eu.tneitzel.rmg.utils; + +import java.rmi.Remote; + +/** + * IUnknown is a dummy interface that is used when performing method guessing with + * a manually specified ObjID. The original implementation required an RemoteObjectWrapper, + * which is basically a wrapper around a remote object. When guessing on ObjID, we only + * have a remote ref. To make a wrapper out of it, we need to specify an interface that + * the ref implements. IUnknown is used for this purpose. + * + * @author Tobias Neitzel (@qtc_de) + */ +public interface IUnknown extends Remote +{ +} diff --git a/src/eu/tneitzel/rmg/utils/UnicastWrapper.java b/src/eu/tneitzel/rmg/utils/UnicastWrapper.java index d8ed51d..2e3771e 100644 --- a/src/eu/tneitzel/rmg/utils/UnicastWrapper.java +++ b/src/eu/tneitzel/rmg/utils/UnicastWrapper.java @@ -150,6 +150,8 @@ public String[] getDuplicateBoundNames() * the specified interface and uses a RemoteObjectInvocationHandler to forward method invocations to * the specified RemoteRef. * + * The boundname property that is part of each UnicastWrapper is set to the ObjID of the remote object. + * * @param unicastRef UnicastRef to the targeted RemoteObject * @param intf Interface that is implemented by the RemoteObject * @return UnicastWrapper created from the specified UnicastRef @@ -168,7 +170,7 @@ public static UnicastWrapper fromRef(UnicastRef unicastRef, Class intf) throw RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(unicastRef); Remote remoteObject = (Remote)Proxy.newProxyInstance(intf.getClassLoader(), new Class[] { intf }, remoteObjectInvocationHandler); - return new UnicastWrapper(remoteObject, null, unicastRef); + return new UnicastWrapper(remoteObject, unicastRef.getLiveRef().getObjID().toString(), unicastRef); } /**