Skip to content

Commit

Permalink
Add comments
Browse files Browse the repository at this point in the history
  • Loading branch information
qtc-de committed Nov 14, 2023
1 parent 298f003 commit 522d868
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 71 deletions.
2 changes: 1 addition & 1 deletion src/de/qtc/rmg/exceptions/SSRFException.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
public class SSRFException extends Exception implements Serializable
{
private static final long serialVersionUID = 1L;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ public UnexpectedCharacterException(String message)
{
super(message);
}
}
}
2 changes: 1 addition & 1 deletion src/de/qtc/rmg/internal/RMGOptionGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@ public ArgumentGroup addArgumentGroup(ArgumentParser argParser, Operation operat

return group;
}
}
}
2 changes: 1 addition & 1 deletion src/de/qtc/rmg/internal/RMIComponent.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ public static RMIComponent getByShortName(String shortName)

return null;
}
}
}
57 changes: 54 additions & 3 deletions src/de/qtc/rmg/operations/MethodGuesser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -203,9 +208,23 @@ private UnicastWrapper[] handleKnownMethods(UnicastWrapper[] remoteObjects)

else
{
List<MethodCandidate> knownMethods = new ArrayList<MethodCandidate>();
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);
}
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -428,19 +448,36 @@ 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<RemoteInvocationHolder> 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<RemoteInvocationHolder> invocationHolders)
{
this.client = client;
this.boundName = client.getBoundName();
this.invocationHolders = invocationHolders;
}

/**
* This function is called when a guessed RemoteInvocation exists. It creates a corresponding log entry and
* saves the candidate within the results map.
*
* @param invoHolder RemoteInvocationHolder that contains the RemoteInvocation that lead to an successful method call
*/
protected void logHit(RemoteInvocationHolder invoHolder)
{
MethodCandidate existingMethod = invoHolder.getCandidate();
Expand All @@ -450,6 +487,13 @@ protected void logHit(RemoteInvocationHolder invoHolder)
client.addRemoteMethod(existingMethod);
}

/**
* Sends the assigned RemoteInvocations to the spring remoting endpoint and inspects the response.
* RemoteInvocations send by the guesser are always malformed. They contain a method name, a list of
* argument types and a list of argument values. The argument values are purposely chosen to not match
* the argument types. This prevents actual method calls that could perform dangerous stuff. However,
* based on the thrown exeception it is still possible to identify existing methods.
*/
public void run()
{
for (RemoteInvocationHolder invocationHolder : invocationHolders)
Expand Down Expand Up @@ -521,6 +565,13 @@ public void run()
}
}

/**
* If an unexpected exception was thrown, this method is called. It prints a warning message to the user,
* but does not interrupt the guessing procedure.
*
* @param invoHolder the RemoteInvocationHolder that caused the exception
* @param e the thrown Exception
*/
private void unexpectedError(RemoteInvocationHolder invoHolder, Exception e)
{
String info = "";
Expand Down
10 changes: 7 additions & 3 deletions src/de/qtc/rmg/operations/RemoteObjectClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import java.util.Iterator;
import java.util.List;

import org.springframework.remoting.support.RemoteInvocation;

import de.qtc.rmg.internal.ExceptionHandler;
import de.qtc.rmg.internal.MethodArguments;
import de.qtc.rmg.internal.MethodCandidate;
Expand Down Expand Up @@ -339,7 +337,13 @@ else if (cause instanceof java.lang.UnsupportedOperationException)
}
}


/**
* Technically the same as the genericCall method, but does not perform any exception handling.
*
* @param targetMethod remote method to call
* @param argumentArray method arguments to use for the call
* @throws All possible encountered exceptions are passed to the caller
*/
public void unmanagedCall(MethodCandidate targetMethod, MethodArguments args) throws Exception
{
rmi.genericCall(null, -1, targetMethod.getHash(), args, false, targetMethod.getName(), remoteRef, null);
Expand Down
19 changes: 19 additions & 0 deletions src/de/qtc/rmg/plugin/ReturnValueProvider.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
package de.qtc.rmg.plugin;

/**
* ReturnValueProvider is a helper class that can be used to capture return values of custom
* RMI calls. Normally these return values are ignored and users can define a custom IResponseHandler
* to process it. For some use cases (e.g. spring remoting) remote-method-guesser also requires
* access to return values. This can be achieved by temporarily using this provider, that stores
* the return value of a call within a static variable.
*
* @author Tobias Neitzel (@qtc_de)
*/
public class ReturnValueProvider implements IResponseHandler
{
private Object value = null;

/**
* Just store the return value within a static class variable.
*
* @param responseObject object returned by the RMI call
*/
public void handleResponse(Object responseObject)
{
value = responseObject;
}

/**
* Obtain the currently set value.
*
* @return currently saved response object
*/
public Object getValue()
{
return value;
Expand Down
2 changes: 1 addition & 1 deletion src/de/qtc/rmg/utils/EmptyWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ public EmptyWrapper(String boundName)
{
super(boundName);
}
}
}
67 changes: 9 additions & 58 deletions src/de/qtc/rmg/utils/RMGUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

import de.qtc.rmg.internal.ExceptionHandler;
import de.qtc.rmg.internal.MethodArguments;
import de.qtc.rmg.internal.MethodCandidate;
import de.qtc.rmg.internal.RMGOption;
import de.qtc.rmg.internal.RMIComponent;
import de.qtc.rmg.io.Logger;
Expand Down Expand Up @@ -657,63 +656,6 @@ private static void addSerialVersionUID(CtClass ctClass, long serialVersionUID)
ctClass.addField(serialID, CtField.Initializer.constant(serialVersionUID));
}

/**
* Helper method that adds remote methods present on known remote objects to the list of successfully guessed methods.
* The known remote object classes are looked up by using the CtClassPool. Afterwards, all implemented interfaces
* of the corresponding CtClass are iterated and it is checked whether the interface extends java.rmi.Remote (this
* is required for all methods, that can be called from remote). From these interfaces, all methods are obtained
* and added to the list of successfully guessed methods.
*
* @param boundName bound name that is using the known class
* @param className name of the class implemented by the bound name
* @param guessedMethods list of successfully guessed methods (bound name -> list)
*/
public static List<MethodCandidate> getKnownMethods(String className)
{
List<MethodCandidate> knownMethods = new ArrayList<MethodCandidate>();

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<MethodCandidate> 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
Expand Down Expand Up @@ -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())
Expand Down
26 changes: 26 additions & 0 deletions src/de/qtc/rmg/utils/RemoteInvocationHolder.java
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/de/qtc/rmg/utils/RogueJMX.java
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,4 @@ public RMIConnection newClient(Object credentials) throws IOException

return conditionalForward(credentials, "Authentication failed!");
}
}
}
Loading

0 comments on commit 522d868

Please sign in to comment.