Added a preview gif, stripped pointless functions and provider simple ways to do advanced stuff via STDIN

This commit is contained in:
Dionysus 2024-03-11 00:45:28 -04:00
parent ecbd8139f4
commit a775d61b83
Signed by: acidvegas
GPG Key ID: EF4B922DB85DC9DE
6 changed files with 118 additions and 153 deletions

BIN
.screens/preview.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,45 +1,40 @@
# Mass DNS AXFR (Zone Transfer)
![](./.screens/preview.gif)
## Information
MDAXFR allows you to perform a DNS [Zone Transfer](https://en.wikipedia.org/wiki/DNS_zone_transfer) against a target domain by resolving all of the domains nameservers to their respective A/AAAA records and making an AXFR attempt against each of the IP addresses.
You can also use this tool against the [Root Nameservers](https://en.wikipedia.org/wiki/Root_name_server) and [Top-level Domains](https://en.wikipedia.org/wiki/Top-level_domain) *(TLD)*, including those in the [Public Suffix List](https://en.wikipedia.org/wiki/Public_Suffix_List) *(PSL)* aswell.
![](.screens/preview_ripe.png)
![](.screens/preview_root.png)
## Expectations & Legalities
It is expected to set *realistic* expectations when using this tool. In contemporary network configurations, AXFR requests are typically restricted, reflecting best practices in DNS security. While many nameservers now disallow AXFR requests, there may still be occasional instances where configurations permit them. Always exercise due diligence and ensure ethical use.
## Usage
### POSIX Version
## Usage:
- AXFR a single domain
```shell
./mdaxfr <option>
./mdaxfr ripe.net
```
###### Options
| Argument | Description |
| ------------- | ------------------------------------------------------------------------------------------- |
| `-tld`, | Perform AXFR on all TLDs |
| `-psl`, | Perform AXFR on all PSL TLDs |
| `<axfr_file>` | Perform AXFR on all domains found in `<axfr_file>` *(must be an AXFR output file from dig)* |
| `<domain>` | Perform AXFR on `<domain>` |
### Python Version
- AXFR a list of domains
```shell
python mdaxfr.py <option>
cat domain_list.txt | ./mdaxfr
```
###### Requirements
- [dnspython](https://pypi.org/project/dnspython/) *(`pip install dnspython`)*
- AXFR all domains in an AXFR output file
```shell
domain="ripe.net" cat axfr-ripe.log | grep -aE "\s+IN\s+NS\s+" | grep -avE "^${domain}\.\s+" | awk '{print $1}' | sort -u | sed 's/\.$//' | ./mdaxfr
```
###### Options
| Argument | Description |
| --------------------- | ---------------------------------------------------- |
| `-c`, `--concurrency` | Maximum concurrent tasks. |
| `-o`, `--output` | Specify the output directory *(default is axfrout)*. |
| `-t`, `--timeout` | DNS timeout *(default: 30)* |
###### You can also use this tool against the [Root Nameservers](https://en.wikipedia.org/wiki/Root_name_server) and [Top-level Domains](https://en.wikipedia.org/wiki/Top-level_domain) *(TLD)*, including those in the [Public Suffix List](https://en.wikipedia.org/wiki/Public_Suffix_List) *(PSL)* aswell.
- AXFR on all TLDs
```shell
curl -s 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt' | tail -n +2 | tr '[:upper:]' '[:lower:] | ./mdaxfr
```
- AXFR on all PSL TLDs
```shell
curl -s https://publicsuffix.org/list/public_suffix_list.dat | grep -vE '^(//|.*[*!])' | grep '\.' | awk '{print $1}' | ./mdaxfr
```
## Statistics, laughs, & further thinking...
I only wrote this to shit on **[this bozo](https://github.com/flotwig/TLDR-2/)** who took a dead project & brought it back to life by making it even worse. Rather than making a pull request to give this bloke more credit in his "tenure" as a developer, I decided to just rewrite it all from scratch so people can fork off of *clean* code instead.

View File

@ -3,6 +3,7 @@
import logging
import os
import re
import urllib.request
try:
@ -14,53 +15,50 @@ except ImportError:
raise SystemExit('missing required \'dnspython\' module (pip install dnspython)')
def attempt_axfr(tld: str, nameserver: str, filename: str):
'''
Perform a DNS zone transfer on a target domain.
# Colours
BLUE = '\033[1;34m'
CYAN = '\033[1;36m'
GREEN = '\033[1;32m'
GREY = '\033[1;90m'
PINK = '\033[1;95m'
PURPLE = '\033[0;35m'
RED = '\033[1;31m'
YELLOW = '\033[1;33m'
RESET = '\033[0m'
:param target: The target domain to perform the zone transfer on.
def attempt_axfr(domain: str, nameserver: str, nameserver_ip: str):
'''
Request a zone transfer from a nameserver on a domain.
:param domain: The domain to perform the zone transfer on.
:param nameserver: The nameserver to perform the zone transfer on.
:param filename: The filename to store the zone transfer results in.
:param nameserver_ip: The IP address of the nameserver.
'''
temp_file = filename + '.temp'
if not (resolvers := resolve_nameserver(nameserver)):
logging.error(f'Failed to resolve nameserver {nameserver}: {ex}')
else:
for ns in resolvers: # Let's try all the IP addresses for the nameserver
try:
xfr = dns.query.xfr(ns, tld, lifetime=300)
if next(xfr, None) is not None:
if not tld:
print(f'\033[32mSUCCESS\033[0m AXFR for \033[36m.\033[0m on \033[33m{nameserver}\033[0m \033[90m({ns})\033[0m')
else:
print(f'\033[32mSUCCESS\033[0m AXFR for \033[36m{tld}\033[0m on \033[33m{nameserver}\033[0m \033[90m({ns})\033[0m')
with open(temp_file, 'w') as file:
for msg in xfr:
for rrset in msg.answer:
for rdata in rrset:
file.write(f'{rrset.name}.{tld} {rrset.ttl} {rdata}\n')
os.rename(temp_file, filename)
break
except Exception as ex:
#logging.error(f'Failed to perform zone transfer from {nameserver} ({ns}) for {tld}: {ex}')
print(f'\033[31mFAIL\033[0m AXFR for \033[36m{tld}\033[0m on \033[33m{nameserver}\033[0m \033[90m({ns})\033[0m has failed! \033[90m({ex})\033[0m')
if os.path.exists(temp_file):
os.remove(temp_file)
print(f' {YELLOW}Attempting AXFR for {CYAN}{domain}{RESET} on {PURPLE}{nameserver} {GREY}({nameserver_ip}){RESET}')
zone = dns.zone.from_xfr(dns.query.xfr(nameserver_ip, domain))
record_count = sum(len(node.rdatasets) for node in zone.nodes.values())
print(f' {GREEN}AXFR successful for {CYAN}{domain}{RESET} on {PURPLE}{nameserver} {GREY}({nameserver_ip}){RESET} - {record_count:,} records')
with open(os.path.join('axfrout', f'{domain}_{nameserver}_{nameserver_ip}.log'), 'w') as file:
file.write(zone.to_text())
def get_nameservers(target: str) -> list:
def get_nameservers(domain: str) -> list:
'''
Generate a list of the root nameservers.
:param target: The target domain to get the nameservers for.
'''
try:
ns_records = dns.resolver.resolve(target, 'NS', lifetime=60)
ns_records = dns.resolver.resolve(domain, 'NS', lifetime=30)
nameservers = [str(rr.target)[:-1] for rr in ns_records]
return nameservers
except Exception as ex:
print(f'\033[31mFAIL\033[0m Error resolving nameservers for \033[36m{target}\033[0m \033[90m({ex})\033[0m')
return []
def get_root_tlds(output_dir: str) -> list:
@ -100,24 +98,62 @@ def resolve_nameserver(nameserver: str) -> list:
:param nameserver: The nameserver to resolve.
'''
data = []
for version in ('A', 'AAAA'):
try:
data += [ip.address for ip in dns.resolver.resolve(nameserver, version, lifetime=60)]
except:
pass
data.extend([ip.address for ip in dns.resolver.resolve(nameserver, version, lifetime=30)])
return data
def process_domain(domain: str):
domain = re.sub(r'^https?://|^(www\.)|(/.*$)', '', domain)
print(f'{PINK}Looking up nameservers for {CYAN}{domain}{RESET}')
try:
nameservers = get_nameservers(domain)
except Exception as ex:
print(f' {RED}Error resolving nameservers for {CYAN}{domain} {GREY}({ex}){RESET}')
return
if not nameservers:
print(f' {GREY}No nameservers found for {CYAN}{domain}{RESET}')
return
print(f' {BLUE}Found {len(nameservers):,} nameservers for {CYAN}{domain}{RESET}')
for nameserver in nameservers:
print(f' {PINK}Looking up IP addresses for {PURPLE}{nameserver}{RESET}')
try:
nameserver_ips = resolve_nameserver(nameserver)
except Exception as ex:
print(f' {RED}Error resolving IP addresses for {PURPLE}{nameserver} {GREY}({ex}){RESET}')
continue
if not nameserver_ips:
print(f' {GREY}No IP addresses found for {PURPLE}{nameserver}{RESET}')
continue
print(f' {BLUE}Found {len(nameserver_ips):,} IP addresses for {PURPLE}{nameserver}{RESET}')
for nameserver_ip in nameserver_ips:
attempt_axfr(domain, nameserver, nameserver_ip)
if __name__ == '__main__':
import argparse
import concurrent.futures
parser = argparse.ArgumentParser(description='Mass DNS AXFR')
parser.add_argument('-d', '--domain', type=str,help='domain to perform AXFR on')
parser.add_argument('-i', '--input', type=str, help='input directory')
parser.add_argument('-t', '--tld', type=str, help='TLD to perform AXFR on')
parser.add_argument('-p', '--psl', action='store_true', help='use the Public Suffix List')
parser.add_argument('-c', '--concurrency', type=int, default=30, help='maximum concurrent tasks')
parser.add_argument('-o', '--output', default='axfrout', help='output directory')
parser.add_argument('-t', '--timeout', type=int, default=15, help='DNS timeout (default: 15)')
args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

96
mdaxfr
View File

@ -1,6 +1,20 @@
#!/bin/sh
# Mass DNS AXFR (POSIX version) - developed by acidvegas (https://git.acid.vegas/mdaxfr)
# Usage:
# AXFR on a single domain:
# ./mdaxfr <domain>
# AXFR on a list of domains:
# cat domain_list.txt | ./mdaxfr
# AXFR on all domains in an AXFR output file:
# domain="ripe.net" cat axfr-ripe.log | grep -aE "\s+IN\s+NS\s+" | grep -avE "^${domain}\.\s+" | awk '{print $1}' | sort -u | sed 's/\.$//' | ./mdaxfr
# AXFR on all TLDs:
# curl -s 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt' | tail -n +2 | tr '[:upper:]' '[:lower:]' | ./mdaxfr
# AXFR on all PSL TLDs:
# curl -s https://publicsuffix.org/list/public_suffix_list.dat | grep -vE '^(//|.*[*!])' | grep '\.' | awk '{print $1}' | ./mdaxfr
# AXFR one-liner to rule them all:
# curl -s https://www.internic.net/domain/root.zone | awk '$4=="A" || $4=="AAAA" {print substr($1, 3) " " $5}' | sed 's/\.$//' | xargs -n2 sh -c 'dig AXFR "$0" "@$1"'
# Colors
BLUE="\033[1;34m"
CYAN="\033[1;36m"
@ -16,7 +30,6 @@ RESET="\033[0m"
output_dir="axfrout"
mkdir -p $output_dir
axfr() {
domain=$1
ns=$2
@ -36,7 +49,6 @@ axfr() {
fi
}
process_domain() {
domain=$1
@ -65,90 +77,12 @@ process_domain() {
axfr "$domain" "$ns" "$ip"
done
done
}
read_axfr_out() {
axfr_output_file=$1
root=$(grep -m1 '^; <<>> DiG' $axfr_output_file | awk '{print $NF}')
domains=$(grep -aE "\s+IN\s+NS\s+" "$axfr_output_file" | grep -avE "^${root}\.\s+" | awk '{print $1}' | sort -u | sed 's/\.$//')
[ -z "$domains" ] && echo "${GREY}No domains found for ${root}${RESET}" && return
total_domains=$(echo "$domains" | wc -l)
echo "${BLUE}Found ${total_domains} domains for ${CYAN}${root}${BLUE} zone"
for domain in $domains; do
process_domain $domain
done
}
psl_crawl() {
psl=$(curl -s https://publicsuffix.org/list/public_suffix_list.dat | grep -vE '^(//|.*[*!])' | grep '\.' | awk '{print $1}')
[ -z "$psl" ] && echo "${RED}No PSL TLDs found${RESET}" && exit 1
total_psl=$(echo "$psl" | wc -l)
echo "${BLUE}Found ${total_psl} PSL TLDs${RESET}"
# Process the PSL TLDs
for tld in $psl; do
process_domain $tld
done
}
tld_crawl() {
process_domain "."
rndroot=$(find $output_dir/*.root-servers.net.txt -type f | shuf -n 1)
if [ -z $rndroot ]; then
echo "${GREY}No root nameserver found, using IANA TLD list${RESET}"
tlds=$(curl -s 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt' | tail -n +2 | tr '[:upper:]' '[:lower:]')
else
tlds=$(cat $rndroot | grep -aE '\s+IN\s+NS\s+' | grep -avE "^\.\s+" | awk '{print $1}' | sed 's/\.$//' | sort -u)
fi
[ -z "$tlds" ] && echo "${RED}No TLDs found${RESET}" && exit 1
total_tld=$(echo "$tlds" | wc -l)
echo "${BLUE}Found ${total_tld} TLDs${RESET}"
# Process the TLDs
for tld in $tlds; do
process_domain $tld
done
}
if [ -t 0 ]; then
if [ $# -ne 1 0 ]; then
echo "Usage: $0 <option>"
echo ""
echo "Options:"
echo " -tld : Perform AXFR on all TLDs"
echo " -psl : Perform AXFR on all PSL TLDs"
echo " <file> : Process AXFR output file (must be an AXFR output file from dig)"
echo " <domain> : Perform AXFR on a single domain"
echo ""
echo "Standard Input:"
echo " cat domain_list.txt | $0"
exit 1
elif [ $1 = '-tld' ]; then
tld_crawl
elif [ $1 = '-psl' ]; then
psl_crawl
elif [ -f $1 ]; then
read_axfr_out $1
else
[ $# -ne 1 ] && echo "Usage: $0 <domain> or cat domain_list.txt | $0" && exit 1
process_domain $1
fi
else
while IFS= read -r line; do
process_domain $line