From 5644ab0c15d8224d35114b37e9f5990ff040dde6 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 | 130 ++++++++++++ .../resources/help/contents/pscanbeta.html | 7 + .../resources/Messages.properties | 5 + .../PolyfillCdnScriptScanRuleUnitTest.java | 190 ++++++++++++++++++ 5 files changed, 335 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..c7570ff2f7e --- /dev/null +++ b/addOns/pscanrulesBeta/src/main/java/org/zaproxy/zap/extension/pscanrulesBeta/PolyfillCdnScriptScanRule.java @@ -0,0 +1,130 @@ +/* + * 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.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); + if (sourceElements != null) { + for (Element sourceElement : sourceElements) { + String src = sourceElement.getAttributeValue("src"); + System.out.println("SBSB " + src); // TODO + if (src != null && POLYFILL_IO.matcher(src).matches()) { + this.raiseAlert(msg, id, src, sourceElement.toString()); + } + } + } + } + } + + private AlertBuilder createAlert(String crossDomainScript, String evidence) { + return newAlert() + .setRisk(getRisk()) + .setConfidence(Alert.CONFIDENCE_HIGH) + .setDescription(getDescription()) + .setParam(crossDomainScript) + .setSolution(getSolution()) + .setReference(getReference()) + .setEvidence(evidence) + .setCweId(getCweId()) + .setWascId(getWascId()); + } + + private void raiseAlert(HttpMessage msg, int id, String crossDomainScript, String evidence) { + createAlert(crossDomainScript, evidence).raise(); + } + + @Override + public List getExampleAlerts() { + return List.of( + createAlert( + "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"); + } + + public String getDescription() { + return Constant.messages.getString(MESSAGE_PREFIX + "desc"); + } + + public String getSolution() { + return Constant.messages.getString(MESSAGE_PREFIX + "soln"); + } + + public String getReference() { + return Constant.messages.getString(MESSAGE_PREFIX + "refs"); + } + + @Override + public Map getAlertTags() { + return ALERT_TAGS; + } + + public int getCweId() { + return 829; // CWE Id 829 - Inclusion of Functionality from Untrusted Control Sphere + } + + public int getWascId() { + return 15; // WASC-15: Application Misconfiguration) + } +} 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..cab4e2ffc19 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,13 @@

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. +

+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..816f6339406 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,11 @@ 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.desc = The page includes one or more script files from the polyfill.io domain.\nThis is not associated with the polyfill.js library and is known to serve malicious content. +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..4ee202bfa43 --- /dev/null +++ b/addOns/pscanrulesBeta/src/test/java/org/zaproxy/zap/extension/pscanrulesBeta/PolyfillCdnScriptScanRuleUnitTest.java @@ -0,0 +1,190 @@ +/* + * 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.Map; +import org.junit.jupiter.api.Test; +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 + int cwe = rule.getCweId(); + int wasc = rule.getWascId(); + Map tags = rule.getAlertTags(); + // Then + assertThat(cwe, is(equalTo(829))); + assertThat(wasc, is(equalTo(15))); + assertThat(tags.size(), is(equalTo(1))); + assertThat( + tags.containsKey(CommonAlertTag.OWASP_2021_A08_INTEGRITY_FAIL.getTag()), + is(equalTo(true))); + assertThat( + tags.get(CommonAlertTag.OWASP_2021_A08_INTEGRITY_FAIL.getTag()), + is(equalTo(CommonAlertTag.OWASP_2021_A08_INTEGRITY_FAIL.getValue()))); + } + + @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( + "" + + "" + + "