Skip to content

Commit

Permalink
Implemented tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lokiuox committed Oct 24, 2024
1 parent c1c5f34 commit 1465ca4
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 36 deletions.
11 changes: 8 additions & 3 deletions doyensec/detectors/rocketmq_rce_cve_2023_33246/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ plugins {
id 'java-library'
}

description = 'CVE-2023-33246 Tsunami detector plugin.'
description = 'Apache RocketMQ CVE-2023-33246 Tsunami detector plugin.'
group = 'com.google.tsunami'
version = '0.0.1-SNAPSHOT'

Expand Down Expand Up @@ -47,9 +47,11 @@ java {

ext {
tsunamiVersion = 'latest.release'
junitVersion = '4.13'
junitVersion = '4.13.1'
okhttpVersion = '3.12.0'
truthVersion = '1.0.1'
mockitoVersion = '2.28.2'
guiceVersion = '4.2.3'
}

dependencies {
Expand All @@ -58,8 +60,11 @@ dependencies {
implementation "com.google.tsunami:tsunami-proto:${tsunamiVersion}"

testImplementation "junit:junit:${junitVersion}"
testImplementation "org.mockito:mockito-core:${mockitoVersion}"
testImplementation "com.google.truth:truth:${truthVersion}"
testImplementation "com.squareup.okhttp3:mockwebserver:${okhttpVersion}"
testImplementation "com.google.truth.extensions:truth-java8-extension:${truthVersion}"
testImplementation "com.google.truth.extensions:truth-proto-extension:${truthVersion}"
testImplementation "com.squareup.okhttp3:mockwebserver:${okhttpVersion}"
testImplementation "com.google.inject:guice:${guiceVersion}"
testImplementation "com.google.inject.extensions:guice-testlib:${guiceVersion}"
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,15 @@

/** Annotation for {@link RocketMqCve202333246Detector}. */
final class Annotations {
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({PARAMETER, METHOD, FIELD})
@interface OobSleepDuration {}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({PARAMETER, METHOD, FIELD})
@interface OobSleepDuration {}

private Annotations() {}
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({PARAMETER, METHOD, FIELD})
@interface SocketFactoryInstance {}

private Annotations() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.GoogleLogger;
import com.google.common.util.concurrent.Uninterruptibles;
Expand All @@ -28,6 +29,8 @@
import com.google.tsunami.plugin.annotations.PluginInfo;
import com.google.tsunami.plugin.payload.Payload;
import com.google.tsunami.plugin.payload.PayloadGenerator;
import com.google.tsunami.plugins.detectors.rce.cve202333246.Annotations.OobSleepDuration;
import com.google.tsunami.plugins.detectors.rce.cve202333246.Annotations.SocketFactoryInstance;
import com.google.tsunami.proto.DetectionReport;
import com.google.tsunami.proto.DetectionReportList;
import com.google.tsunami.proto.DetectionStatus;
Expand All @@ -44,6 +47,7 @@
import java.time.Duration;
import java.time.Instant;
import javax.inject.Inject;
import javax.net.SocketFactory;

/** A Tsunami plugin that detects RocketMQ RCE vulnerability CVE-2023-33246. */
@PluginInfo(
Expand All @@ -55,28 +59,34 @@
bootstrapModule = RocketMqCve202333246DetectorBootstrapModule.class)
public final class RocketMqCve202333246Detector implements VulnDetector {
private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
private static final String VULNERABILITY_ID = "CVE-2023-33246";
private static final String VULNERABILITY_DESCRIPTION =
@VisibleForTesting static final String VULNERABILITY_ID = "CVE-2023-33246";

@VisibleForTesting
static final String VULNERABILITY_DESCRIPTION =
"Apache RocketMQ allows unauthenticated attackers to modify the broker configuration "
+ "through a command injection vulnerability, leading to remote code execution.";
private static final String VULNERABILITY_RECOMMENDATION =

@VisibleForTesting
static final String VULNERABILITY_RECOMMENDATION =
"Remove RocketMQ from internet exposure and apply the latest patches to mitigate the issue.";
private static final String ROCKETMQ_RESPONSE_INDICATOR = "serializeTypeCurrentRPC";

private static final String ROCKETMQ_RESPONSE_INDICATOR = "serializeTypeCurrentRPC";
private static final String ROCKETMQ_PAYLOAD_HEADER_TEMPLATE =
"{\"code\":%d,\"flag\":0,\"language\":\"JAVA\",\"opaque\":0,\"serializeTypeCurrentRPC\":\"JSON\",\"version\":395}";
private final Clock utcClock;
private final PayloadGenerator payloadGenerator;
private final SocketFactory socketFactory;
private final int oobSleepDuration;

@Inject
RocketMqCve202333246Detector(
@UtcClock Clock utcClock,
PayloadGenerator payloadGenerator,
@Annotations.OobSleepDuration int oobSleepDuration
) {
@UtcClock Clock utcClock,
PayloadGenerator payloadGenerator,
@SocketFactoryInstance SocketFactory socketFactory,
@OobSleepDuration int oobSleepDuration) {
this.utcClock = checkNotNull(utcClock);
this.payloadGenerator = checkNotNull(payloadGenerator);
this.socketFactory = checkNotNull(socketFactory);
this.oobSleepDuration = oobSleepDuration;
}

Expand Down Expand Up @@ -130,13 +140,14 @@ private byte[] sendPayload(NetworkService service, byte[] payload) {
var serviceIp = service.getNetworkEndpoint().getIpAddress().getAddress();
var servicePort = service.getNetworkEndpoint().getPort().getPortNumber();

try (var socket = new java.net.Socket(serviceIp, servicePort)) {
try (var socket = socketFactory.createSocket(serviceIp, servicePort)) {
socket.getOutputStream().write(payload);

byte[] response = new byte[4096];
int bytesRead = socket.getInputStream().read(response);
if (bytesRead <= 0) {
logger.atWarning().log("Failed to read response from target server.");
logger.atWarning().log(
"Failed to read response from target server. Read %s bytes.", bytesRead);
return null;
}
return response;
Expand Down Expand Up @@ -172,6 +183,13 @@ private boolean isRocketMQService(NetworkService service) {
private boolean isServiceVulnerable(NetworkService service) {
logger.atInfo().log("Checking if RocketMQ service is vulnerable.");

if (!payloadGenerator.isCallbackServerEnabled()) {
logger.atWarning().log(
"The Tsunami callback server is not available, therefore the presence of the"
+ " vulnerability cannot be verified.");
return false;
}

PayloadGeneratorConfig payloadConfig =
PayloadGeneratorConfig.newBuilder()
.setVulnerabilityType(PayloadGeneratorConfig.VulnerabilityType.BLIND_RCE)
Expand All @@ -183,10 +201,8 @@ private boolean isServiceVulnerable(NetworkService service) {

Payload tsunamiPayload = payloadGenerator.generate(payloadConfig);

if (tsunamiPayload == null || !tsunamiPayload.getPayloadAttributes().getUsesCallbackServer()) {
logger.atWarning().log(
"The Tsunami callback server is not available, therefore the presence of the"
+ " vulnerability cannot be verified.");
if (tsunamiPayload == null) {
logger.atWarning().log("There was an error in the generation of the Tsunami payload.");
return false;
}

Expand All @@ -200,17 +216,9 @@ private boolean isServiceVulnerable(NetworkService service) {
}

// Wait for execution before checking for callback
// We observed varying degrees of delays in responses from the target, so we wait up to one minute
// but we do it in 10s cycles so we can exit early if the response comes
Duration timeout = Duration.ofSeconds(oobSleepDuration);
int retries = 6;
boolean executed = false;
for (int currentTry = 0; currentTry < retries && !executed; currentTry++) {
Uninterruptibles.sleepUninterruptibly(timeout);
executed = tsunamiPayload.checkIfExecuted();
}
Uninterruptibles.sleepUninterruptibly(Duration.ofSeconds(oobSleepDuration));

return executed;
return tsunamiPayload.checkIfExecuted();
}

private DetectionReport buildDetectionReport(TargetInfo targetInfo, NetworkService service) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

import com.google.inject.Provides;
import com.google.tsunami.plugin.PluginBootstrapModule;
import com.google.tsunami.plugins.detectors.rce.cve202333246.Annotations.OobSleepDuration;
import com.google.tsunami.plugins.detectors.rce.cve202333246.Annotations.SocketFactoryInstance;
import javax.net.SocketFactory;

/** A module for bootstrapping the {@link RocketMqCve202333246Detector}. */
public final class RocketMqCve202333246DetectorBootstrapModule extends PluginBootstrapModule {
Expand All @@ -27,11 +30,17 @@ protected void configurePlugin() {
}

@Provides
@Annotations.OobSleepDuration
@OobSleepDuration
int provideOobSleepDuration(RocketMqCve202333246DetectorConfig configs) {
if (configs.oobSleepDuration == 0) {
return 10;
return 40;
}
return configs.oobSleepDuration;
}

@Provides
@SocketFactoryInstance
SocketFactory provideSocketFactoryInstance() {
return SocketFactory.getDefault();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@

@ConfigProperties("plugins.community.detectors.rocketmq_cve_2023_33246")
public class RocketMqCve202333246DetectorConfig {
int oobSleepDuration;
int oobSleepDuration;
}
Loading

0 comments on commit 1465ca4

Please sign in to comment.