From 0ccf4f3703603257acf09484f1df09bff31b561f Mon Sep 17 00:00:00 2001 From: Alessio Dalla Piazza Date: Tue, 17 Sep 2024 09:30:19 +0200 Subject: [PATCH 1/8] add a method to check if the body contains metadata information --- .../ascanrules/CloudMetadataScanRule.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java index c1eb9fcaddc..99c54e00667 100644 --- a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java +++ b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java @@ -95,7 +95,7 @@ public Map getAlertTags() { public AlertBuilder createAlert(HttpMessage newRequest, String host) { return newAlert() - .setConfidence(Alert.CONFIDENCE_LOW) + .setConfidence(Alert.CONFIDENCE_MEDIUM) .setAttack(host) .setOtherInfo(Constant.messages.getString(MESSAGE_PREFIX + "otherinfo")) .setMessage(newRequest); @@ -110,6 +110,12 @@ public void scan() { newRequest.setUserObject(Collections.singletonMap("host", host)); sendAndReceive(newRequest, false); if (isSuccess(newRequest) && newRequest.getResponseBody().length() > 0) { + String responseBody = newRequest.getResponseBody().toString(); + if (containsMetadataIndicators(responseBody)) { + this.createAlert(newRequest, host).raise(); + return; + } + this.createAlert(newRequest, host).raise(); return; } @@ -119,6 +125,17 @@ public void scan() { } } + /** + * Checks if the response body contains indicators of AWS cloud metadata. + * + * @param responseBody the response body to check + * @return {@code true} if cloud metadata indicators are found; {@code false} otherwise + */ + private boolean containsMetadataIndicators(String responseBody) { + return responseBody.contains("ami-id") + && responseBody.contains("ami-launch-index"); + } + @Override public List getExampleAlerts() { return List.of(createAlert(null, "www.example.com").build()); From 3ef78f41f1dd0675ca181e9acfb995069cac139b Mon Sep 17 00:00:00 2001 From: Alessio Dalla Piazza Date: Tue, 17 Sep 2024 09:34:09 +0200 Subject: [PATCH 2/8] fix test and createAlert --- .../zap/extension/ascanrules/CloudMetadataScanRule.java | 3 --- .../extension/ascanrules/CloudMetadataScanRuleUnitTest.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java index 99c54e00667..60e31c2dfd6 100644 --- a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java +++ b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java @@ -115,9 +115,6 @@ public void scan() { this.createAlert(newRequest, host).raise(); return; } - - this.createAlert(newRequest, host).raise(); - return; } } catch (Exception e) { LOGGER.warn("Error sending URL {}", newRequest.getRequestHeader().getURI(), e); diff --git a/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRuleUnitTest.java b/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRuleUnitTest.java index 97d74e75e98..0222be2295d 100644 --- a/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRuleUnitTest.java +++ b/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRuleUnitTest.java @@ -87,7 +87,7 @@ void shouldAlertIfResponseIs200Ok(String host) throws Exception { assertThat(alertsRaised, hasSize(1)); Alert alert = alertsRaised.get(0); assertEquals(Alert.RISK_HIGH, alert.getRisk()); - assertEquals(Alert.CONFIDENCE_LOW, alert.getConfidence()); + assertEquals(Alert.CONFIDENCE_MEDIUM, alert.getConfidence()); assertEquals(host, alert.getAttack()); } From bfe147a921e84d3a58a09acb16f1904d3af3f708 Mon Sep 17 00:00:00 2001 From: Alessio Dalla Piazza Date: Tue, 17 Sep 2024 10:01:09 +0200 Subject: [PATCH 3/8] apply spotlessApply --- .../zap/extension/ascanrules/CloudMetadataScanRule.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java index 60e31c2dfd6..aeaeca7458e 100644 --- a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java +++ b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java @@ -129,8 +129,7 @@ public void scan() { * @return {@code true} if cloud metadata indicators are found; {@code false} otherwise */ private boolean containsMetadataIndicators(String responseBody) { - return responseBody.contains("ami-id") - && responseBody.contains("ami-launch-index"); + return responseBody.contains("ami-id") && responseBody.contains("ami-launch-index"); } @Override From 1b2cc0aa8f0eecc47506924886e3d465018a96cb Mon Sep 17 00:00:00 2001 From: Alessio Dalla Piazza Date: Tue, 17 Sep 2024 12:26:47 +0200 Subject: [PATCH 4/8] add more tests and more checks for cloud-metadata --- .../ascanrules/CloudMetadataScanRule.java | 114 +++++++++++++++--- .../CloudMetadataScanRuleUnitTest.java | 71 ++++++++++- 2 files changed, 167 insertions(+), 18 deletions(-) diff --git a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java index aeaeca7458e..a0012fe9311 100644 --- a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java +++ b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java @@ -42,17 +42,85 @@ public class CloudMetadataScanRule extends AbstractHostPlugin implements CommonA private static final String MESSAGE_PREFIX = "ascanrules.cloudmetadata."; private static final int PLUGIN_ID = 90034; - private static final String METADATA_PATH = "/latest/meta-data/"; - private static final List METADATA_HOSTS = - Arrays.asList( - "169.254.169.254", "aws.zaproxy.org", "100.100.100.200", "alibaba.zaproxy.org"); - private static final Logger LOGGER = LogManager.getLogger(CloudMetadataScanRule.class); private static final Map ALERT_TAGS = CommonAlertTag.toMap( CommonAlertTag.OWASP_2021_A05_SEC_MISCONFIG, CommonAlertTag.OWASP_2017_A06_SEC_MISCONFIG); + // this class hold metadata endpoint details + private static class CloudMetadataEndpoint { + String host; + String path; + String provider; + Map headers; + + CloudMetadataEndpoint( + String host, String path, String provider, Map headers) { + this.host = host; + this.path = path; + this.provider = provider; + this.headers = headers; + } + } + + // metadata endpoints to test + private static final List METADATA_ENDPOINTS = + Arrays.asList( + // AWS + new CloudMetadataEndpoint( + "169.254.169.254", "/latest/meta-data/", "AWS", Collections.emptyMap()), + new CloudMetadataEndpoint( + "aws.zaproxy.org", "/latest/meta-data/", "AWS", Collections.emptyMap()), + // GCP + new CloudMetadataEndpoint( + "169.254.169.254", + "/computeMetadata/v1/", + "GCP", + Map.of("Metadata-Flavor", "Google")), + new CloudMetadataEndpoint( + "metadata.google.internal", + "/computeMetadata/v1/", + "GCP", + Map.of("Metadata-Flavor", "Google")), + // OCI + new CloudMetadataEndpoint( + "169.254.169.254", "/opc/v1/instance/", "OCI", Collections.emptyMap()), + new CloudMetadataEndpoint( + "metadata.oraclecloud.com", + "/opc/v1/instance/", + "OCI", + Collections.emptyMap()), + // Alibaba Cloud + new CloudMetadataEndpoint( + "100.100.100.200", + "/latest/meta-data/", + "AlibabaCloud", + Collections.emptyMap()), + new CloudMetadataEndpoint( + "alibaba.zaproxy.org", + "/latest/meta-data/", + "AlibabaCloud", + Collections.emptyMap()), + // Azure + new CloudMetadataEndpoint( + "169.254.169.254", + "/metadata/instance", + "Azure", + Map.of("Metadata", "true"))); + + // metadata indicators for each cloud provider + private static final Map> PROVIDER_INDICATORS = + Map.of( + "AWS", + Arrays.asList( + "ami-id", "instance-id", "local-hostname", "public-hostname"), + "GCP", Arrays.asList("project-id", "zone", "machineType", "hostname"), + "Azure", Arrays.asList("compute", "network", "osType", "vmSize"), + "AlibabaCloud", + Arrays.asList("image-id", "instance-id", "hostname", "region-id"), + "OCI", Arrays.asList("oci", "instance", "availabilityDomain", "region")); + @Override public int getId() { return PLUGIN_ID; @@ -103,33 +171,49 @@ public AlertBuilder createAlert(HttpMessage newRequest, String host) { @Override public void scan() { - HttpMessage newRequest = getNewMsg(); - for (String host : METADATA_HOSTS) { + for (CloudMetadataEndpoint endpoint : METADATA_ENDPOINTS) { + HttpMessage newRequest = getNewMsg(); try { - newRequest.getRequestHeader().getURI().setPath(METADATA_PATH); - newRequest.setUserObject(Collections.singletonMap("host", host)); + // set the request path + newRequest.getRequestHeader().getURI().setPath(endpoint.path); + // set the Host header + newRequest.setUserObject(Collections.singletonMap("host", endpoint.host)); + // set additional headers if required + for (Map.Entry header : endpoint.headers.entrySet()) { + newRequest.getRequestHeader().setHeader(header.getKey(), header.getValue()); + } sendAndReceive(newRequest, false); if (isSuccess(newRequest) && newRequest.getResponseBody().length() > 0) { String responseBody = newRequest.getResponseBody().toString(); - if (containsMetadataIndicators(responseBody)) { - this.createAlert(newRequest, host).raise(); + if (containsMetadataIndicators(responseBody, endpoint.provider)) { + this.createAlert(newRequest, endpoint.host).raise(); return; } } } catch (Exception e) { - LOGGER.warn("Error sending URL {}", newRequest.getRequestHeader().getURI(), e); + LOGGER.warn("Error sending request to {}: {}", endpoint.host, e.getMessage(), e); } } } /** - * Checks if the response body contains indicators of AWS cloud metadata. + * Checks if the response body contains metadata indicators specific to the cloud provider. * * @param responseBody the response body to check + * @param provider the cloud provider * @return {@code true} if cloud metadata indicators are found; {@code false} otherwise */ - private boolean containsMetadataIndicators(String responseBody) { - return responseBody.contains("ami-id") && responseBody.contains("ami-launch-index"); + private boolean containsMetadataIndicators(String responseBody, String provider) { + List indicators = PROVIDER_INDICATORS.get(provider); + if (indicators == null) { + return false; + } + for (String indicator : indicators) { + if (responseBody.contains(indicator)) { + return true; + } + } + return false; } @Override diff --git a/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRuleUnitTest.java b/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRuleUnitTest.java index 0222be2295d..6e064028152 100644 --- a/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRuleUnitTest.java +++ b/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRuleUnitTest.java @@ -70,10 +70,8 @@ void shouldNotAlertIfResponseIsNot200Ok() throws Exception { strings = { "169.254.169.254", "aws.zaproxy.org", - "100.100.100.200", - "alibaba.zaproxy.org" }) - void shouldAlertIfResponseIs200Ok(String host) throws Exception { + void shouldAlertIfResponseIs200OkAWS(String host) throws Exception { // Given String path = "/latest/meta-data/"; // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html @@ -91,6 +89,73 @@ void shouldAlertIfResponseIs200Ok(String host) throws Exception { assertEquals(host, alert.getAttack()); } + @ParameterizedTest + @ValueSource( + strings = { + "100.100.100.200", + "alibaba.zaproxy.org", + }) + void shouldAlertIfResponseIs200OkAlibabaCloud(String host) throws Exception { + // Given + String path = "/latest/meta-data/"; + String body = "image-id\ninstance-id"; + this.nano.addHandler(createHandler(path, Response.Status.OK, body, host)); + HttpMessage msg = this.getHttpMessage(path); + rule.init(msg, this.parent); + // When + rule.scan(); + // Then + assertThat(alertsRaised, hasSize(1)); + Alert alert = alertsRaised.get(0); + assertEquals(Alert.RISK_HIGH, alert.getRisk()); + assertEquals(Alert.CONFIDENCE_MEDIUM, alert.getConfidence()); + assertEquals(host, alert.getAttack()); + } + + @ParameterizedTest + @ValueSource( + strings = { + "169.254.169.254", + }) + void shouldAlertIfResponseIs200OkGCP(String host) throws Exception { + // Given + String path = "/computeMetadata/v1/"; + String body = "project-id"; + this.nano.addHandler(createHandler(path, Response.Status.OK, body, host)); + HttpMessage msg = this.getHttpMessage(path); + rule.init(msg, this.parent); + // When + rule.scan(); + // Then + assertThat(alertsRaised, hasSize(1)); + Alert alert = alertsRaised.get(0); + assertEquals(Alert.RISK_HIGH, alert.getRisk()); + assertEquals(Alert.CONFIDENCE_MEDIUM, alert.getConfidence()); + assertEquals(host, alert.getAttack()); + } + + @ParameterizedTest + @ValueSource( + strings = { + "169.254.169.254", + }) + void shouldAlertIfResponseIs200OkAzure(String host) throws Exception { + // Given + String path = "/metadata/instance"; + String body = "osType"; + this.nano.addHandler(createHandler(path, Response.Status.OK, body, host)); + HttpMessage msg = this.getHttpMessage(path); + rule.init(msg, this.parent); + // When + rule.scan(); + // Then + assertThat(alertsRaised, hasSize(1)); + Alert alert = alertsRaised.get(0); + assertEquals(Alert.RISK_HIGH, alert.getRisk()); + assertEquals(Alert.CONFIDENCE_MEDIUM, alert.getConfidence()); + assertEquals(host, alert.getAttack()); + } + @Test void shouldReturnExpectedMappings() { // Given / When From 73f2c1e2fce79ccea6b442cbe24e1171e402304f Mon Sep 17 00:00:00 2001 From: Alessio Dalla Piazza Date: Tue, 17 Sep 2024 12:30:24 +0200 Subject: [PATCH 5/8] update changelog --- addOns/ascanrules/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/addOns/ascanrules/CHANGELOG.md b/addOns/ascanrules/CHANGELOG.md index f9f4b850efd..40eac8170d5 100644 --- a/addOns/ascanrules/CHANGELOG.md +++ b/addOns/ascanrules/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased ### Changed +- Cloud Metadata Scan Rule now supports multiple cloud providers. Implemented provider-specific metadata indicators to reduce false positives. - Maintenance changes. - The Spring Actuator Scan Rule now includes example alert functionality for documentation generation purposes (Issue 6119). From c5d946b1001726c38bc4f75f2c29c638f2d09f70 Mon Sep 17 00:00:00 2001 From: Alessio Dalla Piazza Date: Tue, 17 Sep 2024 17:16:41 +0200 Subject: [PATCH 6/8] update changelog --- addOns/ascanrules/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addOns/ascanrules/CHANGELOG.md b/addOns/ascanrules/CHANGELOG.md index 40eac8170d5..2d6c0a8dd93 100644 --- a/addOns/ascanrules/CHANGELOG.md +++ b/addOns/ascanrules/CHANGELOG.md @@ -5,7 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased ### Changed -- Cloud Metadata Scan Rule now supports multiple cloud providers. Implemented provider-specific metadata indicators to reduce false positives. +- Cloud Metadata rule is improved to support GCP, Azure and OCI. - Maintenance changes. - The Spring Actuator Scan Rule now includes example alert functionality for documentation generation purposes (Issue 6119). @@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - False positives in the Path Traversal rule. - Alert text for various rules has been updated to more consistently use periods and spaces in a uniform manner. - False Positives in the Remote File Inclusion rule (Issue 8561). +- False Positives in Cloud Metadata rule. ## [66] - 2024-05-07 ### Changed From ab7a9bf93ae0b3a3da28d577c3a5fb40b44329f8 Mon Sep 17 00:00:00 2001 From: Alessio Dalla Piazza Date: Tue, 17 Sep 2024 17:17:18 +0200 Subject: [PATCH 7/8] apply review --- .../ascanrules/CloudMetadataScanRule.java | 205 ++++++++---------- 1 file changed, 96 insertions(+), 109 deletions(-) diff --git a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java index a0012fe9311..c143d52c6c0 100644 --- a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java +++ b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.parosproxy.paros.Constant; @@ -48,78 +49,84 @@ public class CloudMetadataScanRule extends AbstractHostPlugin implements CommonA CommonAlertTag.OWASP_2021_A05_SEC_MISCONFIG, CommonAlertTag.OWASP_2017_A06_SEC_MISCONFIG); - // this class hold metadata endpoint details - private static class CloudMetadataEndpoint { - String host; - String path; - String provider; - Map headers; - - CloudMetadataEndpoint( - String host, String path, String provider, Map headers) { - this.host = host; - this.path = path; - this.provider = provider; - this.headers = headers; + private enum CloudProvider { + AWS( + Arrays.asList( + new Endpoint( + "169.254.169.254", "/latest/meta-data/", Collections.emptyMap()), + new Endpoint( + "aws.zaproxy.org", "/latest/meta-data/", Collections.emptyMap())), + Set.of("ami-id", "instance-id", "local-hostname", "public-hostname")), + GCP( + Arrays.asList( + new Endpoint( + "169.254.169.254", + "/computeMetadata/v1/", + Map.of("Metadata-Flavor", "Google")), + new Endpoint( + "metadata.google.internal", + "/computeMetadata/v1/", + Map.of("Metadata-Flavor", "Google"))), + Set.of("project-id", "zone", "machineType", "hostname")), + OCI( + Arrays.asList( + new Endpoint( + "169.254.169.254", "/opc/v1/instance/", Collections.emptyMap()), + new Endpoint( + "metadata.oraclecloud.com", + "/opc/v1/instance/", + Collections.emptyMap())), + Set.of("oci", "instance", "availabilityDomain", "region")), + AlibabaCloud( + Arrays.asList( + new Endpoint( + "100.100.100.200", "/latest/meta-data/", Collections.emptyMap()), + new Endpoint( + "alibaba.zaproxy.org", + "/latest/meta-data/", + Collections.emptyMap())), + Set.of("image-id", "instance-id", "hostname", "region-id")), + Azure( + Arrays.asList( + new Endpoint( + "169.254.169.254", + "/metadata/instance", + Map.of("Metadata", "true"))), + Set.of("compute", "network", "osType", "vmSize")); + + private final List endpoints; + private final Set indicators; + + CloudProvider(List endpoints, Set indicators) { + this.endpoints = endpoints; + this.indicators = indicators; + } + + public List getEndpoints() { + return endpoints; + } + + public boolean containsMetadataIndicators(String responseBody) { + for (String indicator : indicators) { + if (responseBody.contains(indicator)) { + return true; + } + } + return false; } - } - // metadata endpoints to test - private static final List METADATA_ENDPOINTS = - Arrays.asList( - // AWS - new CloudMetadataEndpoint( - "169.254.169.254", "/latest/meta-data/", "AWS", Collections.emptyMap()), - new CloudMetadataEndpoint( - "aws.zaproxy.org", "/latest/meta-data/", "AWS", Collections.emptyMap()), - // GCP - new CloudMetadataEndpoint( - "169.254.169.254", - "/computeMetadata/v1/", - "GCP", - Map.of("Metadata-Flavor", "Google")), - new CloudMetadataEndpoint( - "metadata.google.internal", - "/computeMetadata/v1/", - "GCP", - Map.of("Metadata-Flavor", "Google")), - // OCI - new CloudMetadataEndpoint( - "169.254.169.254", "/opc/v1/instance/", "OCI", Collections.emptyMap()), - new CloudMetadataEndpoint( - "metadata.oraclecloud.com", - "/opc/v1/instance/", - "OCI", - Collections.emptyMap()), - // Alibaba Cloud - new CloudMetadataEndpoint( - "100.100.100.200", - "/latest/meta-data/", - "AlibabaCloud", - Collections.emptyMap()), - new CloudMetadataEndpoint( - "alibaba.zaproxy.org", - "/latest/meta-data/", - "AlibabaCloud", - Collections.emptyMap()), - // Azure - new CloudMetadataEndpoint( - "169.254.169.254", - "/metadata/instance", - "Azure", - Map.of("Metadata", "true"))); - - // metadata indicators for each cloud provider - private static final Map> PROVIDER_INDICATORS = - Map.of( - "AWS", - Arrays.asList( - "ami-id", "instance-id", "local-hostname", "public-hostname"), - "GCP", Arrays.asList("project-id", "zone", "machineType", "hostname"), - "Azure", Arrays.asList("compute", "network", "osType", "vmSize"), - "AlibabaCloud", - Arrays.asList("image-id", "instance-id", "hostname", "region-id"), - "OCI", Arrays.asList("oci", "instance", "availabilityDomain", "region")); + private static class Endpoint { + String host; + String path; + Map headers; + + Endpoint(String host, String path, Map headers) { + this.host = host; + this.path = path; + this.headers = headers; + } + } + } @Override public int getId() { @@ -171,49 +178,29 @@ public AlertBuilder createAlert(HttpMessage newRequest, String host) { @Override public void scan() { - for (CloudMetadataEndpoint endpoint : METADATA_ENDPOINTS) { - HttpMessage newRequest = getNewMsg(); - try { - // set the request path - newRequest.getRequestHeader().getURI().setPath(endpoint.path); - // set the Host header - newRequest.setUserObject(Collections.singletonMap("host", endpoint.host)); - // set additional headers if required - for (Map.Entry header : endpoint.headers.entrySet()) { - newRequest.getRequestHeader().setHeader(header.getKey(), header.getValue()); - } - sendAndReceive(newRequest, false); - if (isSuccess(newRequest) && newRequest.getResponseBody().length() > 0) { - String responseBody = newRequest.getResponseBody().toString(); - if (containsMetadataIndicators(responseBody, endpoint.provider)) { - this.createAlert(newRequest, endpoint.host).raise(); - return; + for (CloudProvider provider : CloudProvider.values()) { + for (CloudProvider.Endpoint endpoint : provider.getEndpoints()) { + HttpMessage newRequest = getNewMsg(); + try { + newRequest.getRequestHeader().getURI().setPath(endpoint.path); + newRequest.setUserObject(Collections.singletonMap("host", endpoint.host)); + for (Map.Entry header : endpoint.headers.entrySet()) { + newRequest.getRequestHeader().setHeader(header.getKey(), header.getValue()); } + sendAndReceive(newRequest, false); + if (isSuccess(newRequest) && newRequest.getResponseBody().length() > 0) { + String responseBody = newRequest.getResponseBody().toString(); + if (provider.containsMetadataIndicators(responseBody)) { + this.createAlert(newRequest, endpoint.host).raise(); + return; + } + } + } catch (Exception e) { + LOGGER.warn( + "Error sending request to {}: {}", endpoint.host, e.getMessage(), e); } - } catch (Exception e) { - LOGGER.warn("Error sending request to {}: {}", endpoint.host, e.getMessage(), e); - } - } - } - - /** - * Checks if the response body contains metadata indicators specific to the cloud provider. - * - * @param responseBody the response body to check - * @param provider the cloud provider - * @return {@code true} if cloud metadata indicators are found; {@code false} otherwise - */ - private boolean containsMetadataIndicators(String responseBody, String provider) { - List indicators = PROVIDER_INDICATORS.get(provider); - if (indicators == null) { - return false; - } - for (String indicator : indicators) { - if (responseBody.contains(indicator)) { - return true; } } - return false; } @Override From 80afb76c087c41fefa73925dc2450a2ecfb72931 Mon Sep 17 00:00:00 2001 From: Alessio Dalla Piazza Date: Tue, 17 Sep 2024 18:20:37 +0200 Subject: [PATCH 8/8] use List.of --- .../extension/ascanrules/CloudMetadataScanRule.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java index c143d52c6c0..d623386c82e 100644 --- a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java +++ b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CloudMetadataScanRule.java @@ -19,7 +19,6 @@ */ package org.zaproxy.zap.extension.ascanrules; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -51,14 +50,14 @@ public class CloudMetadataScanRule extends AbstractHostPlugin implements CommonA private enum CloudProvider { AWS( - Arrays.asList( + List.of( new Endpoint( "169.254.169.254", "/latest/meta-data/", Collections.emptyMap()), new Endpoint( "aws.zaproxy.org", "/latest/meta-data/", Collections.emptyMap())), Set.of("ami-id", "instance-id", "local-hostname", "public-hostname")), GCP( - Arrays.asList( + List.of( new Endpoint( "169.254.169.254", "/computeMetadata/v1/", @@ -69,7 +68,7 @@ private enum CloudProvider { Map.of("Metadata-Flavor", "Google"))), Set.of("project-id", "zone", "machineType", "hostname")), OCI( - Arrays.asList( + List.of( new Endpoint( "169.254.169.254", "/opc/v1/instance/", Collections.emptyMap()), new Endpoint( @@ -78,7 +77,7 @@ private enum CloudProvider { Collections.emptyMap())), Set.of("oci", "instance", "availabilityDomain", "region")), AlibabaCloud( - Arrays.asList( + List.of( new Endpoint( "100.100.100.200", "/latest/meta-data/", Collections.emptyMap()), new Endpoint( @@ -87,7 +86,7 @@ private enum CloudProvider { Collections.emptyMap())), Set.of("image-id", "instance-id", "hostname", "region-id")), Azure( - Arrays.asList( + List.of( new Endpoint( "169.254.169.254", "/metadata/instance",