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)
|
# 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.
|
||||||
|
@ -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
98
mdaxfr
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user