diff --git a/addOns/ascanrules/CHANGELOG.md b/addOns/ascanrules/CHANGELOG.md index f9f4b850ef..010df41eab 100644 --- a/addOns/ascanrules/CHANGELOG.md +++ b/addOns/ascanrules/CHANGELOG.md @@ -8,6 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Maintenance changes. - The Spring Actuator Scan Rule now includes example alert functionality for documentation generation purposes (Issue 6119). +### Fixed +- Address false positives with Source Code Disclosure - CVE-2012-1823 scan rule, by not scanning binary responses and responses that already contain PHP source (Issue 8638). + ## [67] - 2024-07-22 ### Changed diff --git a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/SourceCodeDisclosureCve20121823ScanRule.java b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/SourceCodeDisclosureCve20121823ScanRule.java index 6ad9e1abe2..7825cd8a15 100644 --- a/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/SourceCodeDisclosureCve20121823ScanRule.java +++ b/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/SourceCodeDisclosureCve20121823ScanRule.java @@ -121,13 +121,18 @@ public String getReference() { public void scan() { try { - if (!getBaseMsg().getResponseHeader().isText()) { + if (!getBaseMsg().getResponseHeader().isText() + || ResourceIdentificationUtils.responseContainsControlChars(getBaseMsg())) { return; // Ignore images, pdfs, etc. } if (getAlertThreshold() != AlertThreshold.LOW && ResourceIdentificationUtils.isJavaScript(getBaseMsg())) { return; } + + if (isEvidenceInOriginalResponse()) { + return; + } // at Low or Medium strength, do not attack URLs which returned "Not Found" AttackStrength attackStrength = getAttackStrength(); if ((attackStrength == AttackStrength.LOW || attackStrength == AttackStrength.MEDIUM) @@ -181,6 +186,13 @@ public void scan() { } } + private boolean isEvidenceInOriginalResponse() { + var response = getBaseMsg().getResponseBody().toString(); + String responseBodyDecoded = new Source(response).getRenderer().toString(); + return PHP_PATTERN1.matcher(responseBodyDecoded).matches() + || PHP_PATTERN2.matcher(responseBodyDecoded).matches(); + } + private AlertBuilder buildAlert(String otherInfo) { return newAlert() .setConfidence(Alert.CONFIDENCE_MEDIUM) diff --git a/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/SourceCodeDisclosureCve20121823ScanRuleUnitTest.java b/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/SourceCodeDisclosureCve20121823ScanRuleUnitTest.java index c55883f338..7d26407e81 100644 --- a/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/SourceCodeDisclosureCve20121823ScanRuleUnitTest.java +++ b/addOns/ascanrules/src/test/java/org/zaproxy/zap/extension/ascanrules/SourceCodeDisclosureCve20121823ScanRuleUnitTest.java @@ -31,6 +31,8 @@ import java.util.Map; import org.apache.commons.text.StringEscapeUtils; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.parosproxy.paros.core.scanner.Alert; import org.parosproxy.paros.core.scanner.Plugin.AlertThreshold; import org.parosproxy.paros.core.scanner.Plugin.AttackStrength; @@ -253,6 +255,36 @@ protected Response serve(IHTTPSession session) { assertThat(alertsRaised, hasSize(0)); } + @ParameterizedTest + @ValueSource(strings = {PHP_SOURCE_TAGS, PHP_SOURCE_ECHO_TAG}) + void shouldNotScanIfPhpSourceWasAlreadyPresentInResponse(String source) throws Exception { + // Given + var response = + "PHP Tutorial: " + + StringEscapeUtils.escapeHtml4(source) + + ""; + HttpMessage message = getHttpMessage("GET", "/", response); + rule.init(message, parent); + // When + rule.scan(); + // Then + assertThat(httpMessagesSent, hasSize(0)); + assertThat(alertsRaised, hasSize(0)); + } + + @Test + void shouldNotScanBinaryResponse() throws Exception { + // Given + var response = "�PNG\n\n"; + HttpMessage message = getHttpMessage("GET", "/", response); + rule.init(message, parent); + // When + rule.scan(); + // Then + assertThat(httpMessagesSent, hasSize(0)); + assertThat(alertsRaised, hasSize(0)); + } + @Test void shouldAlertIfPhpEchoTagsWereDisclosedInResponseBody() throws Exception { // Given