what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

SimpleRmiDiscoverer 0.1

SimpleRmiDiscoverer 0.1
Posted Jan 5, 2023
Authored by Marcin Wolak | Site github.com

SimpleRmiDiscoverer is a JMX RMI scanning tool for unsecured (without enabled authentication) instances of JAVA JMX. It does not use standard Java RMI/JMX classes like other available tools but rather communicates directly over TCP. The tool is written in Java and is very useful in red teaming operations because JVM is still ubiquitous in corporate environments. It can be executed by unprivileged (non-admin) users.

tags | tool, java, scanner, tcp
systems | unix
SHA-256 | 93daab8314c5a134f408dc5214f71dbb47eac17e499aa7e761104430bd8a7f8f

SimpleRmiDiscoverer 0.1

Change Mirror Download
// Simple JMX scanner extracting data (IP:Port) from RMI Registry and verifying RCE (using MLet) exploitability without credentials.
// Version: 0.1
// Sample use of: https://rabobank.jobs/en/techblog/security-risk-infrastructure/jmx-the-best-buddy-of-every-red-teamer-marcin-wolak/
// Author: Marcin Wolak
// LinkedIn: https://www.linkedin.com/in/marcinwolak/
// Medium: https://marcin-wolak.medium.com/
// Github: https://github.com/marcin-wolak/
// Tested successfuly against JMX enabled on Java x64:
// Java(TM) SE Runtime Environment (build 1.7.0_80-b15) on Windows 10
// Java(TM) SE Runtime Environment (build 1.8.0_202-b08) on Windows 10
// Java(TM) SE Runtime Environment (build 9.0.4+11) on Windows 10
// OpenJDK Runtime Environment (build 11.0.17+8-post-Ubuntu-1ubuntu222.04)
// OpenJDK Runtime Environment (build 17.0.5+8-Debian-2)

import java.net.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.System;
import java.util.*;
import java.io.DataInputStream;
import java.io.ByteArrayInputStream;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.HelpFormatter;


public class SimpleRmiDiscoverer {

// IP Address of the RMI Registry to query (CMD Parameter)
private String regIP;
// TCP Port of the RMI Registry to query (CMD Parameter)
private int regPort;
// Ignore endpoint from RMI Registry to verify exploitability without credentials. Use instead IP of the registry itself and dynamic TCP port from the registry dump (CMD Parameter)
private boolean ignoreEndpoint;
// Extract Host:Port from the RMI Registry without checking RCE exploitability without credentials (CMD Parameter)
private boolean dumpOnly;

// IP Address and TCP port for the endpoint exposed via RMI Registry
private String endPIP;
private int endPPort;

// Sockets for communication with RMI Registry and exposed endpoint
private Socket regS;
private Socket endPS;

//Streams for communication via above sockets
private InputStream in, inEndP;
private OutputStream out, outEndP;

// RMI Handshake data
private byte[] hShake1 ={0x4A,0x52,0x4D,0x49,0x00,0x02,0x4B};
private byte[] hShake2 ={0x00,0x09,0x31,0x32,0x37,0x2E,0x30,0x2E,0x30,0x2E,0x31,0x00,0x00,0x00,0x00};

// Calling Obj
private byte[] hObj ={0x50,(byte)0xac,(byte)0xed,0x00,0x05,0x77,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x44,0x15,0x4d,(byte)0xc9,(byte)0xd4,(byte)0xe6,0x3b,(byte)0xdf,0x74,0x00,0x06,0x6a,0x6d,0x78,0x72,0x6d,0x69};


//Pattern to find in dumped registry (UnicastRef)
private byte[] ucRef = {0x55,0x6e,0x69,0x63,0x61,0x73,0x74,0x52,0x65,0x66};

//Pattern to find in exception dump (Credentials required)
private byte[] credReq = {(byte)0x43,(byte)0x72,(byte)0x65,(byte)0x64,(byte)0x65,(byte)0x6e,(byte)0x74,(byte)0x69,(byte)0x61,(byte)0x6c,(byte)0x73,(byte)0x20,(byte)0x72,(byte)0x65,(byte)0x71,(byte)0x75,(byte)0x69,(byte)0x72,(byte)0x65,(byte)0x64};

// Calling Obj (after modification) on the dynamic port
private byte[] hDObj ={(byte)0x50,(byte)0xac,(byte)0xed,(byte)0x00,(byte)0x05,(byte)0x77,(byte)0x22,(byte)0x6d,(byte)0xf9,(byte)0xa5,(byte)0x39,(byte)0xb4,(byte)0xfa,(byte)0x06,(byte)0xc3,(byte)0xea,(byte)0x8e,(byte)0xa7,(byte)0x0e,(byte)0x00,(byte)0x00,(byte)0x01,(byte)0x85,(byte)0x06,(byte)0x13,(byte)0xa1,(byte)0x4d,(byte)0x80,(byte)0x01,(byte)0xff,(byte)0xff,(byte)0xff,(byte)0xff,(byte)0xf0,(byte)0xe0,(byte)0x74,(byte)0xea,(byte)0xad,(byte)0x0c,(byte)0xae,(byte)0xa8,(byte)0x70};

private String endpIP;
private short enpPort;

//javax.management.remote.rmi.RMIServerImpl_Stub object details collected from RMI registry:
private byte[] rmiStub;
int rmiStubLength;
int eIPlength;
byte [] eIP;
byte [] ePort;

// Constructor. Parameter dumps determines if only dynamic endpoint should be extracted from RMI Registry without checking if JMX is username/password protected
// Ignore detrmines if the IP of endpoint extracted from registry should be ignored and the IP of RMI Registry used instead
public SimpleRmiDiscoverer(String rIP, int rPort, boolean ignore, boolean dump) {
regPort = rPort;
regIP = rIP;
rmiStub = new byte[4096];
ignoreEndpoint = ignore;
dumpOnly = dump;
}

// findArray taken from: https://stackoverflow.com/questions/3940194/find-an-array-inside-another-larger-array
public int findArray(byte[] largeArray, byte[] subArray) {

/* If any of the arrays is empty then not found */
if (largeArray.length == 0 || subArray.length == 0) {
return -1;
}

/* If subarray is larger than large array then not found */
if (subArray.length > largeArray.length) {
return -1;
}

for (int i = 0; i < largeArray.length; i++) {
/* Check if the next element of large array is the same as the first element of subarray */
if (largeArray[i] == subArray[0]) {

boolean subArrayFound = true;
for (int j = 0; j < subArray.length; j++) {
/* If outside of large array or elements not equal then leave the loop */
if (largeArray.length <= i+j || subArray[j] != largeArray[i+j]) {
subArrayFound = false;
break;
}
}

/* Sub array found - return its index */
if (subArrayFound) {
return i;
}

}
}

/* Return default value */
return -1;
}

// Connecting to RMI Registry using IP/TCP pair from CLI
private int connectRegistry()
{
try {
//System.out.println("IP: " + regIP);
regS = new Socket(regIP, regPort);
in = this.regS.getInputStream();
out = this.regS.getOutputStream();
}
catch (Exception e) {
System.out.println("[!] Cannot connect to RMI Registry.");
//e.printStackTrace();
return 0;
}
return 1;
}

// RMI handshake. Common function for handshaking with RMI Registry as well as the endpoint extracted from it
private int rmiHandShake(InputStream inP, OutputStream outP)
{
byte [] buffer = new byte[4096];
try {
outP.write(hShake1, 0, hShake1.length);
int count = inP.read(buffer);
if(count==0)
{
System.out.println("[!] Handshake failed, no date received. Likely not RMI.");
return -1;
}
else
{
if(buffer[0]!=0x4E)
{
System.out.println("[!] Handshake failed, bad response. Likely not RMI.");
return -2;
}
outP.write(hShake2, 0, hShake2.length);
}
}
catch (Exception e) {
System.out.println("[!] Handshake failed, Error in RMI Communication.");
//e.printStackTrace();
return 0;
}
return 1;
}

// Invoking RMIServerImpl_Stub over RMI Registry
private int invokeObject()
{
try {
out.write(hObj, 0, hObj.length);
int rmiStubLength = in.read(rmiStub);
if(rmiStubLength==0)
{
System.out.println("[-] RMIServerImpl_Stub invocation against RMI Registry failed, no response!");
return -1;
}
//String s = new String(rmiStub);
//System.out.println(s);
//Findinf "UnicastRef" in the response from RMI Registry:
int unipos = findArray(rmiStub, ucRef);
if(unipos<1)
{
System.out.println("[-] RMIServerImpl_Stub invocation against RMI Registry failed, Malformed registry dump.");
return -2;
}
//else
// System.out.println("\nThe position is: " + unipos);
eIPlength = (int) rmiStub[unipos+11];
//System.out.println("\nLength of eIP is: " + eIPlength);
eIP = Arrays.copyOfRange(rmiStub,unipos+12,unipos+12+eIPlength);
endPIP = new String(eIP);
//System.out.println("\nThe eIP is: " + endPIP);
ePort = Arrays.copyOfRange(rmiStub,unipos+12+eIPlength,unipos+16+eIPlength);
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(ePort));
endPPort = inputStream.readInt();
//System.out.println(endPPort);
//System.out.println("\nThe ePort is: " + Integer.valueOf(new String(ePort)));
}
catch (Exception e) {
System.out.println("[-] RMIServerImpl_Stub invocation against RMI Registry failed, Other Error.");
// e.printStackTrace();
return 0;
}
return 1;
}

// Connecting to the endpoint extracted from RMI registry or (when ignore option active) to RMI Registry IP and TCP port extracted from this Registry.
private int connectEndPoint(boolean ignoreEndPoint)
{
try {
//System.out.println("End Point IP: " + endPIP);
//System.out.println("End Point Port: " + endPPort);
System.out.println("[+] Connecting to the dynamic Endpoint ... ");
if(!ignoreEndPoint){
endPS = new Socket(endPIP, endPPort);
}
else
{
System.out.println("[+] Ignoring the endpoint exposed on: " + endPIP);
System.out.println("[+] Connecting instead to: " + regIP + " on TCP Port:" + Integer.toString(endPPort));
endPS = new Socket(regIP, endPPort);
}
inEndP = this.endPS.getInputStream();
outEndP = this.endPS.getOutputStream();
}
catch (Exception e) {
// e.printStackTrace();
System.out.println("[-] Connection to the dynamic Endpoint failed!");
return 0;
}
System.out.println("[+] Dynamic Endpoint connected successfuly!");
return 1;
}

// Invoking RMIServerImpl_Stub over the endpoint extracted from RMI registry or (when ignore option active) over RMI Registry IP and TCP port extracted from this Registry.
private int invokeDObject()
{
int unipos = findArray(rmiStub, ucRef);
System.arraycopy(rmiStub, unipos + 16 + endPIP.length(), hDObj, 7, 8);
System.arraycopy(rmiStub, 8, hDObj, 15, 4);
System.arraycopy(rmiStub, 16, hDObj, 23, 4);
byte [] buffer = new byte[4096];
try {
outEndP.write(hDObj, 0, hDObj.length);
int dStubLength = inEndP.read(buffer);
if(dStubLength==0)
{
System.out.println("[-] Invoking RMIServerImpl_Stub on the dynamic Endpoint failed, no response.");
return -1;
}
String s = new String(buffer);
if(s.contains("Credentials required")){
System.out.println("[-] Dynamic Enpoint requires credentials (username and password).");
}
else if(s.contains("UnicastRef")){
System.out.println("[+] Dynamic Enpoint is VULNERABLE to RCE, NO username and password required.");
}
else {
System.out.println("[-] Unknown outcome from invoking RMIServerImpl_Stub on the dynamic Endpoint. See below:");
System.out.println(s);
}
}
catch (Exception e) {
e.printStackTrace();
return 0;
}
return 1;
}

private void closeSocket(Socket inSock){

try{
inSock.close();}
catch (Exception e) {
//e.printStackTrace();
}

}

public static void main(String[] args) throws Exception{

boolean ignore=false;
boolean donly=false;
String IP = "";
long Port = 1099;
Options options = new Options();
options.addOption("h", "help", false, "Prints help for the tool.");
options.addOption("i", "ignore", false, "Uses RMI registry IP for methods invocations. Ignores endpoint from the Registry dump.");
options.addOption("d", "dumponly", false, "Extracting endpoint <host:port> from RMI Registry without checking exploitabilty, which is by default checked.");
options.addOption(OptionBuilder.withLongOpt("host").withDescription("IP of RMI Registry to query.").hasArg().withArgName("RMI HOST IP ").isRequired().create("H"));
options.addOption(OptionBuilder.withLongOpt("port").withDescription("TCP port of RMI Registry to query.").hasArg().withArgName("RMI HOST TCP PORT ").isRequired().create("P"));
//options.addOption("H", "--host", false, "IP of RMI Registry to query.");
//options.addOption("P", "--port", false, "TCP port of RMI Registry.");
CommandLineParser parser = new DefaultParser();

String header = "SimpleRmiDiscoverer extracts JMX host:port endpoint from RMI registry and checks if is exploitable without credentials using MLet.\n\n";
String footer = "\nPlease check my Blog for more details: https://marcin-wolak.medium.com/";

HelpFormatter formatter = new HelpFormatter();


try {
CommandLine cmd = parser.parse(options, args);
if (cmd.hasOption("h")) {
formatter.printHelp("SimpleRmiDiscoverer", header, options, footer, true);
System.exit(0);
}
if (cmd.hasOption("i")) {
ignore = true;
}
if (cmd.hasOption("d")) {
donly = true;
}
if (cmd.hasOption("H")) {
IP = cmd.getOptionValue("H");
}
if (cmd.hasOption("P")) {
try {
Port = Long.valueOf(cmd.getOptionValue("P")); }
catch (Exception e){
System.out.println(e.getMessage());
System.out.println("[!] Parameter -P --port must be a number between 1 and 65535!\n\n");
formatter.printHelp("SimpleRmiDiscoverer", header, options, footer, true);
System.exit(1);
}
if(Port<0 || Port>65535){
System.out.println("[!] Parameter -P --port out of range!\n\n");
formatter.printHelp("SimpleRmiDiscoverer", header, options, footer, true);
System.exit(0);
}

}

} catch (ParseException e){
System.out.println(e.getMessage());
formatter.printHelp("SimpleRmiDiscoverer", header, options, footer, true);
System.exit(1);

}

System.out.println("[+] Approached RMI Registry IP: " + IP);
System.out.println("[+] Approached RMI Registry TCP Port: " + Port);

SimpleRmiDiscoverer sRD = new SimpleRmiDiscoverer(IP, (int) Port, ignore, donly);
sRD.start();
}

public int start() {
// can now access non-static fields
int isConnected = connectRegistry();
if(isConnected == 0){
System.out.println("[-] Aborting Connection to RMI Registry! Quiting!");
closeSocket(regS);
return -1;
}
int isHShaked = rmiHandShake(in,out);
if(isHShaked != 1){
System.out.println("[-] Aborting handShaking with RMI Registry! Quiting!");
closeSocket(regS);
return -2;
}
int isInvoked = invokeObject();
if(isInvoked != 1){
System.out.println("[-] Object Calling Error, RMI Registry! Quiting");
closeSocket(regS);
return -2;
}

System.out.println("[+] RMI Registry contacted successfuly!");
System.out.println("[+] Extracted Endpoint Name / IP Address: " + endPIP);
System.out.println("[+] Extracted Endpoint Dynamic TCP Port: " + endPPort);

closeSocket(regS);

if(dumpOnly)
{
System.out.println("[+] RMI Registry Dump Completed Successfuly!");
System.exit(0);
}

int isEConnected = connectEndPoint(ignoreEndpoint);
if(isEConnected != 1){
System.out.println("[-] Aborting!");
closeSocket(endPS);
return -3;
}
isHShaked = rmiHandShake(inEndP, outEndP);
if(isHShaked != 1){
System.out.println("[-] Cannot handShake with the dynamic Endpoint!");
closeSocket(endPS);
return -2;
}
int isDInvoked = invokeDObject();
if(isDInvoked != 1){
System.out.println("[-] Cannot Invoke RMIServerImpl_Stub on the dynamic Endpoint!");
closeSocket(endPS);
return -4;
}
closeSocket(endPS);
return 1;
}

}

Login or Register to add favorites

File Archive:

January 2023

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Jan 1st
    0 Files
  • 2
    Jan 2nd
    13 Files
  • 3
    Jan 3rd
    5 Files
  • 4
    Jan 4th
    5 Files
  • 5
    Jan 5th
    9 Files
  • 6
    Jan 6th
    5 Files
  • 7
    Jan 7th
    0 Files
  • 8
    Jan 8th
    0 Files
  • 9
    Jan 9th
    18 Files
  • 10
    Jan 10th
    31 Files
  • 11
    Jan 11th
    30 Files
  • 12
    Jan 12th
    33 Files
  • 13
    Jan 13th
    25 Files
  • 14
    Jan 14th
    0 Files
  • 15
    Jan 15th
    0 Files
  • 16
    Jan 16th
    7 Files
  • 17
    Jan 17th
    25 Files
  • 18
    Jan 18th
    38 Files
  • 19
    Jan 19th
    6 Files
  • 20
    Jan 20th
    21 Files
  • 21
    Jan 21st
    0 Files
  • 22
    Jan 22nd
    0 Files
  • 23
    Jan 23rd
    24 Files
  • 24
    Jan 24th
    68 Files
  • 25
    Jan 25th
    22 Files
  • 26
    Jan 26th
    20 Files
  • 27
    Jan 27th
    17 Files
  • 28
    Jan 28th
    0 Files
  • 29
    Jan 29th
    0 Files
  • 30
    Jan 30th
    0 Files
  • 31
    Jan 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Hosting By
Rokasec
close