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
  • Loading branch information
magmax committed Oct 29, 2023
1 parent c16fa91 commit 84c68a0
Show file tree
Hide file tree
Showing 26 changed files with 797 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

## 1 - 2023-10-29

- First version.
- Detection of no-SPF record
- Detection of more than one SPF record
17 changes: 17 additions & 0 deletions addOns/dns/dns.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
description = "Recon via DNS entries"

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)
}
}
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=2
release=false
60 changes: 60 additions & 0 deletions addOns/dns/src/main/java/org/zaproxy/addon/dns/DnsAPI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2018 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 net.sf.json.JSONObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.zaproxy.zap.extension.api.ApiAction;
import org.zaproxy.zap.extension.api.ApiException;
import org.zaproxy.zap.extension.api.ApiImplementor;
import org.zaproxy.zap.extension.api.ApiResponse;
import org.zaproxy.zap.extension.api.ApiResponseElement;

public class DnsAPI extends ApiImplementor {
private static final String PREFIX = "simpleExample";

private static final String ACTION_HELLO_WORLD = "helloWorld";

private static final Logger LOGGER = LogManager.getLogger(DnsAPI.class);

public DnsAPI() {
this.addApiAction(new ApiAction(ACTION_HELLO_WORLD));
}

@Override
public String getPrefix() {
return PREFIX;
}

@Override
public ApiResponse handleApiAction(String name, JSONObject params) throws ApiException {
switch (name) {
case ACTION_HELLO_WORLD:
LOGGER.debug("hello world called");
break;

default:
throw new ApiException(ApiException.Type.BAD_ACTION);
}

return ApiResponseElement.OK;
}
}
140 changes: 140 additions & 0 deletions addOns/dns/src/main/java/org/zaproxy/addon/dns/DnsPassiveScanner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* 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.List;
import javax.naming.NamingEnumeration;
import org.apache.commons.httpclient.URI;
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.Alert;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.zap.extension.pscan.PluginPassiveScanner;

public class DnsPassiveScanner extends PluginPassiveScanner {
private static final Logger LOGGER = LogManager.getLogger(DnsPassiveScanner.class);
private volatile boolean enabled = true;
private static List<String> reviewedDomains = new ArrayList<String>();

@Override
public void scanHttpRequestSend(HttpMessage msg, int id) {
super.scanHttpRequestSend(msg, id);

URI uri = msg.getRequestHeader().getURI();

try {
String host = uri.getHost();
if (reviewedDomains.contains(host)) {
return;
}
reviewedDomains.add(host);
LOGGER.info("HOST: " + host);
List<String> txtRecord = getTxtRecord(host);
checkSpfRecord(txtRecord);
} catch (URIException e) {
e.printStackTrace();
}
}

private void checkSpfRecord(List<String> txtRecord) {
int entries = 0;
for (String entry : txtRecord) {
if (!entry.startsWith("v=spf1 ")) {
continue;
}
LOGGER.info("SPF record: " + entry);
entries++;
}

if (entries == 0) {
newAlert()
.setRisk(Alert.RISK_INFO)
.setConfidence(Alert.CONFIDENCE_MEDIUM)
.setDescription(
Constant.messages.getString("dns.nospfrecord.desc")
+ "\n"
+ Constant.messages.getString("dns.nospfrecord.desclong"))
.setOtherInfo("")
.setSolution("")
.setReference("")
.setEvidence("")
.raise();
} else if (entries > 1) {
newAlert()
.setRisk(Alert.RISK_INFO)
.setConfidence(Alert.CONFIDENCE_MEDIUM)
.setDescription(
Constant.messages.getString("dns.toomanyspfrecords.desc")
+ "\n"
+ Constant.messages.getString("dns.toomanyspfrecords.desclong"))
.setOtherInfo("")
.setSolution("")
.setReference("")
.setEvidence("")
.raise();
}
}

private ArrayList<String> getTxtRecord(String hostName) {
java.util.Hashtable<String, String> env = new java.util.Hashtable<String, String>();
env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
ArrayList<String> result = new ArrayList<String>();

try {
javax.naming.directory.DirContext dirContext =
new javax.naming.directory.InitialDirContext(env);
javax.naming.directory.Attributes attrs =
dirContext.getAttributes(hostName, new String[] {"TXT"});
javax.naming.directory.Attribute attr = attrs.get("TXT");

NamingEnumeration<?> attrenum = attr.getAll();
while (attrenum.hasMore()) {
result.add(attrenum.next().toString());
}

} catch (javax.naming.NamingException e) {
e.printStackTrace();
}
return result;
}

@Override
public String getName() {
return Constant.messages.getString("dns.scanner");
}

@Override
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

@Override
public boolean isEnabled() {
return enabled;
}

@Override
public boolean appliesToHistoryType(int historyType) {
return PluginPassiveScanner.getDefaultHistoryTypes().contains(historyType);
}
}
Loading

0 comments on commit 84c68a0

Please sign in to comment.