From 14914626bd39e30c130a9cc3255feaabe8a9e5d2 Mon Sep 17 00:00:00 2001 From: Miguel Angel Garcia Date: Mon, 23 Oct 2023 08:56:21 +0200 Subject: [PATCH] DNS plugin: currently reviews if there is SPF entry Signed-off-by: Miguel Angel Garcia --- addOns/dns/CHANGELOG.md | 10 ++ addOns/dns/dns.gradle.kts | 21 +++ addOns/dns/gradle.properties | 2 + .../java/org/zaproxy/addon/dns/DnsClient.java | 57 +++++++ .../org/zaproxy/addon/dns/ExtensionDns.java | 47 ++++++ .../java/org/zaproxy/addon/dns/SpfParser.java | 44 ++++++ .../org/zaproxy/addon/dns/SpfScanner.java | 149 ++++++++++++++++++ .../exceptions/TooManyRecordsException.java | 25 +++ .../dns/resources/help/contents/about.html | 27 ++++ .../dns/resources/help/contents/dns.html | 21 +++ .../resources/help/contents/images/dns.png | Bin 0 -> 1051 bytes .../addon/dns/resources/help/helpset.hs | 41 +++++ .../addon/dns/resources/help/index.xml | 9 ++ .../zaproxy/addon/dns/resources/help/map.jhm | 10 ++ .../zaproxy/addon/dns/resources/help/toc.xml | 14 ++ .../addon/dns/resources/Messages.properties | 6 + .../org/zaproxy/addon/dns/SpfParserTest.java | 64 ++++++++ settings.gradle.kts | 1 + 18 files changed, 548 insertions(+) create mode 100644 addOns/dns/CHANGELOG.md create mode 100644 addOns/dns/dns.gradle.kts create mode 100644 addOns/dns/gradle.properties create mode 100644 addOns/dns/src/main/java/org/zaproxy/addon/dns/DnsClient.java create mode 100644 addOns/dns/src/main/java/org/zaproxy/addon/dns/ExtensionDns.java create mode 100644 addOns/dns/src/main/java/org/zaproxy/addon/dns/SpfParser.java create mode 100644 addOns/dns/src/main/java/org/zaproxy/addon/dns/SpfScanner.java create mode 100644 addOns/dns/src/main/java/org/zaproxy/addon/dns/exceptions/TooManyRecordsException.java create mode 100644 addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/contents/about.html create mode 100644 addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/contents/dns.html create mode 100644 addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/contents/images/dns.png create mode 100644 addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/helpset.hs create mode 100644 addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/index.xml create mode 100644 addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/map.jhm create mode 100644 addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/toc.xml create mode 100644 addOns/dns/src/main/resources/org/zaproxy/addon/dns/resources/Messages.properties create mode 100644 addOns/dns/src/test/java/org/zaproxy/addon/dns/SpfParserTest.java diff --git a/addOns/dns/CHANGELOG.md b/addOns/dns/CHANGELOG.md new file mode 100644 index 00000000000..d365317ebf5 --- /dev/null +++ b/addOns/dns/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog +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 + +- First version. +- Detection if SPF record is not present +- Detection of more than one SPF record diff --git a/addOns/dns/dns.gradle.kts b/addOns/dns/dns.gradle.kts new file mode 100644 index 00000000000..21f3a8324be --- /dev/null +++ b/addOns/dns/dns.gradle.kts @@ -0,0 +1,21 @@ +description = "Performs DNS checks" + +zapAddOn { + addOnName.set("DNS") + + manifest { + author.set("ZAP Dev Team") + } +} + +crowdin { + configuration { + val resourcesPath = "org/zaproxy/addon/${zapAddOn.addOnId.get()}/resources/" + tokens.put("%messagesPath%", resourcesPath) + tokens.put("%helpPath%", resourcesPath) + } +} + +dependencies { + testImplementation(project(":testutils")) +} diff --git a/addOns/dns/gradle.properties b/addOns/dns/gradle.properties new file mode 100644 index 00000000000..12f33ef6eb1 --- /dev/null +++ b/addOns/dns/gradle.properties @@ -0,0 +1,2 @@ +version=0.0.1 +release=false diff --git a/addOns/dns/src/main/java/org/zaproxy/addon/dns/DnsClient.java b/addOns/dns/src/main/java/org/zaproxy/addon/dns/DnsClient.java new file mode 100644 index 00000000000..c463ba085a5 --- /dev/null +++ b/addOns/dns/src/main/java/org/zaproxy/addon/dns/DnsClient.java @@ -0,0 +1,57 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2023 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.addon.dns; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import javax.naming.NamingEnumeration; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class DnsClient { + private static final Logger LOGGER = LogManager.getLogger(DnsClient.class); + + public List getTxtRecord(String host) { + Hashtable env = new Hashtable(); + env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); + + List result = new ArrayList(); + try { + DirContext dirContext = new InitialDirContext(env); + Attributes attrs = dirContext.getAttributes(host, new String[] {"TXT"}); + Attribute attr = attrs.get("TXT"); + + if (attr != null) { + NamingEnumeration attrenum = attr.getAll(); + while (attrenum.hasMore()) { + result.add(attrenum.next().toString()); + } + } + } catch (javax.naming.NamingException e) { + LOGGER.debug("There was a problem getting the TXT record: {}", e); + } + return result; + } +} diff --git a/addOns/dns/src/main/java/org/zaproxy/addon/dns/ExtensionDns.java b/addOns/dns/src/main/java/org/zaproxy/addon/dns/ExtensionDns.java new file mode 100644 index 00000000000..1ce2639fe2f --- /dev/null +++ b/addOns/dns/src/main/java/org/zaproxy/addon/dns/ExtensionDns.java @@ -0,0 +1,47 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2023 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.addon.dns; + +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.extension.ExtensionAdaptor; + +/** An ZAP extension which performs DNS operations to get information or vulnerabilities. */ +public class ExtensionDns extends ExtensionAdaptor { + + // The name is public so that other extensions can access it + public static final String NAME = "ExtensionSpfScanner"; + + protected static final String PREFIX = "dns"; + + public ExtensionDns() { + super(NAME); + setI18nPrefix(PREFIX); + } + + @Override + public void init() { + super.init(); + } + + @Override + public String getDescription() { + return Constant.messages.getString(PREFIX + ".description"); + } +} diff --git a/addOns/dns/src/main/java/org/zaproxy/addon/dns/SpfParser.java b/addOns/dns/src/main/java/org/zaproxy/addon/dns/SpfParser.java new file mode 100644 index 00000000000..1786cf6fa31 --- /dev/null +++ b/addOns/dns/src/main/java/org/zaproxy/addon/dns/SpfParser.java @@ -0,0 +1,44 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2023 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.addon.dns; + +import java.util.List; +import org.zaproxy.addon.dns.exceptions.TooManyRecordsException; + +public class SpfParser { + + private String record = null; + + public SpfParser(List txtRecord) throws TooManyRecordsException { + for (String entry : txtRecord) { + if (!entry.startsWith("v=spf1 ")) { + continue; + } + if (record != null) { + throw new TooManyRecordsException(); + } + record = entry; + } + } + + public boolean hasSpfRecord() { + return record != null; + } +} diff --git a/addOns/dns/src/main/java/org/zaproxy/addon/dns/SpfScanner.java b/addOns/dns/src/main/java/org/zaproxy/addon/dns/SpfScanner.java new file mode 100644 index 00000000000..87de43fd61b --- /dev/null +++ b/addOns/dns/src/main/java/org/zaproxy/addon/dns/SpfScanner.java @@ -0,0 +1,149 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2023 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.addon.dns; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.commons.httpclient.URIException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.parosproxy.paros.Constant; +import org.parosproxy.paros.core.scanner.AbstractHostPlugin; +import org.parosproxy.paros.core.scanner.Alert; +import org.parosproxy.paros.core.scanner.Category; +import org.parosproxy.paros.network.HttpMessage; +import org.zaproxy.addon.dns.exceptions.TooManyRecordsException; + +public class SpfScanner extends AbstractHostPlugin { + private static final int id = 90040; + private static final Logger LOGGER = LogManager.getLogger(SpfScanner.class); + private static final String MESSAGE_PREFIX = "dns."; + private volatile boolean enabled = true; + private static List reviewedDomains = new ArrayList(); + + private String getConstantString(String key) { + return Constant.messages.getString(MESSAGE_PREFIX + key); + } + + private String getHigherSubdomain(String host) { + String[] hostarray = host.split("\\."); + if (hostarray.length < 2) { + return null; + } + return String.join(".", Arrays.copyOfRange(hostarray, 1, hostarray.length)); + } + + @Override + public void scan() { + final HttpMessage originalMsg = getBaseMsg(); + try { + String host = originalMsg.getRequestHeader().getURI().getHost(); + DnsClient dns = new DnsClient(); + SpfParser spf = findValidSpfRecord(host, dns); + if (spf == null) { + newAlert() + .setMessage(getBaseMsg()) + .setRisk(Alert.RISK_INFO) + .setConfidence(Alert.CONFIDENCE_MEDIUM) + .setDescription(getConstantString("nospfrecord.description")) + .raise(); + } + } catch (URIException e) { + LOGGER.debug("There was a problem getting the TXT records: {}", e); + } catch (TooManyRecordsException e) { + newAlert() + .setMessage(getBaseMsg()) + .setRisk(Alert.RISK_INFO) + .setConfidence(Alert.CONFIDENCE_MEDIUM) + .setDescription(getConstantString("toomanyspfrecords.description")) + .raise(); + } + } + + private SpfParser findValidSpfRecord(String host, DnsClient dns) + throws TooManyRecordsException { + SpfParser spf = null; + while (host != null) { + if (hasBeenAlreadyAnalyzed(host)) { + return null; + } + markAsAnalyzed(host); + spf = new SpfParser(dns.getTxtRecord(host)); + if (spf.hasSpfRecord()) { + break; + } + host = getHigherSubdomain(host); + } + return spf; + } + + private void markAsAnalyzed(String host) { + reviewedDomains.add(host); + } + + private boolean hasBeenAlreadyAnalyzed(String host) { + return reviewedDomains.contains(host); + } + + @Override + public void setEnabled(boolean enabled) { + this.enabled = enabled; + if (enabled == false) { + // Reset the scanner + reviewedDomains.clear(); + } + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public int getId() { + return id; + } + + @Override + public int getCategory() { + return Category.MISC; + } + + @Override + public String getName() { + return getConstantString("scanner"); + } + + @Override + public String getDescription() { + return getConstantString("description"); + } + + @Override + public String getSolution() { + return getConstantString("solution"); + } + + @Override + public String getReference() { + return getConstantString("reference"); + } +} diff --git a/addOns/dns/src/main/java/org/zaproxy/addon/dns/exceptions/TooManyRecordsException.java b/addOns/dns/src/main/java/org/zaproxy/addon/dns/exceptions/TooManyRecordsException.java new file mode 100644 index 00000000000..1b6bc3259f7 --- /dev/null +++ b/addOns/dns/src/main/java/org/zaproxy/addon/dns/exceptions/TooManyRecordsException.java @@ -0,0 +1,25 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2023 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.addon.dns.exceptions; + +public class TooManyRecordsException extends Exception { + + private static final long serialVersionUID = 1L; +} diff --git a/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/contents/about.html b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/contents/about.html new file mode 100644 index 00000000000..0262aac5fcc --- /dev/null +++ b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/contents/about.html @@ -0,0 +1,27 @@ + + + + +DNS - About + + + +

DNS - About

+ +

Source Code

+https://github.com/zaproxy/zap-extensions/tree/main/addOns/dns + +

Authors

+ZAP Dev Team + +

History

+ +

Version 0.0.1

+First Version +
    +
  • Detection of no-SPF record
  • +
  • Detection of more than one SPF record
  • +
+ + + diff --git a/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/contents/dns.html b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/contents/dns.html new file mode 100644 index 00000000000..81df496450f --- /dev/null +++ b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/contents/dns.html @@ -0,0 +1,21 @@ + + + + DNS + + +

DNS

+ +

+ Add-on to perform automatic DNS checks. +

+ +

Current detections

+ +
    +
  • SPF record presence
  • +
  • SPF record duplication
  • +
+ + + diff --git a/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/contents/images/dns.png b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/contents/images/dns.png new file mode 100644 index 0000000000000000000000000000000000000000..ab7f62989447855297b92c43e7831ac895825892 GIT binary patch literal 1051 zcmV+$1mydPP)iAHr{evR{GQ6*lT&`g1A|Gd!y>E`!PSGau{KY!2G8R~Y{Ka&vlIO_`%O-r zBOW-2Dd?yX=)g~ymzK{@`o*|9=Uu`}Yto21_j@UN!l4r5i$ zhwzw)@C`;^X@Th}f1qr@-u#}{#(?4k!#RIh22W;7A@ti-C<5;1*UoT!N7RtR~yJjUE#B#^33NFt*iA)+P_2sm?T;%aI zOcg0r#eCc+f{aKxnica6Jcv)nko_G0h}c+w533B5L^)nEhH!!FFi`qky{duasl!tI zuJjTsKgkZ^E!>J%OZ2HCuqKI|?`xr;4-a65aQSQayR?3b@X}D-1`CCo$}gJD_*Jyn z-J*~bsCr;}@(hTRzFk=x#5FArpaWlux=&QbVO)WgX-FAkWy?O?o-)HYIq5wjmFvp= z2$qRJDT>lgQFwk4O{bj!FC_0S&GtS^)c)JrDJNbkA8W=zcUr$zG^_$M@JWvD5`7Bp z#r;X|!0Xs*0x#n^;?;6@3!ZJ_l>Np?sS@tNpQ43N2rERi-chqYgqv~<*6^%MzU32D zUSM@@u+HeeSQ|yO4>+&pND literal 0 HcmV?d00001 diff --git a/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/helpset.hs b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/helpset.hs new file mode 100644 index 00000000000..d4f8c0bc89f --- /dev/null +++ b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/helpset.hs @@ -0,0 +1,41 @@ + + + + DNS Add-On + + + top + + + + + TOC + + org.zaproxy.zap.extension.help.ZapTocView + toc.xml + + + + Index + + javax.help.IndexView + index.xml + + + + Search + + javax.help.SearchView + + JavaHelpSearch + + + + + Favorites + + javax.help.FavoritesView + + diff --git a/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/index.xml b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/index.xml new file mode 100644 index 00000000000..fbb6d2be375 --- /dev/null +++ b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/index.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/map.jhm b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/map.jhm new file mode 100644 index 00000000000..2bbb0f875fe --- /dev/null +++ b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/map.jhm @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/toc.xml b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/toc.xml new file mode 100644 index 00000000000..0c82d167307 --- /dev/null +++ b/addOns/dns/src/main/javahelp/org/zaproxy/addon/dns/resources/help/toc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/addOns/dns/src/main/resources/org/zaproxy/addon/dns/resources/Messages.properties b/addOns/dns/src/main/resources/org/zaproxy/addon/dns/resources/Messages.properties new file mode 100644 index 00000000000..5fd847aae26 --- /dev/null +++ b/addOns/dns/src/main/resources/org/zaproxy/addon/dns/resources/Messages.properties @@ -0,0 +1,6 @@ +dns.description = An Add-on to scan SPF configuration +dns.nospfrecord.description = No SPF record found +dns.reference = TODO +dns.scanner = SPF Scanner +dns.solution = TODO +dns.toomanyspfrecords.description = More than one SPF record found diff --git a/addOns/dns/src/test/java/org/zaproxy/addon/dns/SpfParserTest.java b/addOns/dns/src/test/java/org/zaproxy/addon/dns/SpfParserTest.java new file mode 100644 index 00000000000..773b186146d --- /dev/null +++ b/addOns/dns/src/test/java/org/zaproxy/addon/dns/SpfParserTest.java @@ -0,0 +1,64 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2023 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.addon.dns; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.zaproxy.addon.dns.exceptions.TooManyRecordsException; + +class SpfParserTest { + + @Test + void noTxtRecordsFindsNoSPFRecord() throws TooManyRecordsException { + // Given + List records = new ArrayList(); + // When + SpfParser sut = new SpfParser(records); + // Then + assertThat(sut.hasSpfRecord(), is(false)); + } + + @Test + void txtRecordsWithoutSPFFindsNoSPFRecord() throws TooManyRecordsException { + // Given + List records = new ArrayList(); + records.add("foo-verification=foo"); + records.add("bar-verification=bar"); + // When + SpfParser sut = new SpfParser(records); + // Then + assertThat(sut.hasSpfRecord(), is(false)); + } + + @Test + void severalSPFRecordsRaiseAnException() { + // Given + List records = new ArrayList(); + records.add("v=spf1 -all"); + records.add("v=spf1 -all"); + // When / Then + assertThrows(TooManyRecordsException.class, () -> new SpfParser(records)); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 82373b4f2d0..3086aa295c1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -51,6 +51,7 @@ var addOns = listOf( "directorylistv1", "directorylistv2_3", "directorylistv2_3_lc", + "dns", "domxss", "encoder", "evalvillain",