Added a preview gif, stripped pointless functions and provider simple ways to do advanced stuff via STDIN
This commit is contained in:
parent
ecbd8139f4
commit
a775d61b83
BIN
.screens/preview.gif
Normal file
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 |
49
README.md
49
README.md
@ -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.
|
||||
|
@ -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)
|
||||
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 []
|
||||
|
||||
ns_records = dns.resolver.resolve(domain, 'NS', lifetime=30)
|
||||
nameservers = [str(rr.target)[:-1] for rr in ns_records]
|
||||
|
||||
return nameservers
|
||||
|
||||
|
||||
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')
|
100
mdaxfr
100
mdaxfr
@ -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,92 +77,14 @@ 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
|
||||
process_domain $1
|
||||
fi
|
||||
[ $# -ne 1 ] && echo "Usage: $0 <domain> or cat domain_list.txt | $0" && exit 1
|
||||
process_domain $1
|
||||
else
|
||||
while IFS= read -r line; do
|
||||
process_domain $line
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
Loading…
Reference in New Issue
Block a user