jpskill.com
📦 その他 コミュニティ

passive-recon

WHOIS情報やDNS履歴、SSL証明書などを幅広く調査し、相手に気づかれずに公開情報から企業や組織のインフラ全体像を把握したり、初期段階のプロファイリングに役立てたりするSkill。

📜 元の英語説明(参考)

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.

🇯🇵 日本人クリエイター向け解説

一言でいうと

WHOIS情報やDNS履歴、SSL証明書などを幅広く調査し、相手に気づかれずに公開情報から企業や組織のインフラ全体像を把握したり、初期段階のプロファイリングに役立てたりするSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o passive-recon.zip https://jpskill.com/download/15236.zip && unzip -o passive-recon.zip && rm passive-recon.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/15236.zip -OutFile "$d\passive-recon.zip"; Expand-Archive "$d\passive-recon.zip" -DestinationPath $d -Force; ri "$d\passive-recon.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して passive-recon.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → passive-recon フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

[Skill 名] passive-recon

パッシブ偵察

概要

パッシブ偵察は、公開されている情報のみを使用してターゲットに関する情報を収集します。ターゲットのインフラストラクチャにパケットが送信されることはありません。これにより、公開されているデータに対して使用する場合は完全に検出不可能で、法的に安全になります。目標は、ターゲットとアクティブな接触を行う前に、WHOIS レコード、DNS の履歴、SSL 証明書ログ、検索エンジンのキャッシュ、および Web アーカイブから包括的な情報を作成することです。

手順

ステップ 1: WHOIS ルックアップ

import whois
import subprocess
import json
import re

def whois_lookup(domain):
    """python-whois ライブラリを使用して WHOIS ルックアップを実行します。"""
    # 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')}")

        # プライバシー保護の確認
        raw = str(w.text)
        if any(word in raw.lower() for word in ["privacy", "redacted", "withheld", "domains by proxy"]):
            print("\n  ⚠ WHOIS プライバシー保護が有効になっています")

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

def reverse_whois_email(email):
    """同じメールアドレスで登録されているドメインを検索します (ViewDNS または WHOIS API 経由)。"""
    url = f"https://viewdns.info/reversewhois/?q={email}"
    print(f"Reverse WHOIS for {email}: {url}")
    # 注: ViewDNS.info には Web UI があります。自動化には API を使用してください
    return url

whois_lookup("example.com")

ステップ 2: DNS 列挙と履歴

import dns.resolver
import requests

def dns_lookup(domain):
    """ドメインの DNS レコードを列挙します。"""
    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):
    """SecurityTrails API から過去の DNS レコードを取得します。"""
    headers = {"apikey": api_key, "Accept": "application/json"}
    base_url = "https://api.securitytrails.com/v1"

    # 過去の A レコード
    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):
    """無料のパッシブ DNS ソースをクエリします。"""
    results = {}

    # HackerTarget Passive DNS (無料)
    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 は {domain} のサブドメインを {len(hosts)} 個検出しました")
    except Exception as e:
        print(f"HackerTarget error: {e}")

    # CIRCL Passive DNS (無料、認証不要)
    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")

ステップ 3: リバース IP ルックアップ


def reverse_ip_lookup(ip_address):
    """同じ IP アドレスでホストされているすべてのドメインを検索します。"""
    # HackerTarget API (無料、最大 500 件の結果)
    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):
    """IP のジオロケーション、ASN、および組織情報を取得します。"""
    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.2
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

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

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

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

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)

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")

Step 5: Google Dorks

def generate_dorks(domain):
    """Generate a comprehensive set of Google dorks for a target domain."""
    company = domain.split(".")[0]  # Rough heuristic
    dorks = [
        # Infrastructure discovery
        (f"site:{domain}", "All indexed pages"),
        (f"site:{domain} inurl:admin", "Admin panels"),
        (f"site:{domain} inurl:login", "Login pages"),
        (f"site:{domain} inurl:api", "API endpoints"),
        (f"site:{domain} inurl:dev OR inurl:staging OR inurl:test", "Dev/staging environments"),

        # File exposure
        (f"site:{domain} filetype:pdf", "PDF documents"),
        (f"site:{domain} filetype:xlsx OR filetype:csv", "Spreadsheets"),
        (f"site:{domain} filetype:sql", "SQL database files"),
        (f"site:{domain} filetype:env OR filetype:config", "Config files"),
        (f"site:{domain} filetype:log", "Log files"),
        (f'site:{domain} ext:bak OR ext:old OR ext:backup', "Backup files"),

        # Credential exposure
        (f'site:github.com "{domain}" password OR secret OR api_key', "Credentials on GitHub"),
        (f'site:pastebin.com "{domain}"', "Domain on Pastebin"),
        (f'"{domain}" filetype:txt password', "Password files"),

        # Technology fingerprinting
        (f"site:{domain} intext:\"Powered by\"", "CMS/framework identification"),
        (f"site:{domain} inurl:wp-admin", "WordPress admin"),
        (f"site:{domain} inurl:jira", "Jira instances"),
        (f"site:{domain} inurl:confluence", "Confluence instances"),

        # Email harvesting
        (f'"@{domain}" filetype:xlsx', "Email lists in spreadsheets"),
        (f'"@{domain}" site:linkedin.com', "Employee emails on LinkedIn"),

        # Error pages (reveal technology stack)
        (f'site:{domain} intext:"Error" intext:"stack trace"', "Error pages with stack traces"),
        (f'site:{domain} "Index of /"', "Directory listings"),
    ]

    print(f"\n=== Google Dorks: {domain} ===")
    for query, description in dorks:
        encoded = query.replace(" ", "+").replace('"', '%22')
        url = f"https://www.google.com/search?q={encoded}"
        print(f"\n[{description}]")
        print(f"  Query: {query}")
        print(f"  URL:   {url}")

    return dorks

generate_dorks("example.com")

Step 6: Wayback Machine enumeration

def wayback_cdx_enumeration(domain, limit=1000):
    """
    Use the Wayback Machine CDX API to enumerate all historically crawled
    URLs for a domain. Reveals old endpoints, APIs, and forgotten pages.
    """
    cdx_url = "https://web.archive.org/cdx/search/cdx"
    params = {
        "url": f"*.{domain}/*",
        "output": "json",
        "fl": "original,timestamp,statuscode",
        "filter": "statuscode:200",
        "collapse": "original",
        "limit": limit,
    }

    resp = requests.get(cdx_url, params=params, timeout=60)
    data = resp.json()

    if len(data) <= 1:
        print(f"No Wayback data found for {domain}")
        return []

    urls = [row[0] for row in data[1:]]  # Skip header row
    timestamps = [row[1] for row in data[1:]]

    print(f"\n=== Wayback Machine: {domain} ===")
    print(f"Total unique URLs: {len(urls)}")

    # Find interesting endpoints
    interesting_patterns = [
        r'/api/', r'/admin/', r'/login', r'/dashboard',
        r'\.php', r'\.asp', r'\.env', r'\.bak', r'\.sql',
        r'/wp-', r'/cgi-bin/', r'\.xml', r'\.json',
    ]

    for pattern in interesting_patterns:
        matching = [u for u in urls if re.search(pattern, u, re.IGNORECASE)]
        if matching:
            print(f"\n  [{pattern}] {len(matching)} URLs:")
            for u in matching[:5]:
                print(f"    {u}")

    # Find unique subdomains from Wayback
    subdomains = set()
    for url in urls:
        match = re.match(r'https?://([^/]+)', url)
        if match:
            host = match.group(1)
            if domain in host:
                subdomains.add(host.lower())

    print(f"\n  Subdomains in Wayback: {len(subdomains)}")
    for sub in sorted(subdomains):
        print(f"    {sub}")

    return urls

wayback_cdx_enumeration("example.com", limit=500)

Step 7: Full passive recon pipeline

def full_passive_recon(domain, output_dir="."):
    """Run a complete passive recon pipeline for a target domain."""
    import os
    os.makedirs(output_dir, exist_ok=True)

    results = {"domain": domain, "timestamp": __import__("datetime").datetime.utcnow().isoformat()}

    print(f"\n{'='*60}")
    print(f"PASSIVE RECON: {domain}")
    print(f"{'='*60}")

    # 1. WHOIS
    results["whois"] = whois_lookup(domain)

    # 2. DNS records
    results["dns"] = dns_lookup(domain)

    # 3. Passive DNS (free sources)
    results["passive_dns"] = passive_dns_free(domain)

    # 4. Certificate transparency
    results["subdomains_crt"] = crt_sh_lookup(domain)

    # 5. Reverse IP for primary domain IP
    try:
        primary_ip = str(dns.resolver.resolve(domain, "A")[0])
        results["primary_ip"] = primary_ip
        results["reverse_ip"] = reverse_ip_lookup(primary_ip)
        results["ip_info"] = ip_info(primary_ip)
    except Exception as e:
        print(f"IP lookup failed: {e}")

    # 6. Wayback Machine
    results["wayback_urls"] = wayback_cdx_enumeration(domain, limit=200)

    # 7. Dorks (just generate, not execute automatically)
    results["dorks"] = [(q, d) for q, d in generate_dorks(domain)]

    # Save
    output_file = f"{output_dir}/passive_recon_{domain.replace('.', '_')}.json"
    with open(output_file, "w") as f:
        json.dump(results, f, indent=2, default=str)
    print(f"\n✓ Full passive recon saved to {output_file}")

    return results

full_passive_recon("example.com")

Passive Recon Checklist

Technique Tool Data Obtained
WHOIS lookup python-whois Registrar, creation date, name servers, registrant
DNS records dnspython A, MX, NS, TXT, SOA records
DNS history SecurityTrails, CIRCL Historical IPs, old name servers
Reverse IP HackerTarget API Other domains on same server
SSL certs crt.sh All subdomains ever issued a cert
Wayback Machine CDX API Historical URLs, old endpoints
Google Dorks Google Search Exposed files, admin panels, credentials
Shodan shodan library Open ports, services, banners
Passive DNS CIRCL, HackerTarget Current and historical DNS records

Guidelines

  • Zero contact: All techniques in this skill make zero requests to the target's infrastructure. Requests go to third-party services (crt.sh, HackerTarget, CIRCL, etc.) only.
  • Rate limits: HackerTarget free tier allows 100 API calls per day. Spread queries or cache results.
  • crt.sh is gold: Certificate transparency is the fastest way to find subdomains. Combined with Wayback Machine, it often reveals more than active brute-force DNS.
  • Historical IPs: Old IPs from DNS history may point to WAF bypass opportunities or forgotten servers. Always check historical data.
  • Google cache: Combine cache:example.com with regular dorks to access pages that have since been removed.