Subzy Cheat Sheet
Overview
Subzy is a fast subdomain takeover vulnerability scanner that detects dangling DNS records and identifies potential subdomain takeover opportunities. It supports multiple cloud providers and services, making it essential for comprehensive security assessments of modern web applications and cloud infrastructure.
💡 Key Features: Fast subdomain takeover detection, multi-provider support, concurrent scanning, custom fingerprinting, comprehensive reporting, and integration-friendly output formats.
Installation and Setup
Binary Installation
# Download latest release for Linux
wget https://github.com/LukaSikic/subzy/releases/latest/download/subzy_linux_amd64.tar.gz
tar -xzf subzy_linux_amd64.tar.gz
sudo mv subzy /usr/local/bin/
# Download for macOS
wget https://github.com/LukaSikic/subzy/releases/latest/download/subzy_darwin_amd64.tar.gz
tar -xzf subzy_darwin_amd64.tar.gz
sudo mv subzy /usr/local/bin/
# Download for Windows
wget https://github.com/LukaSikic/subzy/releases/latest/download/subzy_windows_amd64.zip
unzip subzy_windows_amd64.zip
# Move subzy.exe to PATH
# Verify installation
subzy --version
# Check available options
subzy --help
Go Installation
# Install via Go (requires Go 1.19+)
go install github.com/LukaSikic/subzy@latest
# Verify installation
subzy --version
# Update to latest version
go install github.com/LukaSikic/subzy@latest
# Install specific version
go install github.com/LukaSikic/subzy@v1.0.7
# Build from source
git clone https://github.com/LukaSikic/subzy.git
cd subzy
go build -o subzy main.go
sudo mv subzy /usr/local/bin/
Docker Installation
# Pull official Docker image
docker pull lukasikic/subzy:latest
# Run Subzy in Docker
docker run --rm lukasikic/subzy:latest --version
# Run with volume mount for input/output
docker run --rm -v $(pwd):/app lukasikic/subzy:latest --targets /app/subdomains.txt
# Create alias for easier usage
echo 'alias subzy="docker run --rm -v $(pwd):/app lukasikic/subzy:latest"' >> ~/.bashrc
source ~/.bashrc
# Build custom Docker image
cat > Dockerfile << EOF
FROM lukasikic/subzy:latest
RUN apk add --no-cache curl jq
COPY custom-fingerprints.json /app/
EOF
docker build -t custom-subzy .
Package Manager Installation
# Homebrew (macOS/Linux)
brew install subzy
# Arch Linux (AUR)
yay -S subzy-bin
# Snap package
sudo snap install subzy
# APT (Ubuntu/Debian) - via GitHub releases
curl -s https://api.github.com/repos/LukaSikic/subzy/releases/latest \
| grep "browser_download_url.*linux_amd64.deb" \
| cut -d '"' -f 4 \
| wget -qi -
sudo dpkg -i subzy_*.deb
# RPM (CentOS/RHEL/Fedora)
curl -s https://api.github.com/repos/LukaSikic/subzy/releases/latest \
| grep "browser_download_url.*linux_amd64.rpm" \
| cut -d '"' -f 4 \
| wget -qi -
sudo rpm -i subzy_*.rpm
Configuration and Setup
# Create configuration directory
mkdir -p ~/.subzy
# Create custom fingerprints file
cat > ~/.subzy/custom-fingerprints.json << 'EOF'
{
"fingerprints": [
{
"service": "Custom Service",
"cname": ["custom.example.com"],
"fingerprint": ["Custom error message"],
"nxdomain": false,
"discussion": "https://example.com/discussion"
}
]
}
EOF
# Create subdomain list for testing
cat > test-subdomains.txt << 'EOF'
subdomain1.example.com
subdomain2.example.com
subdomain3.example.com
api.example.com
admin.example.com
staging.example.com
dev.example.com
test.example.com
EOF
# Set environment variables
export SUBZY_CONFIG=~/.subzy/custom-fingerprints.json
export SUBZY_THREADS=50
export SUBZY_TIMEOUT=10
Basic Usage and Commands
Single Target Scanning
# Basic subdomain takeover check
subzy run --target subdomain.example.com
# Check with verbose output
subzy run --target subdomain.example.com --verbose
# Check with custom timeout
subzy run --target subdomain.example.com --timeout 30
# Check with custom user agent
subzy run --target subdomain.example.com --user-agent "Custom Agent"
# Check with proxy
subzy run --target subdomain.example.com --proxy http://127.0.0.1:8080
# Check with custom DNS resolver
subzy run --target subdomain.example.com --resolver 8.8.8.8
# Check with HTTPS verification disabled
subzy run --target subdomain.example.com --https
# Save results to file
subzy run --target subdomain.example.com --output results.txt
Multiple Target Scanning
# Scan multiple targets from file
echo -e "sub1.example.com\nsub2.example.com" > targets.txt
subzy run --targets targets.txt
# Scan with threading
subzy run --targets targets.txt --concurrency 50
# Scan with custom output format
subzy run --targets targets.txt --output results.json
# Scan with verbose logging
subzy run --targets targets.txt --verbose --output detailed-results.txt
# Scan with custom fingerprints
subzy run --targets targets.txt --fingerprints custom-fingerprints.json
# Scan and save only vulnerable targets
subzy run --targets targets.txt --output vulnerable.txt --vulnerable-only
# Scan with rate limiting
subzy run --targets targets.txt --concurrency 10 --timeout 15
Advanced Scanning Options
# Scan with all available checks
subzy run --targets targets.txt --verify-ssl --https --verbose
# Scan with custom DNS settings
subzy run --targets targets.txt --resolver 1.1.1.1 --timeout 20
# Scan with proxy and custom headers
subzy run --targets targets.txt \
--proxy http://127.0.0.1:8080 \
--user-agent "Mozilla/5.0 Custom Agent" \
--timeout 30
# Scan with output in different formats
subzy run --targets targets.txt --output results.json --format json
subzy run --targets targets.txt --output results.csv --format csv
subzy run --targets targets.txt --output results.xml --format xml
# Scan with custom fingerprint validation
subzy run --targets targets.txt --fingerprints custom.json --validate-fingerprints
# Scan with detailed error reporting
subzy run --targets targets.txt --verbose --debug --output debug-results.txt
Advanced Subdomain Takeover Detection
Custom Fingerprint Development
# Create advanced custom fingerprints file
{
"fingerprints": [
{
"service": "AWS S3",
"cname": ["s3.amazonaws.com", "s3-website"],
"fingerprint": [
"NoSuchBucket",
"The specified bucket does not exist"
],
"nxdomain": false,
"discussion": "https://github.com/EdOverflow/can-i-take-over-xyz"
},
{
"service": "GitHub Pages",
"cname": ["github.io", "githubusercontent.com"],
"fingerprint": [
"There isn't a GitHub Pages site here.",
"For root URLs (like http://example.com/) you must provide an index.html file"
],
"nxdomain": false,
"discussion": "https://github.com/EdOverflow/can-i-take-over-xyz"
},
{
"service": "Heroku",
"cname": ["herokuapp.com", "herokussl.com"],
"fingerprint": [
"No such app",
"There's nothing here, yet."
],
"nxdomain": false,
"discussion": "https://github.com/EdOverflow/can-i-take-over-xyz"
},
{
"service": "Netlify",
"cname": ["netlify.com", "netlifyglobalcdn.com"],
"fingerprint": [
"Not Found - Request ID:",
"Site Not Found"
],
"nxdomain": false,
"discussion": "https://github.com/EdOverflow/can-i-take-over-xyz"
},
{
"service": "Azure",
"cname": ["azurewebsites.net", "cloudapp.net"],
"fingerprint": [
"404 Web Site not found",
"This site is stopped"
],
"nxdomain": false,
"discussion": "https://github.com/EdOverflow/can-i-take-over-xyz"
},
{
"service": "Shopify",
"cname": ["myshopify.com"],
"fingerprint": [
"Sorry, this shop is currently unavailable",
"Only one step left!"
],
"nxdomain": false,
"discussion": "https://github.com/EdOverflow/can-i-take-over-xyz"
},
{
"service": "Fastly",
"cname": ["fastly.com", "fastlylb.net"],
"fingerprint": [
"Fastly error: unknown domain",
"Please check that this domain has been added to a service"
],
"nxdomain": false,
"discussion": "https://github.com/EdOverflow/can-i-take-over-xyz"
},
{
"service": "CloudFront",
"cname": ["cloudfront.net"],
"fingerprint": [
"Bad Request: ERROR: The request could not be satisfied",
"The distribution does not exist"
],
"nxdomain": false,
"discussion": "https://github.com/EdOverflow/can-i-take-over-xyz"
},
{
"service": "Bitbucket",
"cname": ["bitbucket.org"],
"fingerprint": [
"Repository not found"
],
"nxdomain": false,
"discussion": "https://github.com/EdOverflow/can-i-take-over-xyz"
},
{
"service": "Tumblr",
"cname": ["tumblr.com"],
"fingerprint": [
"Whatever you were looking for doesn't currently exist at this address"
],
"nxdomain": false,
"discussion": "https://github.com/EdOverflow/can-i-take-over-xyz"
}
]
}
Automated Subdomain Discovery and Takeover Detection
#!/usr/bin/env python3
# Comprehensive subdomain takeover detection automation
import subprocess
import json
import threading
import time
import dns.resolver
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.parse import urlparse
import re
class SubdomainTakeoverScanner:
def __init__(self, max_workers=50, timeout=10):
self.max_workers = max_workers
self.timeout = timeout
self.results = []
self.lock = threading.Lock()
self.vulnerable_services = []
def discover_subdomains(self, domain, methods=['subfinder', 'amass', 'assetfinder']):
"""Discover subdomains using multiple tools"""
all_subdomains = set()
for method in methods:
try:
if method == 'subfinder':
result = subprocess.run(
['subfinder', '-d', domain, '-silent'],
capture_output=True, text=True, timeout=300
)
if result.returncode == 0:
subdomains = result.stdout.strip().split('\n')
all_subdomains.update([s for s in subdomains if s])
elif method == 'amass':
result = subprocess.run(
['amass', 'enum', '-d', domain, '-passive'],
capture_output=True, text=True, timeout=600
)
if result.returncode == 0:
subdomains = result.stdout.strip().split('\n')
all_subdomains.update([s for s in subdomains if s])
elif method == 'assetfinder':
result = subprocess.run(
['assetfinder', '--subs-only', domain],
capture_output=True, text=True, timeout=300
)
if result.returncode == 0:
subdomains = result.stdout.strip().split('\n')
all_subdomains.update([s for s in subdomains if s])
except subprocess.TimeoutExpired:
print(f"Timeout during {method} subdomain discovery")
except FileNotFoundError:
print(f"{method} not found, skipping...")
except Exception as e:
print(f"Error with {method}: {e}")
return list(all_subdomains)
def check_dns_record(self, subdomain):
"""Check DNS records for subdomain"""
dns_info = {
'subdomain': subdomain,
'a_records': [],
'cname_records': [],
'mx_records': [],
'ns_records': [],
'txt_records': [],
'nxdomain': False,
'error': None
}
try:
# Check A records
try:
answers = dns.resolver.resolve(subdomain, 'A')
dns_info['a_records'] = [str(rdata) for rdata in answers]
except dns.resolver.NXDOMAIN:
dns_info['nxdomain'] = True
except dns.resolver.NoAnswer:
pass
except Exception as e:
dns_info['error'] = str(e)
# Check CNAME records
try:
answers = dns.resolver.resolve(subdomain, 'CNAME')
dns_info['cname_records'] = [str(rdata) for rdata in answers]
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
pass
except Exception as e:
if not dns_info['error']:
dns_info['error'] = str(e)
# Check MX records
try:
answers = dns.resolver.resolve(subdomain, 'MX')
dns_info['mx_records'] = [str(rdata) for rdata in answers]
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
pass
except Exception:
pass
# Check NS records
try:
answers = dns.resolver.resolve(subdomain, 'NS')
dns_info['ns_records'] = [str(rdata) for rdata in answers]
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
pass
except Exception:
pass
# Check TXT records
try:
answers = dns.resolver.resolve(subdomain, 'TXT')
dns_info['txt_records'] = [str(rdata) for rdata in answers]
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
pass
except Exception:
pass
except Exception as e:
dns_info['error'] = str(e)
return dns_info
def check_http_response(self, subdomain):
"""Check HTTP response for takeover indicators"""
http_info = {
'subdomain': subdomain,
'status_code': None,
'response_body': '',
'headers': {},
'redirect_url': None,
'error': None,
'takeover_indicators': []
}
# Common takeover indicators
takeover_patterns = {
'aws_s3': [
'NoSuchBucket',
'The specified bucket does not exist'
],
'github_pages': [
"There isn't a GitHub Pages site here",
'For root URLs (like http://example.com/) you must provide an index.html file'
],
'heroku': [
'No such app',
"There's nothing here, yet"
],
'netlify': [
'Not Found - Request ID:',
'Site Not Found'
],
'azure': [
'404 Web Site not found',
'This site is stopped'
],
'shopify': [
'Sorry, this shop is currently unavailable',
'Only one step left!'
],
'fastly': [
'Fastly error: unknown domain',
'Please check that this domain has been added to a service'
],
'cloudfront': [
'Bad Request: ERROR: The request could not be satisfied',
'The distribution does not exist'
]
}
try:
# Try HTTPS first, then HTTP
for protocol in ['https', 'http']:
try:
url = f"{protocol}://{subdomain}"
response = requests.get(
url,
timeout=self.timeout,
allow_redirects=True,
verify=False
)
http_info['status_code'] = response.status_code
http_info['response_body'] = response.text[:5000] # Limit body size
http_info['headers'] = dict(response.headers)
if response.history:
http_info['redirect_url'] = response.url
# Check for takeover indicators
response_text = response.text.lower()
for service, patterns in takeover_patterns.items():
for pattern in patterns:
if pattern.lower() in response_text:
http_info['takeover_indicators'].append({
'service': service,
'pattern': pattern,
'confidence': 'high'
})
break # Success, no need to try other protocol
except requests.exceptions.SSLError:
if protocol == 'https':
continue # Try HTTP
else:
http_info['error'] = 'SSL Error'
except requests.exceptions.ConnectionError:
if protocol == 'https':
continue # Try HTTP
else:
http_info['error'] = 'Connection Error'
except requests.exceptions.Timeout:
http_info['error'] = 'Timeout'
break
except Exception as e:
http_info['error'] = str(e)
break
except Exception as e:
http_info['error'] = str(e)
return http_info
def run_subzy_scan(self, subdomain):
"""Run Subzy scan on subdomain"""
try:
result = subprocess.run(
['subzy', 'run', '--target', subdomain, '--timeout', str(self.timeout)],
capture_output=True, text=True, timeout=self.timeout + 10
)
subzy_result = {
'subdomain': subdomain,
'vulnerable': False,
'service': None,
'output': result.stdout,
'error': result.stderr if result.returncode != 0 else None
}
# Parse Subzy output for vulnerabilities
if 'VULNERABLE' in result.stdout.upper() or 'TAKEOVER' in result.stdout.upper():
subzy_result['vulnerable'] = True
# Try to extract service name
lines = result.stdout.split('\n')
for line in lines:
if 'service:' in line.lower() or 'provider:' in line.lower():
subzy_result['service'] = line.split(':')[-1].strip()
break
return subzy_result
except subprocess.TimeoutExpired:
return {
'subdomain': subdomain,
'vulnerable': False,
'service': None,
'output': '',
'error': 'Subzy scan timeout'
}
except Exception as e:
return {
'subdomain': subdomain,
'vulnerable': False,
'service': None,
'output': '',
'error': str(e)
}
def comprehensive_scan(self, subdomain):
"""Perform comprehensive subdomain takeover scan"""
print(f"Scanning: {subdomain}")
# Get DNS information
dns_info = self.check_dns_record(subdomain)
# Get HTTP response information
http_info = self.check_http_response(subdomain)
# Run Subzy scan
subzy_info = self.run_subzy_scan(subdomain)
# Combine results
scan_result = {
'subdomain': subdomain,
'timestamp': time.time(),
'dns': dns_info,
'http': http_info,
'subzy': subzy_info,
'vulnerable': False,
'confidence': 'low',
'risk_factors': []
}
# Analyze results for vulnerability indicators
risk_factors = []
# Check DNS indicators
if dns_info['nxdomain']:
risk_factors.append('NXDOMAIN response')
if dns_info['cname_records']:
for cname in dns_info['cname_records']:
# Check for common vulnerable services in CNAME
vulnerable_cnames = [
'github.io', 'herokuapp.com', 'netlify.com',
'azurewebsites.net', 's3.amazonaws.com', 'cloudfront.net'
]
for vcname in vulnerable_cnames:
if vcname in cname:
risk_factors.append(f'Vulnerable CNAME: {cname}')
# Check HTTP indicators
if http_info['takeover_indicators']:
for indicator in http_info['takeover_indicators']:
risk_factors.append(f"HTTP indicator: {indicator['service']} - {indicator['pattern']}")
# Check Subzy results
if subzy_info['vulnerable']:
risk_factors.append(f"Subzy detected vulnerability: {subzy_info['service']}")
scan_result['vulnerable'] = True
scan_result['confidence'] = 'high'
# Determine overall vulnerability status
if len(risk_factors) >= 2:
scan_result['vulnerable'] = True
scan_result['confidence'] = 'medium' if scan_result['confidence'] == 'low' else scan_result['confidence']
scan_result['risk_factors'] = risk_factors
with self.lock:
self.results.append(scan_result)
if scan_result['vulnerable']:
self.vulnerable_services.append(scan_result)
return scan_result
def scan_subdomains(self, subdomains):
"""Scan multiple subdomains concurrently"""
print(f"Starting comprehensive scan of {len(subdomains)} subdomains")
print(f"Max workers: {self.max_workers}")
print(f"Timeout: {self.timeout}s")
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
# Submit all tasks
future_to_subdomain = {
executor.submit(self.comprehensive_scan, subdomain): subdomain
for subdomain in subdomains
}
# Process completed tasks
for future in as_completed(future_to_subdomain):
subdomain = future_to_subdomain[future]
try:
result = future.result()
if result['vulnerable']:
print(f"🚨 VULNERABLE: {subdomain} - {', '.join(result['risk_factors'])}")
else:
print(f"✓ Safe: {subdomain}")
except Exception as e:
print(f"✗ Error scanning {subdomain}: {e}")
return self.results
def generate_report(self, output_file='subdomain_takeover_report.json'):
"""Generate comprehensive report"""
# Calculate statistics
total_subdomains = len(self.results)
vulnerable_subdomains = len(self.vulnerable_services)
report = {
'scan_summary': {
'total_subdomains': total_subdomains,
'vulnerable_subdomains': vulnerable_subdomains,
'vulnerability_rate': (vulnerable_subdomains / total_subdomains * 100) if total_subdomains > 0 else 0,
'scan_date': time.strftime('%Y-%m-%d %H:%M:%S')
},
'vulnerable_subdomains': self.vulnerable_services,
'all_results': self.results
}
# Save report
with open(output_file, 'w') as f:
json.dump(report, f, indent=2)
print(f"\nReport saved to: {output_file}")
# Generate summary
print("\nScan Summary:")
print(f"Total subdomains: {total_subdomains}")
print(f"Vulnerable subdomains: {vulnerable_subdomains}")
print(f"Vulnerability rate: {report['scan_summary']['vulnerability_rate']:.1f}%")
if self.vulnerable_services:
print("\nVulnerable Subdomains:")
for vuln in self.vulnerable_services:
print(f"- {vuln['subdomain']}: {', '.join(vuln['risk_factors'])}")
return report
# Usage example
if __name__ == "__main__":
# Create scanner instance
scanner = SubdomainTakeoverScanner(max_workers=20, timeout=10)
# Discover subdomains
domain = "example.com"
print(f"Discovering subdomains for {domain}...")
subdomains = scanner.discover_subdomains(domain)
print(f"Found {len(subdomains)} subdomains")
# Scan for takeover vulnerabilities
results = scanner.scan_subdomains(subdomains)
# Generate report
report = scanner.generate_report('comprehensive_takeover_report.json')
Automation and Integration
CI/CD Integration
#!/bin/bash
# CI/CD script for subdomain takeover detection
set -e
DOMAIN="$1"
OUTPUT_DIR="$2"
THRESHOLD="$3"
if [ -z "$DOMAIN" ] || [ -z "$OUTPUT_DIR" ]; then
echo "Usage: $0 <domain> <output_dir> [threshold]"
exit 1
fi
THRESHOLD=${THRESHOLD:-0} # Default: fail on any takeover found
echo "Starting subdomain takeover scan..."
echo "Domain: $DOMAIN"
echo "Output directory: $OUTPUT_DIR"
echo "Threshold: $THRESHOLD vulnerabilities"
mkdir -p "$OUTPUT_DIR"
# Discover subdomains
echo "Discovering subdomains..."
subfinder -d "$DOMAIN" -silent > "$OUTPUT_DIR/subdomains.txt" 2>/dev/null || echo "Subfinder not available"
amass enum -d "$DOMAIN" -passive >> "$OUTPUT_DIR/subdomains.txt" 2>/dev/null || echo "Amass not available"
assetfinder --subs-only "$DOMAIN" >> "$OUTPUT_DIR/subdomains.txt" 2>/dev/null || echo "Assetfinder not available"
# Remove duplicates and empty lines
sort "$OUTPUT_DIR/subdomains.txt" | uniq | grep -v '^$' > "$OUTPUT_DIR/unique_subdomains.txt"
SUBDOMAIN_COUNT=$(wc -l < "$OUTPUT_DIR/unique_subdomains.txt")
echo "Found $SUBDOMAIN_COUNT unique subdomains"
# Run Subzy scan
echo "Running Subzy scan..."
subzy run --targets "$OUTPUT_DIR/unique_subdomains.txt" \
--concurrency 50 \
--timeout 15 \
--output "$OUTPUT_DIR/subzy_results.txt" \
--verbose
# Parse results
VULN_COUNT=$(grep -c "VULNERABLE\|TAKEOVER" "$OUTPUT_DIR/subzy_results.txt" || echo "0")
echo "Found $VULN_COUNT potential subdomain takeover vulnerabilities"
# Generate summary report
cat > "$OUTPUT_DIR/takeover-summary.txt" << EOF
Subdomain Takeover Scan Summary
==============================
Date: $(date)
Domain: $DOMAIN
Total Subdomains: $SUBDOMAIN_COUNT
Potential Vulnerabilities: $VULN_COUNT
Threshold: $THRESHOLD
Status: $(if [ "$VULN_COUNT" -le "$THRESHOLD" ]; then echo "PASS"; else echo "FAIL"; fi)
EOF
# Generate detailed report
python3 << 'PYTHON_EOF'
import sys
import json
import re
from datetime import datetime
output_dir = sys.argv[1]
domain = sys.argv[2]
# Read Subzy results
try:
with open(f"{output_dir}/subzy_results.txt", 'r') as f:
subzy_output = f.read()
except FileNotFoundError:
subzy_output = ""
# Parse vulnerabilities
vulnerabilities = []
lines = subzy_output.split('\n')
for line in lines:
if 'VULNERABLE' in line.upper() or 'TAKEOVER' in line.upper():
# Extract subdomain and service information
parts = line.split()
if len(parts) >= 2:
subdomain = parts[0] if '://' not in parts[0] else parts[0].split('://')[-1]
service = 'Unknown'
# Try to extract service name
for part in parts:
if any(svc in part.lower() for svc in ['aws', 'github', 'heroku', 'netlify', 'azure']):
service = part
break
vulnerability = {
'subdomain': subdomain,
'service': service,
'description': line.strip(),
'severity': 'High',
'risk': 'Subdomain takeover possible'
}
vulnerabilities.append(vulnerability)
# Create detailed report
report = {
'scan_info': {
'domain': domain,
'scanner': 'Subzy',
'scan_date': datetime.now().isoformat(),
'vulnerability_count': len(vulnerabilities)
},
'vulnerabilities': vulnerabilities,
'raw_output': subzy_output
}
# Save JSON report
with open(f"{output_dir}/subzy-report.json", 'w') as f:
json.dump(report, f, indent=2)
# Generate HTML report
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>Subdomain Takeover Scan Report</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; }}
.header {{ background-color: #f0f0f0; padding: 20px; border-radius: 5px; }}
.vuln {{ margin: 10px 0; padding: 15px; border-left: 4px solid #dc3545; background-color: #f8f9fa; }}
.safe {{ color: #28a745; }}
.danger {{ color: #dc3545; }}
table {{ border-collapse: collapse; width: 100%; margin: 20px 0; }}
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
th {{ background-color: #f2f2f2; }}
.code {{ background-color: #f8f9fa; padding: 10px; border-radius: 3px; font-family: monospace; }}
</style>
</head>
<body>
<div class="header">
<h1>Subdomain Takeover Scan Report</h1>
<p><strong>Domain:</strong> {domain}</p>
<p><strong>Scan Date:</strong> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
<p><strong>Vulnerabilities Found:</strong> <span class="{'danger' if len(vulnerabilities) > 0 else 'safe'}">{len(vulnerabilities)}</span></p>
</div>
<h2>Vulnerability Details</h2>
"""
if vulnerabilities:
html_content += "<table><tr><th>Subdomain</th><th>Service</th><th>Risk</th><th>Description</th></tr>"
for vuln in vulnerabilities:
html_content += f"""
<tr>
<td>{vuln.get('subdomain', '')}</td>
<td>{vuln.get('service', '')}</td>
<td>{vuln.get('risk', '')}</td>
<td><div class="code">{vuln.get('description', '')}</div></td>
</tr>
"""
html_content += "</table>"
else:
html_content += "<p class='safe'>No subdomain takeover vulnerabilities detected.</p>"
html_content += """
<h2>Raw Output</h2>
<div class="code">
<pre>{}</pre>
</div>
</body>
</html>
""".format(subzy_output.replace('<', '<').replace('>', '>'))
with open(f"{output_dir}/subzy-report.html", 'w') as f:
f.write(html_content)
print(f"Detailed reports generated:")
print(f"- JSON: {output_dir}/subzy-report.json")
print(f"- HTML: {output_dir}/subzy-report.html")
PYTHON_EOF
# Check threshold and exit
if [ "$VULN_COUNT" -gt "$THRESHOLD" ]; then
echo "ERROR: Found $VULN_COUNT vulnerabilities, exceeds threshold of $THRESHOLD"
exit 1
else
echo "SUCCESS: Vulnerability count within acceptable threshold"
exit 0
fi
GitHub Actions Integration
# .github/workflows/subdomain-takeover-scan.yml
name: Subdomain Takeover Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 4 * * 3' # Weekly scan on Wednesdays at 4 AM
jobs:
subdomain-takeover-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install subdomain discovery tools
run: |
# Install Subfinder
wget https://github.com/projectdiscovery/subfinder/releases/latest/download/subfinder_2.6.3_linux_amd64.zip
unzip subfinder_2.6.3_linux_amd64.zip
sudo mv subfinder /usr/local/bin/
# Install Assetfinder
go install github.com/tomnomnom/assetfinder@latest
# Install Subzy
wget https://github.com/LukaSikic/subzy/releases/latest/download/subzy_linux_amd64.tar.gz
tar -xzf subzy_linux_amd64.tar.gz
sudo mv subzy /usr/local/bin/
# Verify installations
subfinder -version
assetfinder --help
subzy --version
- name: Discover subdomains
run: |
mkdir -p scan-results
# Discover subdomains using multiple tools
subfinder -d ${{ vars.TARGET_DOMAIN }} -silent > scan-results/subfinder.txt
assetfinder --subs-only ${{ vars.TARGET_DOMAIN }} > scan-results/assetfinder.txt
# Combine and deduplicate
cat scan-results/subfinder.txt scan-results/assetfinder.txt | sort | uniq > scan-results/all_subdomains.txt
SUBDOMAIN_COUNT=$(wc -l < scan-results/all_subdomains.txt)
echo "SUBDOMAIN_COUNT=$SUBDOMAIN_COUNT" >> $GITHUB_ENV
echo "Found $SUBDOMAIN_COUNT unique subdomains"
- name: Run subdomain takeover scan
run: |
# Run Subzy scan
subzy run --targets scan-results/all_subdomains.txt \
--concurrency 30 \
--timeout 15 \
--output scan-results/subzy_results.txt \
--verbose
# Count vulnerabilities
VULN_COUNT=$(grep -c "VULNERABLE\|TAKEOVER" scan-results/subzy_results.txt || echo "0")
echo "VULN_COUNT=$VULN_COUNT" >> $GITHUB_ENV
# Generate summary
echo "Subdomain takeover vulnerabilities found: $VULN_COUNT" > scan-results/summary.txt
echo "Total subdomains scanned: $SUBDOMAIN_COUNT" >> scan-results/summary.txt
- name: Process scan results
run: |
# Extract vulnerable subdomains
grep "VULNERABLE\|TAKEOVER" scan-results/subzy_results.txt > scan-results/vulnerable_subdomains.txt || touch scan-results/vulnerable_subdomains.txt
# Create detailed summary
cat > scan-results/detailed_summary.txt << EOF
Subdomain Takeover Scan Results
==============================
Domain: ${{ vars.TARGET_DOMAIN }}
Scan Date: $(date)
Total Subdomains: $SUBDOMAIN_COUNT
Vulnerable Subdomains: $VULN_COUNT
Vulnerable Subdomains:
$(cat scan-results/vulnerable_subdomains.txt)
EOF
- name: Upload scan results
uses: actions/upload-artifact@v3
with:
name: subdomain-takeover-results
path: scan-results/
- name: Comment PR with results
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const summary = fs.readFileSync('scan-results/summary.txt', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## Subdomain Takeover Scan Results\n\n\`\`\`\n${summary}\n\`\`\``
});
- name: Fail if vulnerabilities found
run: |
if [ "$VULN_COUNT" -gt "0" ]; then
echo "Subdomain takeover vulnerabilities detected! Check the scan results."
cat scan-results/vulnerable_subdomains.txt
exit 1
fi
Performance Optimization and Troubleshooting
Performance Tuning
# Optimize Subzy for different scenarios
# High-speed scanning
subzy run --targets subdomains.txt \
--concurrency 100 \
--timeout 5 \
--https
# Thorough scanning
subzy run --targets subdomains.txt \
--concurrency 20 \
--timeout 30 \
--verify-ssl \
--verbose
# Memory-efficient scanning for large lists
subzy run --targets large_subdomains.txt \
--concurrency 10 \
--timeout 15 \
--output results.txt
# Network-optimized scanning
subzy run --targets subdomains.txt \
--concurrency 50 \
--timeout 10 \
--resolver 8.8.8.8
# Performance monitoring script
#!/bin/bash
monitor_subzy_performance() {
local targets_file="$1"
local output_file="subzy-performance-$(date +%s).log"
echo "Starting performance monitoring for Subzy scan"
echo "Targets file: $targets_file"
echo "Log file: $output_file"
# Start monitoring in background
{
echo "Timestamp,CPU%,Memory(MB),Targets/sec"
start_time=$(date +%s)
while true; do
if pgrep -f "subzy" > /dev/null; then
local cpu=$(ps -p $(pgrep -f "subzy") -o %cpu --no-headers | awk '{sum+=$1} END {print sum}')
local mem=$(ps -p $(pgrep -f "subzy") -o rss --no-headers | awk '{sum+=$1} END {print sum/1024}')
local current_time=$(date +%s)
local elapsed=$((current_time - start_time))
local targets_processed=$(wc -l < "$targets_file" 2>/dev/null || echo "0")
local rate=$(echo "scale=2; $targets_processed / $elapsed" | bc -l 2>/dev/null || echo "0")
echo "$(date +%s),$cpu,$mem,$rate"
fi
sleep 5
done
} > "$output_file" &
local monitor_pid=$!
# Run Subzy scan
subzy run --targets "$targets_file" \
--concurrency 50 \
--timeout 15 \
--verbose \
--output "subzy-results-$(date +%s).txt"
# Stop monitoring
kill $monitor_pid 2>/dev/null
echo "Performance monitoring completed: $output_file"
}
# Usage
monitor_subzy_performance "subdomains.txt"
Troubleshooting Common Issues
# Troubleshooting script for Subzy
troubleshoot_subzy() {
echo "Subzy Troubleshooting Guide"
echo "=========================="
# Check if Subzy is installed
if ! command -v subzy &> /dev/null; then
echo "❌ Subzy not found in PATH"
echo "Solution: Install Subzy using one of the installation methods"
return 1
fi
echo "✅ Subzy found: $(which subzy)"
echo "Version: $(subzy --version 2>&1)"
# Check network connectivity
if ! curl -s --connect-timeout 5 https://httpbin.org/get > /dev/null; then
echo "❌ Network connectivity issues"
echo "Solution: Check internet connection and proxy settings"
return 1
fi
echo "✅ Network connectivity OK"
# Test basic functionality
echo "Testing basic Subzy functionality..."
echo "test.example.com" > /tmp/test_subdomains.txt
if timeout 30 subzy run --targets /tmp/test_subdomains.txt --timeout 10 > /dev/null 2>&1; then
echo "✅ Basic functionality test passed"
else
echo "❌ Basic functionality test failed"
echo "Solution: Check Subzy installation and permissions"
return 1
fi
# Check DNS resolution
if ! nslookup google.com > /dev/null 2>&1; then
echo "❌ DNS resolution issues"
echo "Solution: Check DNS settings or use custom resolver"
echo "Example: subzy run --targets file.txt --resolver 8.8.8.8"
return 1
fi
echo "✅ DNS resolution OK"
# Check for common issues
echo "Checking for common configuration issues..."
# Check file permissions
if [ ! -r "/tmp/test_subdomains.txt" ]; then
echo "⚠️ File permission issues detected"
echo "Solution: Check read permissions on target files"
fi
# Check for proxy issues
if [ -n "$HTTP_PROXY" ] || [ -n "$HTTPS_PROXY" ]; then
echo "⚠️ Proxy environment variables detected"
echo "Note: Use --proxy option if needed"
fi
# Clean up
rm -f /tmp/test_subdomains.txt
echo "Troubleshooting completed"
}
# Common error solutions
fix_common_subzy_errors() {
echo "Common Subzy Error Solutions"
echo "==========================="
echo "1. 'no such file or directory'"
echo " Solution: Check if targets file exists and is readable"
echo " Example: ls -la targets.txt"
echo ""
echo "2. 'connection timeout'"
echo " Solution: Increase timeout or reduce concurrency"
echo " Example: subzy run --targets file.txt --timeout 30 --concurrency 10"
echo ""
echo "3. 'too many open files'"
echo " Solution: Reduce concurrency or increase system limits"
echo " Example: ulimit -n 4096 && subzy run --targets file.txt --concurrency 20"
echo ""
echo "4. 'DNS resolution failed'"
echo " Solution: Use custom DNS resolver"
echo " Example: subzy run --targets file.txt --resolver 1.1.1.1"
echo ""
echo "5. 'SSL certificate verification failed'"
echo " Solution: Disable SSL verification (use with caution)"
echo " Example: subzy run --targets file.txt --https"
echo ""
echo "6. 'permission denied'"
echo " Solution: Check file permissions and run with appropriate privileges"
echo " Example: chmod +r targets.txt"
echo ""
echo "7. 'no vulnerabilities found' (potential false negatives)"
echo " Solution: Use verbose mode and custom fingerprints"
echo " Example: subzy run --targets file.txt --verbose --fingerprints custom.json"
}
# Run troubleshooting
troubleshoot_subzy
fix_common_subzy_errors
Resources and Documentation
Official Resources
- Subzy GitHub Repository - Main repository and documentation
- Release Notes - Latest updates and changes
- Bug Reports - Issue tracking and bug reports
- Docker Hub - Official Docker images
Community Resources
- Can I Take Over XYZ? - Comprehensive subdomain takeover guide
- Subdomain Takeover Scanner - Alternative scanning tool
- Nuclei Templates - Takeover detection templates
- Bug Bounty Methodologies - Subdomain takeover hunting
Research and Learning
- OWASP Subdomain Takeover - OWASP testing guide
- Subdomain Takeover Vulnerabilities - Technical deep dive
- Cloud Security Research - Historical research
- DNS Security Best Practices - Prevention strategies