Implements external attack surface management (EASM) using Shodan, Censys, and ProjectDiscovery tools (subfinder, httpx, nuclei) for asset discovery, subdomain enumeration, service fingerprinting, and exposure scoring. Includes a weighted risk scoring algorithm based on OWASP attack surface analysis methodology and the Relative Attack Surface Quotient (RSQ). Use when building continuous ASM programs or performing external reconnaissance for security assessments.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Implements external attack surface management (EASM) using Shodan, Censys, and ProjectDiscovery tools (subfinder, httpx, nuclei) for asset discovery, subdomain enumeration, service fingerprinting, and exposure scoring. Includes a weighted risk scoring algorithm based on OWASP attack surface analysis methodology and the Relative Attack Surface Quotient (RSQ). Use when building continuous ASM programs or performing external reconnaissance for security assessments.
When building an external attack surface management (EASM) program from scratch
When performing authorized external reconnaissance for penetration testing engagements
When continuously monitoring organizational exposure across internet-facing assets
When scoring and prioritizing external attack surface risks for remediation
When integrating multiple discovery tools into an automated ASM pipeline
Most Often Missed & How to Confirm
Acquisition/sibling assets: teams scan the apex domain but miss subsidiaries, fresh ASNs, and recently registered domains. Confirm ownership via amass intel -asn/-org, WHOIS registrant correlation, and matching TLS cert organization fields before adding to scope.
Cert-transparency-only hosts with no DNS: a name in a CT log isn't live. Confirm with dnsx resolution then httpx -sc -title — only record assets that actually answer.
Shodan/Censys staleness: banners can be days/weeks old. Confirm an exposure is current by directly re-probing the ip:port (httpx, nmap -sV) before scoring it; flag closed/changed services as historical.
Non-HTTP exposure: databases (3306/5432/27017/6379), RDP/SMB, Kubernetes API (6443/10250), and message brokers get skipped by HTTP-only pipelines. Confirm with a targeted nmap -sV and an unauthenticated connection test.
Nuclei false positives: a single template hit is a lead, not a finding. Confirm by manually reproducing the request/response, checking the matched string is genuinely the vuln (not an error page), and re-running to rule out flapping.
CVE inference from version banners: a version string suggests, it doesn't prove. Confirm exploitability against the live service or note it explicitly as "version-implied, unverified."
Scope drift: shared CDN/SaaS IPs (Cloudflare, AWS, GitHub Pages) frequently fall outside authorization even when the hostname matches — confirm the underlying IP/org sits in the signed scope before any active scan.
Prerequisites
Python 3.8+ with requests, shodan, censys libraries installed
Shodan API key (free tier provides 100 queries/month)
Go 1.21+ for building ProjectDiscovery tools from source
Appropriate authorization for all external scanning activities
Target domains and IP ranges with written scope documentation
Instructions
Phase 1: Subdomain Enumeration with Multiple Sources
Use subfinder for passive subdomain discovery leveraging dozens of data sources
including certificate transparency logs, DNS datasets, and search engines.
# Install ProjectDiscovery tools
go install -v github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest
go install -v github.com/projectdiscovery/httpx/cmd/httpx@latest
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
# Basic subdomain enumeration
subfinder -d example.com -o subdomains.txt
# Verbose with all sources and recursive enumeration
subfinder -d example.com -all -recursive -o subdomains_full.txt
# Multi-domain enumeration from file
subfinder -dL domains.txt -o all_subdomains.txt
# Using OWASP Amass for deeper enumeration
amass enum -d example.com -passive -o amass_subdomains.txt
# Merge and deduplicate resultscat subdomains.txt amass_subdomains.txt | sort -u > combined_subdomains.txt
Phase 2: Live Host Discovery and Service Fingerprinting
Probe discovered subdomains to identify live hosts, technologies, and services.
Query Shodan for exposed services, open ports, and known vulnerabilities
associated with discovered assets.
import shodan
api = shodan.Shodan("YOUR_SHODAN_API_KEY")
# Search by organization
results = api.search("org:\"Example Corp\"")
for service in results["matches"]:
print(f"{service['ip_str']}:{service['port']} - {service.get('product', 'unknown')}")
if service.get("vulns"):
for cve in service["vulns"]:
print(f" CVE: {cve}")
# Search by hostname
results = api.search("hostname:example.com")
# Search by SSL certificate
results = api.search("ssl.cert.subject.cn:example.com")
# Get host details with all services
host = api.host("93.184.216.34")
print(f"IP: {host['ip_str']}")
print(f"Ports: {host['ports']}")
print(f"Vulns: {host.get('vulns', [])}")
Phase 4: Censys Asset Discovery
Use Censys to discover internet-facing assets through certificate and host search.
from censys.search import CensysHosts, CensysCerts
# Host search
hosts = CensysHosts()
query = hosts.search("services.tls.certificates.leaf.subject.common_name: example.com")
for page in query:
for host in page:
print(f"IP: {host['ip']}")
for service in host.get("services", []):
print(f" Port: {service['port']} Protocol: {service['transport_protocol']}")
print(f" Service: {service.get('service_name', 'unknown')}")
# Certificate transparency search
certs = CensysCerts()
query = certs.search("parsed.names: example.com")
for page in query:
for cert in page:
print(f"Fingerprint: {cert['fingerprint_sha256']}")
print(f"Names: {cert.get('parsed', {}).get('names', [])}")
Phase 5: Vulnerability Scanning with Nuclei
Run targeted vulnerability scans against discovered assets using Nuclei templates.
# Update nuclei templates
nuclei -ut
# Scan with all templatescat combined_subdomains.txt | httpx -silent | nuclei -o nuclei_results.txt
# Scan with specific severitycat combined_subdomains.txt | httpx -silent | \
nuclei -severity critical,high -o critical_findings.txt
# Scan with specific template categoriescat combined_subdomains.txt | httpx -silent | \
nuclei -tags cve,misconfig,exposure -o categorized_findings.txt
# Scan for exposed panels and sensitive filescat combined_subdomains.txt | httpx -silent | \
nuclei -tags panel,exposure,config -o exposed_panels.txt
Phase 6: Exposure Scoring Algorithm
Score each asset based on OWASP attack surface analysis principles, using
a weighted formula derived from the Relative Attack Surface Quotient (RSQ)
and damage-potential-to-effort ratio.
The scoring algorithm considers:
Open ports and services - weighted by service risk (management ports score higher)
Known vulnerabilities - weighted by CVSS score
Technology age - outdated software increases score
Exposure level - internet-facing vs. authenticated access
Data sensitivity - based on service type and content indicators
# Exposure Score = sum of weighted factors, normalized to 0-100# See agent.py for the full implementation