diff --git a/src/de/qtc/rmg/exceptions/SSRFException.java b/src/de/qtc/rmg/exceptions/SSRFException.java index 97f6c2b..2462de2 100644 --- a/src/de/qtc/rmg/exceptions/SSRFException.java +++ b/src/de/qtc/rmg/exceptions/SSRFException.java @@ -5,4 +5,4 @@ public class SSRFException extends Exception implements Serializable { private static final long serialVersionUID = 1L; -} \ No newline at end of file +} diff --git a/src/de/qtc/rmg/exceptions/UnexpectedCharacterException.java b/src/de/qtc/rmg/exceptions/UnexpectedCharacterException.java index 4461234..c9a0907 100644 --- a/src/de/qtc/rmg/exceptions/UnexpectedCharacterException.java +++ b/src/de/qtc/rmg/exceptions/UnexpectedCharacterException.java @@ -22,4 +22,4 @@ public UnexpectedCharacterException(String message) { super(message); } -} \ No newline at end of file +} diff --git a/src/de/qtc/rmg/internal/RMGOptionGroup.java b/src/de/qtc/rmg/internal/RMGOptionGroup.java index 7d517d4..0a57964 100644 --- a/src/de/qtc/rmg/internal/RMGOptionGroup.java +++ b/src/de/qtc/rmg/internal/RMGOptionGroup.java @@ -62,4 +62,4 @@ public ArgumentGroup addArgumentGroup(ArgumentParser argParser, Operation operat return group; } -} \ No newline at end of file +} diff --git a/src/de/qtc/rmg/internal/RMIComponent.java b/src/de/qtc/rmg/internal/RMIComponent.java index 248cdb8..6393dae 100644 --- a/src/de/qtc/rmg/internal/RMIComponent.java +++ b/src/de/qtc/rmg/internal/RMIComponent.java @@ -47,4 +47,4 @@ public static RMIComponent getByShortName(String shortName) return null; } -} \ No newline at end of file +} diff --git a/src/de/qtc/rmg/operations/MethodGuesser.java b/src/de/qtc/rmg/operations/MethodGuesser.java index d48e6f0..396a209 100644 --- a/src/de/qtc/rmg/operations/MethodGuesser.java +++ b/src/de/qtc/rmg/operations/MethodGuesser.java @@ -4,7 +4,6 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -22,6 +21,8 @@ import de.qtc.rmg.utils.RemoteInvocationHolder; import de.qtc.rmg.utils.SpringRemotingWrapper; import de.qtc.rmg.utils.UnicastWrapper; +import javassist.CannotCompileException; +import javassist.NotFoundException; /** * The MethodGuesser class is used to brute force available remote methods on Java RMI endpoints. It uses @@ -60,6 +61,10 @@ public class MethodGuesser * Furthermore, you need to specify a Set of MethodCandidates that represents the methods you want * to guess. * + * If one of the UnicastWrapper objects within the array is a SpringRemotingWrapper, the set of + * MethodCandidates gets cloned and transformed into a set of RemoteInvocation. Both sets are still + * available and the guessing procedure decides based on the wrapper type which set should be used. + * * @param remoteObjects Array of looked up remote objects from the RMI registry * @param candidates MethodCandidates that should be guessed */ @@ -203,9 +208,23 @@ private UnicastWrapper[] handleKnownMethods(UnicastWrapper[] remoteObjects) else { + List knownMethods = new ArrayList(); RemoteObjectClient knownClient = new RemoteObjectClient(o); - knownClient.addRemoteMethods(RMGUtils.getKnownMethods(o.getInterfaceName())); + for (String method : o.knownEndpoint.getRemoteMethods()) + { + try + { + knownMethods.add(new MethodCandidate(method)); + } + + catch (CannotCompileException | NotFoundException e) + { + Logger.printlnMixedYellowFirst("Internal Error", "- Unable to compile known method with signature: " + method); + } + } + + knownClient.addRemoteMethods(knownMethods); knownClientList.add(knownClient); } } @@ -267,7 +286,8 @@ else if (clientList.size() == 0) /** * This method starts the actual guessing process. It creates a GuessingWorker for each remoteClient in the clientList - * and for each Set of MethodCandidates in the candidateSets. + * and for each Set of MethodCandidates in the candidateSets. If the underlying RemoteObjectWrapper type of a client + * is a SpringRemotingWrapper, the spring remoting compatible SpringGuessingWorker will be used. * * @return List of RemoteObjectClient containing the successfully guessed methods. Only clients containing * guessed methods are returned. Clients without guessed methods are filtered. @@ -428,12 +448,23 @@ public void run() } } + /** + * The SpringGuessingWorker does basically the same as the GuessingWorker, but for spring remoting :) + * + * @author Tobias Neitzel (@qtc_de) + */ private class SpringGuessingWorker implements Runnable { protected String boundName; protected RemoteObjectClient client; protected Set invocationHolders; + /** + * Initialize the spring guessing worker with all the required information. + * + * @param client RemoteObjectClient to the targeted remote object + * @param invocationHolders set of RemoteInvocationHolders that contain the RemoteInvocations to guess + */ public SpringGuessingWorker(RemoteObjectClient client, Set invocationHolders) { this.client = client; @@ -441,6 +472,12 @@ public SpringGuessingWorker(RemoteObjectClient client, Set list) - */ - public static List getKnownMethods(String className) - { - List knownMethods = new ArrayList(); - - try { - CtClass knownClass = pool.getCtClass(className); - - if( knownClass.isInterface() ) - addKnownMethods(knownClass, knownMethods); - - for(CtClass intf : knownClass.getInterfaces()) { - - if(! isAssignableFrom(intf, "java.rmi.Remote")) - continue; - - addKnownMethods(intf, knownMethods); - } - - } catch(Exception e) { - ExceptionHandler.unexpectedException(e, "translation process", "of known remote methods", false); - } - - return knownMethods; - } - - /** - * Same as the previous addKnownMethods function, but takes the corresponding interface as argument directly. - * This function is called by the previous addKnownMethods function to add the methods. - * - * @param intf Interface class to add methods from - * @param boundName bound name that is using the known class - * @param guessedMethods list of successfully guessed methods (bound name -> list) - */ - public static void addKnownMethods(CtClass intf, List knownMethodCandidates) - { - try { - CtMethod[] knownMethods = intf.getDeclaredMethods(); - - for(CtMethod knownMethod: knownMethods) - knownMethodCandidates.add(new MethodCandidate(knownMethod)); - - } catch(Exception e) { - ExceptionHandler.unexpectedException(e, "translation process", "of known remote methods", false); - } - } - /** * Returns a human readable method signature of a CtMethod. Builtin methods only return the signature in * a non well formatted format. This function is used to display known remote methods as the result of a @@ -1332,6 +1274,15 @@ public static long getSerialVersionUID(InvalidClassException e) return Long.parseLong(message); } + /** + * Convert a CtClass back to an ordinary Class object. This method is intended to be called + * for classes that are known to already exist within the JVM. No compilation is triggered but + * the Class object is simply obtained by Class.forName (including handling for all the edge + * cases). + * + * @param type the CtClass that should be converted back to a Class object + * @return Class associated to the specified CtClass + */ public static Class ctClassToClass(CtClass type) throws ClassNotFoundException, NotFoundException { if (type.isPrimitive()) diff --git a/src/de/qtc/rmg/utils/RemoteInvocationHolder.java b/src/de/qtc/rmg/utils/RemoteInvocationHolder.java index b2238d1..17e5394 100644 --- a/src/de/qtc/rmg/utils/RemoteInvocationHolder.java +++ b/src/de/qtc/rmg/utils/RemoteInvocationHolder.java @@ -6,17 +6,43 @@ import de.qtc.rmg.internal.MethodCandidate; + +/** + * RemoteInvocation objects do not contain all information that MethodCandidates contain. When converting + * a MethodCandidate to a RemoteInvocation, information like the signature or the return value get lost. + * Moreover, two RemoteInvocations can be considered the same when they have a similar name and similar + * method arguments. The return value does not matter. + * + * The RemoteInvocationHolder class is designed to overcome these problems. It tracks the associated + * MethodCandidate to each RemoteInvocation and implements methods that allow to compare RemoteInvocations + * and to filter duplicates. + * + * @author Tobias Neitzel (@qtc_de) + */ public class RemoteInvocationHolder { private RemoteInvocation invo; private MethodCandidate candidate; + /** + * An RemoteInvocationWrapper simply contains a RemoteInvocation and the associated MethodCandidate. + * + * @param invo the RemoteInvocation + * @param candidate the associated MethodCandidate + */ public RemoteInvocationHolder(RemoteInvocation invo, MethodCandidate candidate) { this.invo = invo; this.candidate = candidate; } + /** + * Two RemoteInocationHolders are the same, if their contained RemoteInvocation uses the same + * method name and the same argument types. + * + * @param other Object to compare with + * @return true if the objects can be considered the same + */ public boolean equals(Object other) { if (other instanceof RemoteInvocationHolder) diff --git a/src/de/qtc/rmg/utils/RogueJMX.java b/src/de/qtc/rmg/utils/RogueJMX.java index 29ea49d..e0a48ab 100644 --- a/src/de/qtc/rmg/utils/RogueJMX.java +++ b/src/de/qtc/rmg/utils/RogueJMX.java @@ -225,4 +225,4 @@ public RMIConnection newClient(Object credentials) throws IOException return conditionalForward(credentials, "Authentication failed!"); } -} \ No newline at end of file +} diff --git a/src/de/qtc/rmg/utils/SpringRemotingWrapper.java b/src/de/qtc/rmg/utils/SpringRemotingWrapper.java index baf4fa7..97c5305 100644 --- a/src/de/qtc/rmg/utils/SpringRemotingWrapper.java +++ b/src/de/qtc/rmg/utils/SpringRemotingWrapper.java @@ -10,6 +10,7 @@ import de.qtc.rmg.internal.ExceptionHandler; import de.qtc.rmg.internal.MethodCandidate; import de.qtc.rmg.operations.RemoteObjectClient; +import de.qtc.rmg.plugin.IResponseHandler; import de.qtc.rmg.plugin.PluginSystem; import de.qtc.rmg.plugin.ReturnValueProvider; import javassist.CannotCompileException; @@ -36,11 +37,21 @@ public class SpringRemotingWrapper extends UnicastWrapper private static String remotingInterfaceName; + /** + * Within it's constructor, SpringRemotingWrapper already sends an RMI call to the server using the getTargetInterfaceName method. + * This is required to tell remote-method-guesser what the underlying interface class is. This information is displayed within the + * enum output and used for identifying potentialy known endpoints. + * + * @param remoteObject the spring remoting remoteObject obtained from the registry + * @param boundName the boundName that is associated with the remoteObject + * @param ref a UnicastRef that can be used to call methods on the remoteObject + */ public SpringRemotingWrapper(Remote remoteObject, String boundName, UnicastRef ref) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { super(remoteObject, boundName, ref); ReturnValueProvider respHandler = new ReturnValueProvider(); + IResponseHandler cachedHandler = PluginSystem.getResponseHandler(); PluginSystem.setResponeHandler(respHandler); RemoteObjectClient client = new RemoteObjectClient(this); @@ -48,6 +59,8 @@ public SpringRemotingWrapper(Remote remoteObject, String boundName, UnicastRef r remotingInterfaceName = (String)respHandler.getValue(); knownEndpoint = KnownEndpointHolder.getHolder().lookup(remotingInterfaceName); + + PluginSystem.setResponeHandler(cachedHandler); } /** @@ -98,6 +111,12 @@ public static MethodCandidate getInvokeMethod() return methodInvoke; } + /** + * Determines whether the method to call is a known spring remoting method, that needs to be processed by the + * remoting wrapper itself, or whether it is an RMI method implemented by the underlying object. + * + * @return true if the method needs to be dispatched using spring remoting + */ public boolean isRemotingCall(MethodCandidate targetMethod) { long targetHash = targetMethod.getHash(); @@ -110,11 +129,24 @@ public boolean isRemotingCall(MethodCandidate targetMethod) return true; } + /** + * Return the interface name of the underlying interface class that can be accessed via spring remoting. + * + * @return interface name implemented by the underlying remote object + */ public String getInterfaceName() { return remotingInterfaceName; } + /** + * Prepare a RemoteInvocation object from a MethodCandidate and the user specified arguments. The resulting + * object can be passed to the spring remoting endpoint in order to call the specified MethodCandidate. + * + * @param targetMethod method that should be called via spring remoting + * @param args arguments that should be used for the call + * @return RemoteInvocation that can be passed to the spring remoting server + */ public static RemoteInvocation buildRemoteInvocation(MethodCandidate targetMethod, Object[] args) { RemoteInvocation invo = new RemoteInvocation(); @@ -152,6 +184,12 @@ public static RemoteInvocation buildRemoteInvocation(MethodCandidate targetMetho return invo; } + /** + * Transform a set of MethodCandidate to a set of RemoteInvocationHolders. + * + * @param candidates v set of MethodCandidate to transform + * @return set of RemoteInvocationHolders + */ public static Set getInvocationHolders(Set candidates) { Set invocationHolderSet = new HashSet(); @@ -165,7 +203,6 @@ public static Set getInvocationHolders(Set getInvocationHolders(Set