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 <[email protected]>
  • Loading branch information
magmax committed Nov 7, 2023
1 parent c16fa91 commit e456938
Show file tree
Hide file tree
Showing 18 changed files with 538 additions and 0 deletions.
10 changes: 10 additions & 0 deletions addOns/dns/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
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 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"))
}
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
57 changes: 57 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,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<String> getTxtRecord(String host) {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");

List<String> result = new ArrayList<String>();
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;
}
}
47 changes: 47 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,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");
}
}
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.TooManyRecords;

public class SpfParser {

private String record = null;

public SpfParser(List<String> txtRecord) throws TooManyRecords {
for (String entry : txtRecord) {
if (!entry.startsWith("v=spf1 ")) {
continue;
}
if (record != null) {
throw new TooManyRecords();
}
record = entry;
}
}

public boolean hasSpfRecord() {
return record != null;
}
}
148 changes: 148 additions & 0 deletions addOns/dns/src/main/java/org/zaproxy/addon/dns/SpfScanner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* 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.TooManyRecords;

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<String> reviewedDomains = new ArrayList<String>();

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 (TooManyRecords 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 TooManyRecords {
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");
}
}
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 TooManyRecords extends Exception {

private static final long serialVersionUID = 1L;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>
DNS - About
</title>
</head>
<body>
<h1>DNS - About</h1>

<h2>Source Code</h2>
<a href="https://github.com/zaproxy/zap-extensions/tree/main/addOns/dns">https://github.com/zaproxy/zap-extensions/tree/main/addOns/dns</a>

<h2>Authors</h2>
ZAP Dev Team

<h2>History</h2>

<h3>Version 0.0.1</h3>
First Version
<ul>
<li>Detection of no-SPF record</li>
<li>Detection of more than one SPF record</li>
</ul>

</body>
</html>
Loading

0 comments on commit e456938

Please sign in to comment.