| name | building-adversary-infrastructure-tracking-system |
| description | Build an automated adversary infrastructure tracking system in Python (dnspython, python-whois, shodan, networkx) that pivots across passive DNS, certificate transparency logs, WHOIS records, and IP enrichment to map threat-actor C2 networks and flag newly registered domains matching known patterns. Use when pivoting from known indicators to discover related C2 infrastructure or maintaining a continuously updated map of a threat actor's network. |
| domain | cybersecurity |
| subdomain | threat-intelligence |
| tags | ["infrastructure-tracking","passive-dns","c2","whois","threat-actor","pivoting","threat-intelligence","domain-analysis"] |
| version | 1.0 |
| author | mahipal |
| license | Apache-2.0 |
| nist_csf | ["ID.RA-01","ID.RA-05","DE.CM-01","DE.AE-02"] |
| mitre_attack | ["T1583.001","T1583.004","T1596.001","T1590.002","T1071.001"] |
Building Adversary Infrastructure Tracking System
Overview
Adversary infrastructure tracking uses passive DNS records, certificate transparency logs, WHOIS registration data, and IP enrichment to discover, map, and monitor threat actor command-and-control (C2) networks. Attackers frequently reuse hosting providers, registrars, SSL certificates, and naming patterns across campaigns, enabling analysts to pivot from known indicators to discover new infrastructure. This skill covers building an automated tracking system that identifies infrastructure relationships, detects newly registered domains matching adversary patterns, and maintains a continuously updated map of threat actor networks.
When to Use
- When deploying or configuring building adversary infrastructure tracking system capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- Python 3.9+ with
requests, dnspython, python-whois, shodan, networkx libraries
- API keys: SecurityTrails, PassiveTotal/RiskIQ, Shodan, VirusTotal
- Access to passive DNS data sources
- Understanding of DNS infrastructure, hosting, and domain registration
- Graph database (Neo4j) or NetworkX for relationship visualization
Key Concepts
Passive DNS
Passive DNS captures historical DNS resolution data, recording which domains resolved to which IPs and when. Unlike active DNS queries, passive DNS preserves historical relationships even after records change, enabling analysts to track infrastructure changes, identify shared hosting patterns, and discover related domains that resolved to the same IP addresses over time.
Infrastructure Pivoting
Pivoting identifies related infrastructure by following connections: IP pivot (find all domains on an IP), domain pivot (find all IPs a domain resolved to), WHOIS pivot (find domains with same registrant), certificate pivot (find hosts sharing SSL certificates), and NS/MX pivot (find domains using same name servers or mail servers).
Adversary Infrastructure Patterns
Threat actors exhibit patterns: preferred registrars (Namecheap, REG.RU, Tucows), preferred hosting (bulletproof hosting providers, cloud services), domain generation algorithms (DGA), consistent naming patterns, and certificate reuse across campaigns.
Workflow
Step 1: Passive DNS Infrastructure Discovery
requests
json
collections defaultdict
datetime datetime
:
():
.st_key = securitytrails_key
.vt_key = vt_key
.shodan_key = shodan_key
.infrastructure_graph = defaultdict(: {: (), : []})
():
headers = {: .st_key}
url =
resp = requests.get(url, headers=headers, timeout=)
resp.status_code == :
records = resp.json().get(, [])
history = []
record records:
value record.get(, []):
history.append({
: domain,
: value.get(, ),
: record.get(, ),
: record.get(, ),
: record.get(, ),
})
()
history
[]
():
headers = {: .st_key}
url =
resp = requests.get(url, headers=headers, timeout=)
resp.status_code == :
blocks = resp.json().get(, [])
domains = []
block blocks:
site block.get(, []):
domains.append(site)
()
domains
[]
():
headers = {: .st_key}
url =
resp = requests.get(url, headers=headers, timeout=)
resp.status_code == :
data = resp.json()
whois_data = {
: domain,
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(, []),
: data.get(, ),
: data.get(, ),
: data.get(, ),
}
whois_data
{}
():
discovered = {: (), : (), : []}
indicator_type == :
discovered[].add(seed_indicator)
pdns = .passive_dns_lookup(seed_indicator)
record pdns:
ip = record[]
discovered[].add(ip)
discovered[].append({
: seed_indicator, : ip,
: ,
: record[],
: record[],
})
depth > :
reverse_domains = .reverse_ip_lookup(ip)
rd reverse_domains[:]:
discovered[].add(rd)
discovered[].append({
: rd, : ip,
: ,
})
indicator_type == :
discovered[].add(seed_indicator)
domains = .reverse_ip_lookup(seed_indicator)
domain domains[:]:
discovered[].add(domain)
discovered[].append({
: domain, : seed_indicator,
: ,
})
(
)
discovered
tracker = InfrastructureTracker(
securitytrails_key=,
vt_key=,
)