From 2a8e92ce67fe2ba69c03a8e2f9a8608c68160b89 Mon Sep 17 00:00:00 2001 From: Simon Bennetts Date: Thu, 27 Jun 2024 10:31:05 +0100 Subject: [PATCH] Pscanbeta - add polyfill.io scan rule Signed-off-by: Simon Bennetts --- addOns/pscanrulesBeta/CHANGELOG.md | 3 + .../PolyfillCdnScriptScanRule.java | 146 ++++++++++ .../resources/help/contents/pscanbeta.html | 9 + .../resources/Messages.properties | 6 + .../PolyfillCdnScriptScanRuleUnitTest.java | 263 ++++++++++++++++++ 5 files changed, 427 insertions(+) create mode 100644 addOns/pscanrulesBeta/src/main/java/org/zaproxy/zap/extension/pscanrulesBeta/PolyfillCdnScriptScanRule.java create mode 100644 addOns/pscanrulesBeta/src/test/java/org/zaproxy/zap/extension/pscanrulesBeta/PolyfillCdnScriptScanRuleUnitTest.java diff --git a/addOns/pscanrulesBeta/CHANGELOG.md b/addOns/pscanrulesBeta/CHANGELOG.md index 13736d88069..f73cc8835f5 100644 --- a/addOns/pscanrulesBeta/CHANGELOG.md +++ b/addOns/pscanrulesBeta/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this add-on will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased +### Added +- Polyfill.io script detection + ### Changed - Update minimum ZAP version to 2.15.0. diff --git a/addOns/pscanrulesBeta/src/main/java/org/zaproxy/zap/extension/pscanrulesBeta/PolyfillCdnScriptScanRule.java b/addOns/pscanrulesBeta/src/main/java/org/zaproxy/zap/extension/pscanrulesBeta/PolyfillCdnScriptScanRule.java new file mode 100644 index 00000000000..7653b089a5f --- /dev/null +++ b/addOns/pscanrulesBeta/src/main/java/org/zaproxy/zap/extension/pscanrulesBeta/PolyfillCdnScriptScanRule.java @@ -0,0 +1,146 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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 org.zaproxy.zap.extension.pscanrulesBeta; + +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import net.htmlparser.jericho.Element; +import net.htmlparser.jericho.HTMLElementName; +import net.htmlparser.jericho.Source; +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.core.scanner.Alert; +import org.parosproxy.paros.network.HttpMessage; +import org.zaproxy.addon.commonlib.CommonAlertTag; +import org.zaproxy.zap.extension.pscan.PluginPassiveScanner; + +public class PolyfillCdnScriptScanRule extends PluginPassiveScanner + implements CommonPassiveScanRuleInfo { + + /** Prefix for internationalised messages used by this rule */ + private static final String MESSAGE_PREFIX = "pscanbeta.polyfillcdnscript."; + + private static final Map ALERT_TAGS = + CommonAlertTag.toMap( + CommonAlertTag.OWASP_2021_A06_VULN_COMP, + CommonAlertTag.OWASP_2017_A09_VULN_COMP); + + private static final int PLUGIN_ID = 10115; + + private static Pattern POLYFILL_IO = + Pattern.compile("http[s]?://.*polyfill\\.io/.*", Pattern.CASE_INSENSITIVE); + + @Override + public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) { + if (msg.getResponseBody().length() > 0 && msg.getResponseHeader().isHtml()) { + List sourceElements = source.getAllElements(HTMLElementName.SCRIPT); + boolean alertRaised = false; + if (sourceElements != null) { + for (Element sourceElement : sourceElements) { + String src = sourceElement.getAttributeValue("src"); + if (src != null && POLYFILL_IO.matcher(src).matches()) { + this.createHighConfidenceAlert(src, sourceElement.toString()).raise(); + alertRaised = true; + } + } + if (alertRaised) { + // Definitely an issue, no point checking the script contents + return; + } + // Check the script contents, in case they are loading scripts via JS + for (Element sourceElement : sourceElements) { + String contents = sourceElement.getContent().toString(); + Matcher matcher = POLYFILL_IO.matcher(contents); + if (matcher.find()) { + this.createLowConfidenceAlert(null, matcher.group(0)).raise(); + break; + } + } + } + } + } + + private AlertBuilder createHighConfidenceAlert(String param, String evidence) { + return this.createAlert( + Alert.CONFIDENCE_HIGH, + Constant.messages.getString(MESSAGE_PREFIX + "desc1"), + param, + evidence, + 1); + } + + private AlertBuilder createLowConfidenceAlert(String param, String evidence) { + return this.createAlert( + Alert.CONFIDENCE_LOW, + Constant.messages.getString(MESSAGE_PREFIX + "desc2"), + param, + evidence, + 2); + } + + private AlertBuilder createAlert( + int confidence, String description, String param, String evidence, int alertRef) { + return newAlert() + .setRisk(getRisk()) + .setConfidence(confidence) + .setDescription(description) + .setParam(param) + .setSolution(Constant.messages.getString(MESSAGE_PREFIX + "soln")) + .setReference(Constant.messages.getString(MESSAGE_PREFIX + "refs")) + .setEvidence(evidence) + .setCweId(829) // CWE Id 829 - Inclusion of Functionality from Untrusted Control + // Sphere) + .setWascId(15) // WASC-15: Application Misconfiguration) + .setAlertRef(PLUGIN_ID + "-" + alertRef); + } + + @Override + public List getExampleAlerts() { + return List.of( + createHighConfidenceAlert( + "https://cdn.polyfill.io/malicious.js", + "") + .build(), + createLowConfidenceAlert( + "https://cdn.polyfill.io/malicious.js", + "") + .build()); + } + + @Override + public int getPluginId() { + return PLUGIN_ID; + } + + public int getRisk() { + return Alert.RISK_HIGH; + } + + @Override + public String getName() { + return Constant.messages.getString(MESSAGE_PREFIX + "name"); + } + + @Override + public Map getAlertTags() { + return ALERT_TAGS; + } +} diff --git a/addOns/pscanrulesBeta/src/main/javahelp/org/zaproxy/zap/extension/pscanrulesBeta/resources/help/contents/pscanbeta.html b/addOns/pscanrulesBeta/src/main/javahelp/org/zaproxy/zap/extension/pscanrulesBeta/resources/help/contents/pscanbeta.html index 242f220e776..560b3b110ab 100644 --- a/addOns/pscanrulesBeta/src/main/javahelp/org/zaproxy/zap/extension/pscanrulesBeta/resources/help/contents/pscanbeta.html +++ b/addOns/pscanrulesBeta/src/main/javahelp/org/zaproxy/zap/extension/pscanrulesBeta/resources/help/contents/pscanbeta.html @@ -72,6 +72,15 @@

Permissions Policy Header Not Set

Latest code: PermissionsPolicyScanRule.java
Alert ID: 10063 +

Script Served From Malicious polyfill.io Domain

+This checks for scripts being served from the polyfill.io domain, which is known to have been compromised.
+It will raise an alert with a High confidence if a script is loaded from a polyfill.io domain, +and a Low confidence if it just finds an apparent reference to a polyfill.io domain in the script contents. +

+Latest code: PolyfillCdnScriptScanRule.java +
+Alert ID: 10115. +

Site Isolation Scan Rule

Spectre is a side-channel attack allowing an attacker to read data from memory. One of the counter-measures is to prevent sensitive data diff --git a/addOns/pscanrulesBeta/src/main/resources/org/zaproxy/zap/extension/pscanrulesBeta/resources/Messages.properties b/addOns/pscanrulesBeta/src/main/resources/org/zaproxy/zap/extension/pscanrulesBeta/resources/Messages.properties index 4d170cb4fed..41b8acd1d2a 100644 --- a/addOns/pscanrulesBeta/src/main/resources/org/zaproxy/zap/extension/pscanrulesBeta/resources/Messages.properties +++ b/addOns/pscanrulesBeta/src/main/resources/org/zaproxy/zap/extension/pscanrulesBeta/resources/Messages.properties @@ -35,6 +35,12 @@ pscanbeta.permissionspolicymissing.name = Permissions Policy Header Not Set pscanbeta.permissionspolicymissing.refs = https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy\nhttps://developer.chrome.com/blog/feature-policy/\nhttps://scotthelme.co.uk/a-new-security-header-feature-policy/\nhttps://w3c.github.io/webappsec-feature-policy/\nhttps://www.smashingmagazine.com/2018/12/feature-policy/ pscanbeta.permissionspolicymissing.soln = Ensure that your web server, application server, load balancer, etc. is configured to set the Permissions-Policy header. +pscanbeta.polyfillcdnscript.desc1 = The page includes one or more script files loaded from the polyfill.io domain.\nThis is not associated with the polyfill.js library and is known to serve malicious content. +pscanbeta.polyfillcdnscript.desc2 = The page includes one or more script which appear to include a reference to a polyfill.io domain.\nThis is not associated with the polyfill.js library and is known to serve malicious content.\nYou should check to see if it is a safe reference (for example in a comment) or whether the script is loading content from that domain. +pscanbeta.polyfillcdnscript.name = Script Served From Malicious polyfill.io Domain +pscanbeta.polyfillcdnscript.refs = https://sansec.io/research/polyfill-supply-chain-attack\nhttps://x.com/triblondon/status/1761852117579427975 +pscanbeta.polyfillcdnscript.soln = Change all scripts to use a known good source based on their documentation. + pscanbeta.servletparameterpollution.desc = Unspecified form action: HTTP parameter override attack potentially possible. This is a known problem with Java Servlets but other platforms may also be vulnerable. pscanbeta.servletparameterpollution.name = HTTP Parameter Override pscanbeta.servletparameterpollution.refs = https://download.oracle.com/javaee-archive/servlet-spec.java.net/jsr340-experts/att-0317/OnParameterPollutionAttacks.pdf diff --git a/addOns/pscanrulesBeta/src/test/java/org/zaproxy/zap/extension/pscanrulesBeta/PolyfillCdnScriptScanRuleUnitTest.java b/addOns/pscanrulesBeta/src/test/java/org/zaproxy/zap/extension/pscanrulesBeta/PolyfillCdnScriptScanRuleUnitTest.java new file mode 100644 index 00000000000..ae686154e2d --- /dev/null +++ b/addOns/pscanrulesBeta/src/test/java/org/zaproxy/zap/extension/pscanrulesBeta/PolyfillCdnScriptScanRuleUnitTest.java @@ -0,0 +1,263 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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 org.zaproxy.zap.extension.pscanrulesBeta; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.parosproxy.paros.core.scanner.Alert; +import org.parosproxy.paros.network.HttpMalformedHeaderException; +import org.parosproxy.paros.network.HttpMessage; +import org.zaproxy.addon.commonlib.CommonAlertTag; +import org.zaproxy.zap.utils.ZapXmlConfiguration; + +/** Unit test for {@link PolyfillCdnScriptScanRule}. */ +class PolyfillCdnScriptScanRuleUnitTest extends PassiveScannerTest { + + @Override + protected PolyfillCdnScriptScanRule createScanner() { + PolyfillCdnScriptScanRule rule = new PolyfillCdnScriptScanRule(); + rule.setConfig(new ZapXmlConfiguration()); + return rule; + } + + @Test + void shouldReturnExpectedMappings() { + // Given / When + Map tags = rule.getAlertTags(); + List exampleAlerts = rule.getExampleAlerts(); + // Then + assertThat(exampleAlerts.size(), is(equalTo(2))); + assertThat(exampleAlerts.get(0).getCweId(), is(equalTo(829))); + assertThat(exampleAlerts.get(0).getWascId(), is(equalTo(15))); + assertThat(exampleAlerts.get(0).getConfidence(), is(equalTo(3))); + assertThat(tags.size(), is(equalTo(2))); + assertThat( + tags.containsKey(CommonAlertTag.OWASP_2021_A06_VULN_COMP.getTag()), + is(equalTo(true))); + assertThat( + tags.containsKey(CommonAlertTag.OWASP_2017_A09_VULN_COMP.getTag()), + is(equalTo(true))); + assertThat( + tags.get(CommonAlertTag.OWASP_2021_A06_VULN_COMP.getTag()), + is(equalTo(CommonAlertTag.OWASP_2021_A06_VULN_COMP.getValue()))); + assertThat( + tags.get(CommonAlertTag.OWASP_2017_A09_VULN_COMP.getTag()), + is(equalTo(CommonAlertTag.OWASP_2017_A09_VULN_COMP.getValue()))); + assertThat(exampleAlerts.get(1).getConfidence(), is(equalTo(1))); + } + + @Test + void noScripts() throws HttpMalformedHeaderException { + + HttpMessage msg = new HttpMessage(); + msg.setRequestHeader("GET http://www.example.com/test/ HTTP/1.1"); + + msg.setResponseBody(""); + msg.setResponseHeader( + "HTTP/1.1 200 OK\r\n" + + "Server: Apache-Coyote/1.1\r\n" + + "Content-Type: text/html;charset=ISO-8859-1\r\n" + + "Content-Length: " + + msg.getResponseBody().length() + + "\r\n"); + scanHttpResponseReceive(msg); + + assertThat(alertsRaised.size(), equalTo(0)); + } + + @Test + void noPollyfillScripts() throws HttpMalformedHeaderException { + + HttpMessage msg = new HttpMessage(); + msg.setRequestHeader("GET https://www.example.com/test/ HTTP/1.1"); + + msg.setResponseBody( + "" + + "" + + "" + + "" + + ""); + msg.setResponseHeader( + "HTTP/1.1 200 OK\r\n" + + "Server: Apache-Coyote/1.1\r\n" + + "Content-Type: text/html;charset=ISO-8859-1\r\n" + + "Content-Length: " + + msg.getResponseBody().length() + + "\r\n"); + scanHttpResponseReceive(msg); + + assertThat(alertsRaised.size(), equalTo(1)); + assertThat( + alertsRaised.get(0).getEvidence(), + equalTo("https://cdn.polyfill.io/v3/polyfill.min.js")); + assertThat(alertsRaised.get(0).getConfidence(), equalTo(1)); + } +}