npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
Overview
Threat actor infrastructure tracking involves monitoring and mapping adversary-controlled assets including command-and-control (C2) servers, phishing domains, exploit kit hosts, bulletproof hosting, and staging servers. This skill covers using passive DNS, certificate transparency logs, Shodan/Censys scanning, WHOIS analysis, and network fingerprinting to discover, track, and pivot across threat actor infrastructure over time.
When to Use
- When managing security operations that require tracking threat actor infrastructure
- When improving security program maturity and operational processes
- When establishing standardized procedures for security team workflows
- When integrating threat intelligence or vulnerability data into operations
Prerequisites
- Python 3.9+ with
shodan,censys,requests,stix2libraries - API keys: Shodan, Censys, VirusTotal, SecurityTrails, PassiveTotal
- Understanding of DNS, TLS/SSL certificates, IP allocation, ASN structure
- Familiarity with passive DNS and certificate transparency concepts
- Access to domain registration (WHOIS) lookup services
Key Concepts
Infrastructure Pivoting
Pivoting is the technique of using one known indicator to discover related infrastructure. Starting from a known C2 IP address, analysts can pivot via: passive DNS (find domains), reverse WHOIS (find related registrations), SSL certificates (find shared certs), SSH key fingerprints, HTTP response fingerprints, JARM/JA3S hashes, and WHOIS registrant data.
Passive DNS
Passive DNS databases record DNS query/response data observed at recursive resolvers. This allows analysts to find historical domain-to-IP mappings, discover domains hosted on a known C2 IP, and identify fast-flux or domain generation algorithm (DGA) behavior.
Certificate Transparency
Certificate Transparency (CT) logs publicly record all SSL/TLS certificates issued by CAs. Monitoring CT logs reveals new certificates registered for suspicious domains, helping identify phishing sites and C2 infrastructure before they become active.
Network Fingerprinting
- JARM: Active TLS server fingerprint (hash of TLS handshake responses)
- JA3S: Passive TLS server fingerprint (hash of Server Hello)
- HTTP Headers: Server banners, custom headers, response patterns
- Favicon Hash: Hash of HTTP favicon for server identification
Workflow
Step 1: Shodan Infrastructure Discovery
import shodan
api = shodan.Shodan("YOUR_SHODAN_API_KEY")
def discover_infrastructure(ip_address):
"""Discover services and metadata for a target IP."""
try:
host = api.host(ip_address)
return {
"ip": host["ip_str"],
"org": host.get("org", ""),
"asn": host.get("asn", ""),
"isp": host.get("isp", ""),
"country": host.get("country_name", ""),
"city": host.get("city", ""),
"os": host.get("os"),
"ports": host.get("ports", []),
"vulns": host.get("vulns", []),
"hostnames": host.get("hostnames", []),
"domains": host.get("domains", []),
"tags": host.get("tags", []),
"services": [
{
"port": svc.get("port"),
"transport": svc.get("transport"),
"product": svc.get("product", ""),
"version": svc.get("version", ""),
"ssl_cert": svc.get("ssl", {}).get("cert", {}).get("subject", {}),
"jarm": svc.get("ssl", {}).get("jarm", ""),
}
for svc in host.get("data", [])
],
}
except shodan.APIError as e:
print(f"[-] Shodan error: {e}")
return None
def search_c2_framework(framework_name):
"""Search Shodan for known C2 framework signatures."""
c2_queries = {
"cobalt-strike": 'product:"Cobalt Strike Beacon"',
"metasploit": 'product:"Metasploit"',
"covenant": 'http.html:"Covenant" http.title:"Covenant"',
"sliver": 'ssl.cert.subject.cn:"multiplayer" ssl.cert.issuer.cn:"operators"',
"havoc": 'http.html_hash:-1472705893',
}
query = c2_queries.get(framework_name.lower(), framework_name)
results = api.search(query, limit=100)
hosts = []
for match in results.get("matches", []):
hosts.append({
"ip": match["ip_str"],
"port": match["port"],
"org": match.get("org", ""),
"country": match.get("location", {}).get("country_name", ""),
"asn": match.get("asn", ""),
"timestamp": match.get("timestamp", ""),
})
return hostsStep 2: Passive DNS Pivoting
import requests
def passive_dns_lookup(indicator, api_key, indicator_type="ip"):
"""Query SecurityTrails for passive DNS records."""
base_url = "https://api.securitytrails.com/v1"
headers = {"APIKEY": api_key, "Accept": "application/json"}
if indicator_type == "ip":
url = f"{base_url}/search/list"
payload = {
"filter": {"ipv4": indicator}
}
resp = requests.post(url, json=payload, headers=headers, timeout=30)
else:
url = f"{base_url}/domain/{indicator}/subdomains"
resp = requests.get(url, headers=headers, timeout=30)
if resp.status_code == 200:
return resp.json()
return None
def query_passive_total(indicator, user, api_key):
"""Query PassiveTotal for passive DNS and WHOIS data."""
base_url = "https://api.passivetotal.org/v2"
auth = (user, api_key)
# Passive DNS
pdns_resp = requests.get(
f"{base_url}/dns/passive",
params={"query": indicator},
auth=auth,
timeout=30,
)
# WHOIS
whois_resp = requests.get(
f"{base_url}/whois",
params={"query": indicator},
auth=auth,
timeout=30,
)
results = {}
if pdns_resp.status_code == 200:
results["passive_dns"] = pdns_resp.json().get("results", [])
if whois_resp.status_code == 200:
results["whois"] = whois_resp.json()
return resultsStep 3: Certificate Transparency Monitoring
import requests
def search_ct_logs(domain):
"""Search Certificate Transparency logs via crt.sh."""
resp = requests.get(
f"https://crt.sh/?q=%.{domain}&output=json",
timeout=30,
)
if resp.status_code == 200:
certs = resp.json()
unique_domains = set()
cert_info = []
for cert in certs:
name_value = cert.get("name_value", "")
for name in name_value.split("\n"):
unique_domains.add(name.strip())
cert_info.append({
"id": cert.get("id"),
"issuer": cert.get("issuer_name", ""),
"common_name": cert.get("common_name", ""),
"name_value": name_value,
"not_before": cert.get("not_before", ""),
"not_after": cert.get("not_after", ""),
"serial_number": cert.get("serial_number", ""),
})
return {
"domain": domain,
"total_certificates": len(certs),
"unique_domains": sorted(unique_domains),
"certificates": cert_info[:50],
}
return None
def monitor_new_certs(domains, interval_hours=1):
"""Monitor for newly issued certificates for a list of domains."""
from datetime import datetime, timedelta
cutoff = (datetime.utcnow() - timedelta(hours=interval_hours)).isoformat()
new_certs = []
for domain in domains:
result = search_ct_logs(domain)
if result:
for cert in result.get("certificates", []):
if cert.get("not_before", "") > cutoff:
new_certs.append({
"domain": domain,
"cert": cert,
})
return new_certsStep 4: Infrastructure Correlation and Timeline
from datetime import datetime
def build_infrastructure_timeline(indicators):
"""Build a timeline of infrastructure changes."""
timeline = []
for ind in indicators:
if "passive_dns" in ind:
for record in ind["passive_dns"]:
timeline.append({
"timestamp": record.get("firstSeen", ""),
"event": "dns_resolution",
"source": record.get("resolve", ""),
"target": record.get("value", ""),
"record_type": record.get("recordType", ""),
})
if "certificates" in ind:
for cert in ind["certificates"]:
timeline.append({
"timestamp": cert.get("not_before", ""),
"event": "certificate_issued",
"domain": cert.get("common_name", ""),
"issuer": cert.get("issuer", ""),
})
timeline.sort(key=lambda x: x.get("timestamp", ""))
return timelineValidation Criteria
- Shodan/Censys queries return infrastructure details for target IPs
- Passive DNS reveals historical domain-IP mappings
- Certificate transparency search finds associated domains
- Infrastructure pivoting discovers new related indicators
- Timeline shows infrastructure evolution over time
- Results are exportable as STIX 2.1 Infrastructure objects
References
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.8 KB
API Reference: Tracking Threat Actor Infrastructure
Pivoting Techniques
| Technique | Source | Discovers |
|---|---|---|
| Passive DNS | DNS resolvers | Domains on same IP, historical mappings |
| Reverse WHOIS | Registrar data | Domains by same registrant |
| SSL Certificate | CT logs, direct | Shared certs, SANs, issuers |
| Shodan/Censys | Internet scanning | Open ports, services, banners |
| HTTP fingerprint | Server responses | Body hash, headers, favicon |
| JARM/JA3S | TLS handshake | C2 framework identification |
API Endpoints
| Service | Endpoint | Auth |
|---|---|---|
| Shodan Host | GET /shodan/host/{ip}?key= |
API key |
| VirusTotal IP | GET /api/v3/ip-addresses/{ip} |
x-apikey header |
| VirusTotal Domain | GET /api/v3/domains/{domain} |
x-apikey header |
| SecurityTrails | GET /v1/domain/{d}/subdomains |
APIKEY header |
| RDAP WHOIS | GET https://rdap.org/domain/{d} |
None |
Network Fingerprinting
| Method | Tool | Description |
|---|---|---|
| JARM | jarm.py | Active TLS server fingerprint |
| JA3S | Zeek/Wireshark | Passive TLS Server Hello hash |
| Favicon hash | Shodan http.favicon.hash |
mmh3 hash of favicon.ico |
| HTTP body hash | SHA-256 | Response body fingerprint |
| Server banner | HTTP Server header | Software identification |
Python Libraries
| Library | Version | Purpose |
|---|---|---|
requests |
>=2.28 | API queries to Shodan/VT |
ssl |
stdlib | TLS certificate retrieval |
socket |
stdlib | DNS resolution, connections |
hashlib |
stdlib | Certificate/content fingerprinting |
References
- Shodan API: https://developer.shodan.io/api
- VirusTotal API v3: https://docs.virustotal.com/reference/overview
- Certificate Transparency: https://certificate.transparency.dev/
- JARM: https://github.com/salesforce/jarm
standards.md1.6 KB
Standards and Frameworks Reference
STIX 2.1 Infrastructure Object
{
"type": "infrastructure",
"name": "C2 Server",
"infrastructure_types": ["command-and-control"],
"description": "Cobalt Strike TeamServer at 198.51.100.1",
"first_seen": "2025-01-01T00:00:00Z",
"last_seen": "2025-06-01T00:00:00Z"
}Diamond Model of Intrusion Analysis
- Adversary: Threat actor or group
- Capability: Tools, techniques, and malware
- Infrastructure: C2 servers, domains, hosting
- Victim: Targeted organization or individual
Infrastructure Types (STIX vocabulary)
- command-and-control, botnet, exfiltration, hosting-malware
- hosting-target-lists, phishing, staging, undefined
Network Fingerprinting Methods
| Method | Type | Description |
|---|---|---|
| JARM | Active | TLS server fingerprint from 10 TLS handshakes |
| JA3S | Passive | Server Hello hash from TLS negotiation |
| JA3 | Passive | Client Hello hash for client fingerprinting |
| Favicon Hash | Active | HTTP favicon file hash |
| HTTP Headers | Active/Passive | Server banner and header fingerprinting |
| SSH Key | Active | SSH host key fingerprint |
Passive DNS Record Types
- A/AAAA: Domain to IP mapping
- CNAME: Domain alias
- MX: Mail server records
- NS: Nameserver records
- TXT: Text records (SPF, DKIM, verification)
References
workflows.md2.4 KB
Infrastructure Tracking Workflows
Workflow 1: IP-Centric Pivoting
[Known C2 IP] --> [Shodan/Censys] --> [Service Fingerprints]
| |
v v
[Passive DNS] --> [Associated Domains] --> [WHOIS Analysis] --> [Registrant Pivot]
| |
v v
[SSL Certs] --> [Subject Alt Names] --> [New Domains] --> [Additional IPs]Workflow 2: Domain-Centric Pivoting
[Known C2 Domain] --> [DNS History] --> [Historical IPs] --> [Co-hosted Domains]
| |
v v
[CT Logs] --> [Subdomains] --> [Infrastructure Map] --> [Shared Hosting Analysis]
|
v
[WHOIS] --> [Registrant/Email] --> [Other Registered Domains]Workflow 3: C2 Framework Hunting
[C2 Signature] --> [Shodan Search] --> [Candidate Servers] --> [Validation]
|
v
[JARM Fingerprint]
|
v
[Confirm C2 Type]
|
v
[Track Over Time]Workflow 4: Continuous Monitoring
[Watchlist IPs/Domains] --> [Scheduled Scans] --> [Change Detection] --> [Alerts]
|
+--------+--------+
| | |
v v v
[New Port] [DNS Change] [New Cert]
| | |
v v v
[Investigate] [Update TI] [Share]Scripts 2
agent.py7.6 KB
#!/usr/bin/env python3
"""Agent for tracking threat actor infrastructure.
Uses passive DNS, certificate transparency, Shodan, WHOIS, and
network fingerprinting to discover, pivot across, and map
adversary-controlled infrastructure.
"""
import json
import sys
import socket
import ssl
import hashlib
from pathlib import Path
from datetime import datetime
try:
import requests
except ImportError:
requests = None
class ThreatInfraTracker:
"""Tracks and pivots across threat actor infrastructure."""
def __init__(self, shodan_key=None, vt_key=None, output_dir="./threat_infra"):
self.shodan_key = shodan_key
self.vt_key = vt_key
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.findings = []
self.infrastructure = {}
def _get(self, url, params=None, headers=None, timeout=10):
if not requests:
return None
try:
return requests.get(url, params=params, headers=headers, timeout=timeout)
except requests.RequestException:
return None
def query_shodan(self, ip):
"""Query Shodan for host information and services."""
if not self.shodan_key:
return {"error": "No Shodan API key"}
resp = self._get(f"https://api.shodan.io/shodan/host/{ip}",
params={"key": self.shodan_key})
if resp and resp.status_code == 200:
data = resp.json()
result = {
"ip": ip, "org": data.get("org"), "asn": data.get("asn"),
"os": data.get("os"), "ports": data.get("ports", []),
"hostnames": data.get("hostnames", []),
"vulns": data.get("vulns", []),
"country": data.get("country_code"),
}
self.infrastructure[ip] = result
return result
return None
def query_virustotal(self, indicator, indicator_type="ip"):
"""Query VirusTotal for IP/domain reputation."""
if not self.vt_key:
return {"error": "No VT API key"}
type_map = {"ip": "ip-addresses", "domain": "domains", "hash": "files"}
endpoint = type_map.get(indicator_type, "ip-addresses")
resp = self._get(f"https://www.virustotal.com/api/v3/{endpoint}/{indicator}",
headers={"x-apikey": self.vt_key})
if resp and resp.status_code == 200:
data = resp.json().get("data", {}).get("attributes", {})
stats = data.get("last_analysis_stats", {})
result = {
"indicator": indicator, "type": indicator_type,
"malicious": stats.get("malicious", 0),
"suspicious": stats.get("suspicious", 0),
"reputation": data.get("reputation", 0),
}
if stats.get("malicious", 0) > 3:
self.findings.append({"severity": "high", "type": "Malicious Infrastructure",
"detail": f"{indicator} flagged by {stats['malicious']} engines"})
return result
return None
def passive_dns_lookup(self, indicator):
"""Query passive DNS via SecurityTrails-style API."""
resp = self._get(f"https://api.securitytrails.com/v1/domain/{indicator}/subdomains",
headers={"APIKEY": "demo"})
if resp and resp.status_code == 200:
return resp.json().get("subdomains", [])
try:
ips = socket.getaddrinfo(indicator, None)
return list({addr[4][0] for addr in ips})
except socket.gaierror:
return []
def get_ssl_certificate(self, host, port=443):
"""Retrieve SSL certificate details for fingerprinting."""
try:
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
with ctx.wrap_socket(socket.socket(), server_hostname=host) as s:
s.settimeout(5)
s.connect((host, port))
cert = s.getpeercert(binary_form=True)
cert_hash = hashlib.sha256(cert).hexdigest()
der_info = s.getpeercert()
return {
"host": host, "sha256": cert_hash,
"subject": dict(x[0] for x in der_info.get("subject", [])) if der_info else {},
"issuer": dict(x[0] for x in der_info.get("issuer", [])) if der_info else {},
"serial": der_info.get("serialNumber") if der_info else None,
"not_after": der_info.get("notAfter") if der_info else None,
}
except Exception:
return None
def check_whois(self, domain):
"""Retrieve WHOIS data via RDAP for pivoting."""
resp = self._get(f"https://rdap.org/domain/{domain}")
if resp and resp.status_code == 200:
data = resp.json()
registrar = None
for entity in data.get("entities", []):
if "registrar" in entity.get("roles", []):
registrar = entity.get("handle")
return {
"domain": domain, "status": data.get("status", []),
"registrar": registrar,
"nameservers": [ns.get("ldhName") for ns in data.get("nameservers", [])],
}
return None
def fingerprint_http(self, ip, port=80):
"""Fingerprint HTTP server for infrastructure correlation."""
resp = self._get(f"http://{ip}:{port}/", timeout=5)
if not resp:
return None
headers = dict(resp.headers)
body_hash = hashlib.sha256(resp.content).hexdigest()
return {
"ip": ip, "port": port, "status": resp.status_code,
"server": headers.get("Server"), "content_type": headers.get("Content-Type"),
"body_hash": body_hash, "body_length": len(resp.content),
"headers_of_interest": {k: v for k, v in headers.items()
if k.lower() not in ("date", "content-length", "connection")},
}
def pivot_from_ip(self, ip):
"""Perform infrastructure pivoting from a known IP."""
result = {"ip": ip, "shodan": None, "vt": None, "ssl": None, "http": None}
result["shodan"] = self.query_shodan(ip)
result["vt"] = self.query_virustotal(ip, "ip")
result["ssl"] = self.get_ssl_certificate(ip)
result["http"] = self.fingerprint_http(ip)
return result
def generate_report(self, indicators=None):
results = {}
if indicators:
for ind in indicators:
results[ind] = self.pivot_from_ip(ind)
report = {
"report_date": datetime.utcnow().isoformat(),
"indicators_analyzed": len(indicators or []),
"pivot_results": results,
"infrastructure_map": self.infrastructure,
"findings": self.findings,
"total_findings": len(self.findings),
}
out = self.output_dir / "threat_infra_report.json"
with open(out, "w") as f:
json.dump(report, f, indent=2)
print(json.dumps(report, indent=2))
return report
def main():
if len(sys.argv) < 2:
print("Usage: agent.py <ip_or_domain> [--shodan-key KEY] [--vt-key KEY]")
sys.exit(1)
indicators = [sys.argv[1]]
shodan_key = vt_key = None
if "--shodan-key" in sys.argv:
shodan_key = sys.argv[sys.argv.index("--shodan-key") + 1]
if "--vt-key" in sys.argv:
vt_key = sys.argv[sys.argv.index("--vt-key") + 1]
agent = ThreatInfraTracker(shodan_key, vt_key)
agent.generate_report(indicators)
if __name__ == "__main__":
main()
process.py10.8 KB
#!/usr/bin/env python3
"""
Threat Actor Infrastructure Tracking Script
Tracks and maps adversary infrastructure using:
- Shodan/Censys for service discovery
- Passive DNS for domain-IP relationships
- Certificate Transparency for certificate monitoring
- WHOIS for registration data pivoting
Requirements:
pip install shodan requests stix2
Usage:
python process.py --ip 198.51.100.1 --shodan-key KEY
python process.py --domain evil.com --ct-search
python process.py --c2-hunt cobalt-strike --shodan-key KEY
"""
import argparse
import json
import sys
from datetime import datetime
from collections import defaultdict
from typing import Optional
import requests
try:
import shodan
except ImportError:
shodan = None
class InfrastructureTracker:
"""Track threat actor infrastructure across multiple data sources."""
def __init__(self, shodan_key: str = "", securitytrails_key: str = ""):
self.shodan_api = shodan.Shodan(shodan_key) if shodan and shodan_key else None
self.st_key = securitytrails_key
self.findings = {"ips": {}, "domains": {}, "certificates": [], "pivots": []}
def shodan_host_lookup(self, ip: str) -> Optional[dict]:
"""Look up IP on Shodan."""
if not self.shodan_api:
print("[-] Shodan API not configured")
return None
try:
host = self.shodan_api.host(ip)
result = {
"ip": ip,
"org": host.get("org", ""),
"asn": host.get("asn", ""),
"isp": host.get("isp", ""),
"country": host.get("country_name", ""),
"city": host.get("city", ""),
"os": host.get("os"),
"ports": host.get("ports", []),
"vulns": host.get("vulns", []),
"hostnames": host.get("hostnames", []),
"services": [],
}
for svc in host.get("data", []):
service = {
"port": svc.get("port"),
"transport": svc.get("transport"),
"product": svc.get("product", ""),
"version": svc.get("version", ""),
"banner": svc.get("data", "")[:200],
}
ssl = svc.get("ssl", {})
if ssl:
service["jarm"] = ssl.get("jarm", "")
service["ja3s"] = ssl.get("ja3s", "")
cert = ssl.get("cert", {})
if cert:
service["cert_subject"] = cert.get("subject", {})
service["cert_issuer"] = cert.get("issuer", {})
service["cert_expires"] = cert.get("expires", "")
result["services"].append(service)
self.findings["ips"][ip] = result
print(f"[+] Shodan: {ip} - {result['org']} - Ports: {result['ports']}")
return result
except Exception as e:
print(f"[-] Shodan error for {ip}: {e}")
return None
def search_c2_servers(self, framework: str, limit: int = 50) -> list:
"""Search for C2 framework servers on Shodan."""
if not self.shodan_api:
return []
queries = {
"cobalt-strike": 'product:"Cobalt Strike Beacon"',
"metasploit": 'product:"Metasploit"',
"sliver": 'ssl:"multiplayer" ssl:"operators"',
"havoc": 'http.html_hash:-1472705893',
"brute-ratel": 'http.html_hash:"-1957161625"',
}
query = queries.get(framework.lower(), framework)
try:
results = self.shodan_api.search(query, limit=limit)
servers = []
for match in results.get("matches", []):
servers.append({
"ip": match["ip_str"],
"port": match["port"],
"org": match.get("org", ""),
"asn": match.get("asn", ""),
"country": match.get("location", {}).get("country_name", ""),
"timestamp": match.get("timestamp", ""),
})
print(f"[+] Found {len(servers)} {framework} servers")
return servers
except Exception as e:
print(f"[-] C2 search error: {e}")
return []
def ct_log_search(self, domain: str) -> Optional[dict]:
"""Search Certificate Transparency logs via crt.sh."""
try:
resp = requests.get(
f"https://crt.sh/?q=%.{domain}&output=json", timeout=30
)
if resp.status_code == 200:
certs = resp.json()
unique_domains = set()
for cert in certs:
for name in cert.get("name_value", "").split("\n"):
name = name.strip()
if name:
unique_domains.add(name)
result = {
"domain": domain,
"total_certs": len(certs),
"unique_domains": sorted(unique_domains),
"recent_certs": [
{
"common_name": c.get("common_name", ""),
"issuer": c.get("issuer_name", ""),
"not_before": c.get("not_before", ""),
"not_after": c.get("not_after", ""),
}
for c in certs[:20]
],
}
self.findings["certificates"].append(result)
print(f"[+] CT: {domain} - {len(certs)} certs, {len(unique_domains)} domains")
return result
except Exception as e:
print(f"[-] CT search error: {e}")
return None
def passive_dns_securitytrails(self, domain: str) -> Optional[dict]:
"""Query SecurityTrails passive DNS."""
if not self.st_key:
print("[-] SecurityTrails API key not configured")
return None
try:
resp = requests.get(
f"https://api.securitytrails.com/v1/domain/{domain}",
headers={"APIKEY": self.st_key},
timeout=30,
)
if resp.status_code == 200:
data = resp.json()
dns = data.get("current_dns", {})
result = {
"domain": domain,
"a_records": [
r.get("ip") for r in dns.get("a", {}).get("values", [])
],
"mx_records": [
r.get("host") for r in dns.get("mx", {}).get("values", [])
],
"ns_records": [
r.get("nameserver") for r in dns.get("ns", {}).get("values", [])
],
"alexa_rank": data.get("alexa_rank"),
}
self.findings["domains"][domain] = result
print(f"[+] pDNS: {domain} -> {result['a_records']}")
return result
except Exception as e:
print(f"[-] SecurityTrails error: {e}")
return None
def pivot_from_ip(self, ip: str) -> dict:
"""Perform full infrastructure pivot from an IP address."""
pivot_results = {"origin_ip": ip, "discovered": []}
# Shodan lookup
shodan_data = self.shodan_host_lookup(ip)
if shodan_data:
for hostname in shodan_data.get("hostnames", []):
pivot_results["discovered"].append({
"type": "domain",
"value": hostname,
"source": "shodan_hostname",
})
for svc in shodan_data.get("services", []):
cert_cn = svc.get("cert_subject", {}).get("CN", "")
if cert_cn and cert_cn != ip:
pivot_results["discovered"].append({
"type": "domain",
"value": cert_cn,
"source": "ssl_certificate",
})
# CT search for discovered domains
seen_domains = set()
for item in pivot_results["discovered"]:
if item["type"] == "domain":
domain = item["value"]
if domain not in seen_domains:
seen_domains.add(domain)
ct = self.ct_log_search(domain)
if ct:
for d in ct.get("unique_domains", []):
if d not in seen_domains:
pivot_results["discovered"].append({
"type": "domain",
"value": d,
"source": "ct_log",
})
self.findings["pivots"].append(pivot_results)
return pivot_results
def generate_report(self) -> dict:
"""Generate infrastructure tracking report."""
return {
"timestamp": datetime.utcnow().isoformat(),
"summary": {
"ips_tracked": len(self.findings["ips"]),
"domains_tracked": len(self.findings["domains"]),
"certificates_found": sum(
c.get("total_certs", 0) for c in self.findings["certificates"]
),
"pivots_performed": len(self.findings["pivots"]),
},
"findings": self.findings,
}
def main():
parser = argparse.ArgumentParser(description="Infrastructure Tracking Tool")
parser.add_argument("--ip", help="IP address to investigate")
parser.add_argument("--domain", help="Domain to investigate")
parser.add_argument("--c2-hunt", help="C2 framework to hunt")
parser.add_argument("--ct-search", action="store_true", help="Search CT logs")
parser.add_argument("--pivot", action="store_true", help="Full pivot from IP")
parser.add_argument("--shodan-key", default="", help="Shodan API key")
parser.add_argument("--st-key", default="", help="SecurityTrails API key")
parser.add_argument("--output", default="infra_report.json", help="Output file")
args = parser.parse_args()
tracker = InfrastructureTracker(args.shodan_key, args.st_key)
if args.ip and args.pivot:
results = tracker.pivot_from_ip(args.ip)
print(json.dumps(results, indent=2))
elif args.ip:
tracker.shodan_host_lookup(args.ip)
elif args.domain and args.ct_search:
tracker.ct_log_search(args.domain)
elif args.domain:
tracker.passive_dns_securitytrails(args.domain)
elif args.c2_hunt:
servers = tracker.search_c2_servers(args.c2_hunt)
print(json.dumps(servers, indent=2))
report = tracker.generate_report()
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"[+] Report saved to {args.output}")
if __name__ == "__main__":
main()