Claude
Skills
Sign in
Back

passive-recon

Included with Lifetime
$97 forever

Comprehensive passive reconnaissance covering WHOIS, DNS history, reverse IP, SSL certificates, Google dorks, and Wayback Machine enumeration. Use when: initial target profiling without active scanning, stealth recon where no direct target contact is desired, or building a complete picture of a target's infrastructure from public data alone.

General

What this skill does


# Passive Reconnaissance

## Overview

Passive reconnaissance gathers intelligence about a target using only publicly available information — no packets are sent to the target's infrastructure. This makes it completely undetectable and legally safe when used against publicly accessible data. The goal is to build a comprehensive intelligence picture from WHOIS records, DNS history, SSL certificate logs, search engine caches, and web archives before making any active contact with the target.

## Instructions

### Step 1: WHOIS lookups

```python
import whois
import subprocess
import json
import re

def whois_lookup(domain):
    """Perform WHOIS lookup using python-whois library."""
    # pip install python-whois
    try:
        w = whois.whois(domain)
        print(f"\n=== WHOIS: {domain} ===")
        print(f"Registrar:       {w.registrar}")
        print(f"Created:         {w.creation_date}")
        print(f"Updated:         {w.updated_date}")
        print(f"Expires:         {w.expiration_date}")
        print(f"Name servers:    {w.name_servers}")
        print(f"Registrant org:  {getattr(w, 'org', 'N/A')}")
        print(f"Registrant email:{getattr(w, 'emails', 'N/A')}")
        print(f"Registrant country: {getattr(w, 'country', 'N/A')}")

        # Check for privacy protection
        raw = str(w.text)
        if any(word in raw.lower() for word in ["privacy", "redacted", "withheld", "domains by proxy"]):
            print("\n  ⚠ WHOIS privacy protection is enabled")

        return dict(w)
    except Exception as e:
        print(f"WHOIS error for {domain}: {e}")
        return None

def reverse_whois_email(email):
    """Find domains registered with the same email (via ViewDNS or WHOIS API)."""
    url = f"https://viewdns.info/reversewhois/?q={email}"
    print(f"Reverse WHOIS for {email}: {url}")
    # Note: ViewDNS.info has a web UI — use their API for automation
    return url

whois_lookup("example.com")
```

### Step 2: DNS enumeration and history

```python
import dns.resolver
import requests

def dns_lookup(domain):
    """Enumerate DNS records for a domain."""
    record_types = ["A", "AAAA", "MX", "NS", "TXT", "SOA", "CNAME"]
    results = {}

    print(f"\n=== DNS Records: {domain} ===")
    for rtype in record_types:
        try:
            answers = dns.resolver.resolve(domain, rtype, lifetime=5)
            records = [str(r) for r in answers]
            results[rtype] = records
            print(f"  {rtype:<8}: {', '.join(records)}")
        except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN, dns.exception.Timeout):
            pass

    return results

def dns_history_securitytrails(domain, api_key):
    """Get historical DNS records from SecurityTrails API."""
    headers = {"apikey": api_key, "Accept": "application/json"}
    base_url = "https://api.securitytrails.com/v1"

    # Historical A records
    resp = requests.get(f"{base_url}/history/{domain}/dns/a", headers=headers, timeout=15)
    data = resp.json()

    print(f"\n=== DNS History (SecurityTrails): {domain} ===")
    for record in data.get("records", []):
        first_seen = record.get("first_seen")
        last_seen = record.get("last_seen")
        ips = [v.get("ip") for v in record.get("values", [])]
        print(f"  [{first_seen} → {last_seen}] {', '.join(ips)}")

    return data

def passive_dns_free(domain):
    """Query free passive DNS sources."""
    results = {}

    # HackerTarget Passive DNS (free)
    try:
        resp = requests.get(
            f"https://api.hackertarget.com/hostsearch/?q={domain}",
            timeout=15
        )
        if "error" not in resp.text.lower():
            hosts = [line.split(",")[0] for line in resp.text.strip().split("\n") if "," in line]
            results["hackertarget_hosts"] = hosts
            print(f"HackerTarget found {len(hosts)} subdomains for {domain}")
    except Exception as e:
        print(f"HackerTarget error: {e}")

    # CIRCL Passive DNS (free, no auth)
    try:
        resp = requests.get(
            f"https://www.circl.lu/pdns/query/{domain}",
            headers={"Accept": "application/json"},
            timeout=15
        )
        records = [json.loads(line) for line in resp.text.strip().split("\n") if line]
        ips = list({r.get("rdata") for r in records if r.get("rrtype") == "A"})
        results["circl_ips"] = ips
        print(f"CIRCL passive DNS: {len(records)} records, {len(ips)} unique IPs")
    except Exception as e:
        print(f"CIRCL error: {e}")

    return results

dns_lookup("example.com")
```

### Step 3: Reverse IP lookup

```python
def reverse_ip_lookup(ip_address):
    """Find all domains hosted on the same IP address."""
    # HackerTarget API (free, up to 500 results)
    try:
        resp = requests.get(
            f"https://api.hackertarget.com/reverseiplookup/?q={ip_address}",
            timeout=15
        )
        if "error" not in resp.text.lower() and resp.text.strip():
            domains = [d.strip() for d in resp.text.strip().split("\n")]
            print(f"\n=== Reverse IP: {ip_address} ===")
            print(f"Hosted domains: {len(domains)}")
            for d in domains[:20]:
                print(f"  {d}")
            if len(domains) > 20:
                print(f"  ... and {len(domains) - 20} more")
            return domains
    except Exception as e:
        print(f"Reverse IP error: {e}")
    return []

def ip_info(ip_address):
    """Get IP geolocation, ASN, and organization info."""
    resp = requests.get(f"https://ipapi.co/{ip_address}/json/", timeout=10)
    data = resp.json()
    print(f"\n=== IP Info: {ip_address} ===")
    print(f"  Organization: {data.get('org', 'N/A')}")
    print(f"  ASN:          {data.get('asn', 'N/A')}")
    print(f"  Country:      {data.get('country_name', 'N/A')}")
    print(f"  City:         {data.get('city', 'N/A')}")
    print(f"  Hostname:     {data.get('hostname', 'N/A')}")
    return data

reverse_ip_lookup("93.184.216.34")
ip_info("93.184.216.34")
```

### Step 4: SSL certificate transparency (crt.sh)

```python
def crt_sh_lookup(domain, include_expired=True, deduplicate=True):
    """
    Query crt.sh for all SSL certificates issued for a domain.
    Certificate transparency logs are public and reveal all subdomains
    that have ever had a certificate issued.
    """
    # Use wildcard to find all subdomains
    query = f"%.{domain}"
    url = f"https://crt.sh/?q={query}&output=json"

    try:
        resp = requests.get(url, timeout=30)
        certs = resp.json()
    except (requests.RequestException, json.JSONDecodeError) as e:
        print(f"crt.sh error: {e}")
        return []

    # Extract all names from certificates
    all_names = set()
    for cert in certs:
        name_value = cert.get("name_value", "")
        # name_value can contain multiple names separated by newlines
        for name in name_value.split("\n"):
            name = name.strip().lstrip("*.")
            if name and domain in name:
                all_names.add(name.lower())

    subdomains = sorted(all_names)
    print(f"\n=== Certificate Transparency: {domain} ===")
    print(f"Total certificates: {len(certs)}")
    print(f"Unique subdomains: {len(subdomains)}")
    for sub in subdomains[:30]:
        print(f"  {sub}")
    if len(subdomains) > 30:
        print(f"  ... and {len(subdomains) - 30} more")

    return subdomains

# Also retrieve detailed cert info
def crt_sh_certs_detail(domain):
    """Get certificate details including issuer, validity, and fingerprint."""
    resp = requests.get(f"https://crt.sh/?q={domain}&output=json", timeout=30)
    certs = resp.json()

    print(f"\nRecent certificates for {domain}:")
    for cert in sorted(certs, key=lambda x: x.get("entry_timestamp", ""), reverse=True)[:10]:
        print(f"  [{cert.get('entry_timestamp', '')[:10]}] "
              f"CN={cert.get('common_name', '?'):<40} "
              f"Issuer: {cert.get('issuer_name', '?')[:50]}")

crt_sh_lookup("example.com
Files: 2
Size: 17.3 KB
Complexity: 29/100
Category: General

Related in General