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) # Mass DNS AXFR (Zone Transfer)
![](./.screens/preview.gif)
## Information ## 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. 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 ## 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. 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 ## Usage:
### POSIX Version - AXFR a single domain
```shell ```shell
./mdaxfr <option> ./mdaxfr ripe.net
``` ```
###### Options - AXFR a list of domains
| 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
```shell ```shell
python mdaxfr.py <option> cat domain_list.txt | ./mdaxfr
``` ```
###### Requirements - AXFR all domains in an AXFR output file
- [dnspython](https://pypi.org/project/dnspython/) *(`pip install dnspython`)* ```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 ###### 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.
| Argument | Description |
| --------------------- | ---------------------------------------------------- | - AXFR on all TLDs
| `-c`, `--concurrency` | Maximum concurrent tasks. | ```shell
| `-o`, `--output` | Specify the output directory *(default is axfrout)*. | curl -s 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt' | tail -n +2 | tr '[:upper:]' '[:lower:] | ./mdaxfr
| `-t`, `--timeout` | DNS timeout *(default: 30)* | ```
- 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... ## 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. 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 logging
import os import os
import re
import urllib.request import urllib.request
try: try:
@ -14,53 +15,50 @@ except ImportError:
raise SystemExit('missing required \'dnspython\' module (pip install dnspython)') raise SystemExit('missing required \'dnspython\' module (pip install dnspython)')
def attempt_axfr(tld: str, nameserver: str, filename: str): # Colours
''' BLUE = '\033[1;34m'
Perform a DNS zone transfer on a target domain. 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 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)): print(f' {YELLOW}Attempting AXFR for {CYAN}{domain}{RESET} on {PURPLE}{nameserver} {GREY}({nameserver_ip}){RESET}')
logging.error(f'Failed to resolve nameserver {nameserver}: {ex}')
else: zone = dns.zone.from_xfr(dns.query.xfr(nameserver_ip, domain))
for ns in resolvers: # Let's try all the IP addresses for the nameserver
try: record_count = sum(len(node.rdatasets) for node in zone.nodes.values())
xfr = dns.query.xfr(ns, tld, lifetime=300)
if next(xfr, None) is not None: print(f' {GREEN}AXFR successful for {CYAN}{domain}{RESET} on {PURPLE}{nameserver} {GREY}({nameserver_ip}){RESET} - {record_count:,} records')
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') with open(os.path.join('axfrout', f'{domain}_{nameserver}_{nameserver_ip}.log'), 'w') as file:
else: file.write(zone.to_text())
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)
def get_nameservers(target: str) -> list: def get_nameservers(domain: str) -> list:
''' '''
Generate a list of the root nameservers. Generate a list of the root nameservers.
:param target: The target domain to get the nameservers for. :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] nameservers = [str(rr.target)[:-1] for rr in ns_records]
return nameservers
except Exception as ex: return nameservers
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: def get_root_tlds(output_dir: str) -> list:
@ -100,24 +98,62 @@ def resolve_nameserver(nameserver: str) -> list:
:param nameserver: The nameserver to resolve. :param nameserver: The nameserver to resolve.
''' '''
data = [] data = []
for version in ('A', 'AAAA'): for version in ('A', 'AAAA'):
try: data.extend([ip.address for ip in dns.resolver.resolve(nameserver, version, lifetime=30)])
data += [ip.address for ip in dns.resolver.resolve(nameserver, version, lifetime=60)]
except:
pass
return data 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__': if __name__ == '__main__':
import argparse import argparse
import concurrent.futures import concurrent.futures
parser = argparse.ArgumentParser(description='Mass DNS AXFR') 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('-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() args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

98
mdaxfr
View File

@ -1,6 +1,20 @@
#!/bin/sh #!/bin/sh
# Mass DNS AXFR (POSIX version) - developed by acidvegas (https://git.acid.vegas/mdaxfr) # 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 # Colors
BLUE="\033[1;34m" BLUE="\033[1;34m"
CYAN="\033[1;36m" CYAN="\033[1;36m"
@ -16,7 +30,6 @@ RESET="\033[0m"
output_dir="axfrout" output_dir="axfrout"
mkdir -p $output_dir mkdir -p $output_dir
axfr() { axfr() {
domain=$1 domain=$1
ns=$2 ns=$2
@ -36,7 +49,6 @@ axfr() {
fi fi
} }
process_domain() { process_domain() {
domain=$1 domain=$1
@ -65,90 +77,12 @@ process_domain() {
axfr "$domain" "$ns" "$ip" axfr "$domain" "$ns" "$ip"
done done
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 [ -t 0 ]; then
if [ $# -ne 1 0 ]; then [ $# -ne 1 ] && echo "Usage: $0 <domain> or cat domain_list.txt | $0" && exit 1
echo "Usage: $0 <option>" process_domain $1
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
process_domain $1
fi
else else
while IFS= read -r line; do while IFS= read -r line; do
process_domain $line process_domain $line