Skip to content

Commit

Permalink
DNS plugin: currently reviews if there is SPF entry
Browse files Browse the repository at this point in the history
Signed-off-by: Miguel Angel Garcia <miguelangel.garcia@gmail.com>
  • Loading branch information
magmax authored and kingthorin committed May 15, 2024
1 parent db4835f commit 6135d79
Show file tree
Hide file tree
Showing 18 changed files with 552 additions and 0 deletions.
12 changes: 12 additions & 0 deletions addOns/dns/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# 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
## Added

- Detection if SPF record is not present.
- Detection of more than one SPF record.

[0.0.1]: https://github.com/zaproxy/zap-extensions/releases/dns-v0.0.1
21 changes: 21 additions & 0 deletions addOns/dns/dns.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
description = "Performs DNS related checks."

zapAddOn {
addOnName.set("DNS Recon")

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"))
}
2 changes: 2 additions & 0 deletions addOns/dns/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
version=0.0.1
release=false
61 changes: 61 additions & 0 deletions addOns/dns/src/main/java/org/zaproxy/addon/dns/DnsClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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 java.util.Map;
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);
private static final String TXT = "TXT";

public List<String> getTxtRecord(String host) {
Hashtable<String, String> env =
new Hashtable<>(
Map.of(
"java.naming.factory.initial",
"com.sun.jndi.dns.DnsContextFactory"));
List<String> 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;
}
}
53 changes: 53 additions & 0 deletions addOns/dns/src/main/java/org/zaproxy/addon/dns/ExtensionDns.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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;
import org.parosproxy.paros.extension.ExtensionHook;

/** An ZAP extension which performs DNS operations to get information or vulnerabilities. */
public class ExtensionDns extends ExtensionAdaptor {

public static final String NAME = "ExtensionDns";

protected static final String PREFIX = "dns";

public ExtensionDns() {
super(NAME);
setI18nPrefix(PREFIX);
}

@Override
public String getDescription() {
return Constant.messages.getString(PREFIX + ".description");
}

@Override
public String getUIName() {
return Constant.messages.getString(PREFIX + ".scanner");
}

@Override
public void hook(ExtensionHook extensionHook) {
super.hook(extensionHook);
extensionHook.addSessionListener(null);
}
}
44 changes: 44 additions & 0 deletions addOns/dns/src/main/java/org/zaproxy/addon/dns/SpfParser.java
Original file line number Diff line number Diff line change
@@ -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<String> 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;
}
}
135 changes: 135 additions & 0 deletions addOns/dns/src/main/java/org/zaproxy/addon/dns/SpfScanRule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* 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.Collections;
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 SpfScanRule extends AbstractHostPlugin {
private static final int ID = 90040;
private static final Logger LOGGER = LogManager.getLogger(SpfScanRule.class);
private static final String MESSAGE_PREFIX = "dns.spf.";
private static List<String> reviewedDomains = Collections.synchronizedList(new ArrayList<>());

private static 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("norecord.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("toomanyrecords.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 int getId() {
return ID;
}

@Override
public int getCategory() {
return Category.MISC;
}

@Override
public String getName() {
return getConstantString("name");
}

@Override
public String getDescription() {
return getConstantString("description");
}

@Override
public String getSolution() {
return "";
}

@Override
public String getReference() {
return "";
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>DNS</title>
</head>
<body>
<h1>DNS</h1>

<p>
Add-on to include DNS scan rules.
</p>

<h2>Current Rules</h2>

<ul>
<li>No SPF Record Found</li>
<li>Multiple SPF Records Found</li>
</ul>

</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 6135d79

Please sign in to comment.