Skip to content

Commit

Permalink
Implemented Java RMI service detection
Browse files Browse the repository at this point in the history
  • Loading branch information
lokiuox committed Sep 16, 2024
1 parent 4be9797 commit e79069e
Showing 1 changed file with 76 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import com.google.common.collect.ImmutableList;
import com.google.common.flogger.GoogleLogger;
import com.google.common.net.HostAndPort;
import com.google.protobuf.util.Timestamps;
import com.google.tsunami.common.data.NetworkEndpointUtils;
import com.google.tsunami.common.data.NetworkServiceUtils;
Expand All @@ -35,9 +36,16 @@
import com.google.tsunami.proto.TargetInfo;
import com.google.tsunami.proto.Vulnerability;
import com.google.tsunami.proto.VulnerabilityId;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Instant;
import java.util.Arrays;
import java.util.UUID;
import javax.inject.Inject;
import javax.management.MBeanServerConnection;
Expand Down Expand Up @@ -72,7 +80,7 @@ public DetectionReportList detect(
return DetectionReportList.newBuilder()
.addAllDetectionReports(
matchedServices.stream()
.filter(JavaJmxRceDetector::isRmiOrUnknownService)
.filter(JavaJmxRceDetector::isRmi)
.filter(JavaJmxRceDetector::isServiceVulnerable)
.map(networkService -> buildDetectionReport(targetInfo, networkService))
.collect(toImmutableList()))
Expand Down Expand Up @@ -145,11 +153,73 @@ private DetectionReport buildDetectionReport(
* Checks whether the network service is a Java RMI service or unknown.
*
* <p>Tsunami currently runs the port scanner nmap with version detection intensity set to 5,
* which isn't high enough to detect Java RMI services. Therefore we run this detector for
* "java-rmi" services as well as network service whose service name is empty.
* which isn't high enough to detect Java RMI services. Therefore we try to identify the RMI
* service by sending some data and checking the response. This is based on nmap's service probe
* file: https://svn.nmap.org/nmap/nmap-service-probes
*/
private static boolean isRmiOrUnknownService(NetworkService networkService) {
return networkService.getServiceName().isEmpty()
|| NetworkServiceUtils.getServiceName(networkService).equals("java-rmi");
private static boolean isRmi(NetworkService networkService) {
if (NetworkServiceUtils.getServiceName(networkService).equals("java-rmi")) {
return true;
}

// Probe the service
HostAndPort hostAndPort =
NetworkEndpointUtils.toHostAndPort(networkService.getNetworkEndpoint());

try {
Socket socket = new Socket();
socket.connect(
new InetSocketAddress(hostAndPort.getHost(), hostAndPort.getPort()), 10 * 1000);

DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());

// Send probe
byte[] probe = {0x4a, 0x52, 0x4d, 0x49, 0x00, 0x02, 0x4b};
dataOutputStream.write(probe);
dataOutputStream.flush();

// Receive response
byte[] buffer = new byte[1024];
int bytesRead = dataInputStream.read(buffer);
bytesRead = bytesRead == -1 ? buffer.length : bytesRead;

// Close socket after reading
dataInputStream.close();
dataOutputStream.close();
socket.close();

// 0x4e = ProtocolAck
if (buffer[0] != 0x4e) {
return false;
}

// Hostname size, Big Endian
int hostnameOffset = 3;
int hostnameSize = ((buffer[1] & 0xFF) << 8 | (buffer[2] & 0xFF)) & 0xFFFF;

// +2 for 2 null byte
// +2 for 2 bytes for the port
if (hostnameOffset + hostnameSize + 2 + 2 > bytesRead) {
logger.atWarning().log("Data exceeds buffer size");
return false;
}

// Check for 2 null bytes after hostname
if (buffer[hostnameOffset + hostnameSize] != 0x00
|| buffer[hostnameOffset + hostnameSize + 1] != 0x00) {
return false;
}

// Parse hostname
byte[] hostnameBytes =
Arrays.copyOfRange(buffer, hostnameOffset, hostnameOffset + hostnameSize);
String hostname = new String(hostnameBytes, StandardCharsets.UTF_8);

logger.atInfo().log("RMI server detected. Declared hostname: %s", hostname);
return true;
} catch (IOException e) {
return false;
}
}
}

0 comments on commit e79069e

Please sign in to comment.