mcpField
R
recon-sector-expansion

recon-sector-expansion

github.com/uphiagoセキュリティ1.3k39 回インストール8 週間前に更新
リポジトリ

recon-sector-expansion について

複数の業種に偵察範囲を広げ、新たな企業ドメインを一括テストする、許可されたセキュリティテスト向けスキルです。既存対象の確認、証明書透明性ログや手動での発見、生存確認、テストと結果分析を扱います。

主な機能

  1. 01テスト済みドメインの重複排除
  2. 02crt.sh と検索によるドメイン発見
  3. 03インフラや無関係な結果の除外
  4. 04候補サイトの稼働確認
  5. 05稼働ドメインの一括テスト
  6. 06一般的な Web セキュリティ問題の分析

活用シーン

  1. 01既知の業種以外へ許可済みテストを拡張
  2. 02地域サービス業のテスト対象を発見
  3. 03新たに見つけたドメイン群を確認・テスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/uphiago/recon-skills --skill recon-sector-expansion

インストール後、Claude Code で/recon-sector-expansionを入力して読み込まれたか確認します。

詳細

RECON-SECTOR-EXPANSION — 未開拓セクターの一括探索

使用するタイミング

認可されたエンゲージメント内で、新しいセクターへのカバレッジ拡大に使用します。すでに把握している単一のターゲットを深掘りするのではなく、5〜10種類の異なるセクターで、新たに20以上の企業ドメインを見つけてテストします。ターゲットリストは認可されたスコープ内のものに限ります。一括拡大はスコープが定められたプログラム向けであり、無差別なスクレイピングには使用しません。

兆候:

  • 「規制対象外のセクターで、さらに20件のドメインを見つけてください」
  • 「カバレッジを拡大する必要があります。まだテストしていないのはどこですか?」
  • 「いつものセクター以外で、新しいターゲットを見つけてください」

セクター拡大ワークフロー

フェーズ 1 — カバレッジ監査

新しいターゲットを見つける前に、すでにテスト済みの対象を把握します。

# 1. Check existing target list
ALL_TARGETS="${OUTPUT_DIR:-./output}/sector-expansion/all_targets.txt"
ALL_MASSIVE="${OUTPUT_DIR:-./output}/sector-expansion/all_massive.txt"

# Extract all domains from both files
grep -ohE '[a-zA-Z0-9][a-zA-Z0-9.-]*\.[a-z]{2,}' "$ALL_TARGETS" "$ALL_MASSIVE" | sort -u > /tmp/targets_deduped.txt
echo "Already tested: $(wc -l < /tmp/targets_deduped.txt) domains"

# 2. Check what findings files exist
ls "${OUTPUT_DIR:-./output}"/sector-expansion/*_findings.md 2>/dev/null | wc -l

# 3. Identify which sectors have been covered by inspecting findings files
# Check for sector-specific keywords
for sector in "dentist" "dental" "gym" "fitness" "baker" "auto_body" "carpet" "laundry" "daycare" "pest" "tree" "pet"; do
  count=$(grep -l "$sector" "${OUTPUT_DIR:-./output}"/sector-expansion/*_findings*.md 2>/dev/null | wc -l)
  echo "Sector '$sector': $count findings files"
done
フェーズ 2a — ソース 1:crt.sh Certificate Transparency(地域密着型ビジネスセクターに推奨)

地域サービスのセクター(屋根工事、造園、配管、プールサービス、HVAC)では、全国チェーンが存在しないため、crt.sh が主な探索ソースです。これらの企業は、[name][SECTOR_EXAMPLE] や [city][SECTOR_EXAMPLE] のような説明的なドメイン名を使用していることがよくあります。

crt.sh のクエリパターン

幅広いクエリでは JSON API が 502/503 を返すため、HTML 出力モードで単純なセクターキーワードを使用します。

QUERY="roofing"
curl -s --max-time 40 --connect-timeout 10 \
  "https://crt.sh/?q=${QUERY}&excluded=expired&dedup=Y" \
  -H 'User-Agent: Mozilla/5.0' 2>/dev/null | \
  grep -oE '>[A-Za-z0-9][A-Za-z0-9.-]*\.com<' | \
  sed 's/^>//;s/<$//' | \
  sort -u >> /tmp/all_crt_domains.txt

パラメーターの説明:

  • &excluded=expired — 現在有効な証明書のみ
  • &dedup=Y — 証明書を重複排除
  • -oE 正規表現 — BusyBox 互換(-P フラグは使用できません)
  • &output=json は指定しない — 幅広いクエリでは JSON API が不安定です
セクターキーワードカタログ

候補を集めるため、以下のセクターをすべてクエリします。

SECTORS="roofing landscaping pestcontrol dentist dentist fitness \
cleaningservice movingcompany photography vetclinic realtor hvac \
treeservice lawncare plumbing poolcleaning windowcleaning petsalon \
barbershop daycare carpetcleaning handyman lawfirm concrete \
autorepair petgrooming autobody remodeling"
for sector in $SECTORS; do
  curl -s --max-time 40 --connect-timeout 10 "https://crt.sh/?q=${sector}&excluded=expired&dedup=Y" \
    -H 'User-Agent: Mozilla/5.0' 2>/dev/null | \
    grep -oE '>[A-Za-z0-9][A-Za-z0-9.-]*\.com<' | \
    sed 's/^>//;s/<$//' >> /tmp/all_crt_domains.txt
  sleep 2  # avoid rate limiting
done
crt.sh の結果からノイズを除外する

crt.sh は多くのサブドメインやインフラホストを返します。以下のパターンを除外します。

import re

# Common noise patterns from crt.sh
skip_patterns = [
    '^autodiscover\\.', '^vpn\\.', '^api\\.', '^mail\\.', '^remote\\.',
    '^webmail\\.', '^crm\\.', '^ftp\\.', '^test\\.', '^dev\\.',
    '^exchange', '^hostmaster', '^owa\\.', '^smtp\\.',
    # Generic placeholder domains
    'roofingcompany\\.com$', 'landscapingcompany\\.com$', 'vetclinic\\.com$',
]

次に、ドメイン名に一致するセクターを割り当てます。

SECTOR_KEYWORDS = {
    'roofing': ['roofing', 'roof', 'roofer'],
    'landscaping': ['landscaping', 'landscape', 'lawn'],
    'hvac': ['hvac', 'heating', 'cooling', 'air'],
    'dental': ['dentist', 'dental'],
    'pest-control': ['pest', 'mosquito'],
    'daycare': ['daycare'],
    'legal': ['lawfirm', 'lawfirm'],
    'fitness': ['gym', 'fitness'],
    'auto-repair': ['autorepair', 'auto', 'collision'],
    'cleaning': ['cleaning', 'maid'],
    'veterinary': ['vet', 'pet'],
    'pool-services': ['pool', 'spa'],
}
ノイズとなるサブドメインのフィルタリング

crt.sh の HTML 出力に grep を使用すると、大手ホスティングプロバイダーのエントリーも取得されます。以下を除外してください。

  • autodiscover.*、vpn.*、api.*、mail.*、remote.* — インフラ
  • .*\.hvacrightnow\.com、.*\.hvacbrain\.com — SaaS プラットフォーム(実際のターゲットではありません)
  • .*\.theroofingprofessor\.com、.*\.bakerroofing\.com — 大規模組織のサブドメイン
  • .*\.singaporepools\.com、.*\.devsingaporepools\.com — 米国外

重複排除の前に、除外パターンを使って grep -v に渡すと時間を節約できます。

フェーズ 2b — ソース 2:Google/手動検索(全国チェーンに推奨)

全国または地域のチェーンが中心のセクター(歯科、ジム、ベーカリー、自動車板金)では、チェーンのドメイン名にセクターのキーワードが含まれることは少ないため、Google 検索を使用します。

セクター候補の探し方チェーンの例
歯科チェーンGoogle で「top dental chains USA」を検索Aspen Dental、Gentle Dental、Coast Dental、DentalWorks、Heartland Dental、Pacific Dental
ジム/フィットネスGoogle で「largest fitness chains USA」を検索F45、Barry's、CrossFit、Gold's Gym、24 Hour Fitness、LA Fitness、OrangeTheory
ベーカリーチェーンGoogle で「largest bakery chains USA」を検索Cinnabon、Crumbl、Insomnia Cookies、Sprinkles、Nothing Bundt Cakes
自動車板金/衝突修理Google で「auto body repair chains USA」を検索ABRA、Caliber、Gerber、Service King、Fix Auto、Sterling、Crash Champions
カーペットクリーニングGoogle で「carpet cleaning companies USA」を検索Chem-Dry、ServPro、Stanley Steemer、Rainbow International、Heaven's Best
ランドリーサービスGoogle で「laundry delivery service USA」を検索Poplin、Rinse、Washlava、LaundryHeap
保育/託児Google で「largest daycare chains USA」を検索Bright Horizons、KinderCare、Goddard、Primrose、Children of America
ペットグルーミングGoogle で「pet grooming chains USA」を検索Petco、PetSmart、Camp Bow Wow、Dogtopia
害虫駆除Google で「pest control companies USA」を検索Terminix、Orkin、Ehrlich、Bulwark、Arrow
樹木サービスGoogle で「tree service companies USA」を検索Davey、SavATree、Arbor Care、Bartlett
フェーズ 3 — 一括稼働確認(Curl プローブ)

候補ドメインが50〜60件集まったら、すでにテスト済みのドメインを除外してから、HTTP を素早く確認します。

# Filter against known targets
grep -v -f /tmp/targets_deduped.txt /tmp/candidates.txt | sort -u > /tmp/fresh_candidates.txt

# Quick alive check with curl (serial, ~2.5 min for 60 targets)
echo "=== Checking all candidates with curl ==="
for domain in $(cat /tmp/fresh_candidates.txt); do
  code=$(curl -sk -o /dev/null -w '%{http_code}' --connect-timeout 5 --max-time 8 "https://${domain}" 2>/dev/null)
  echo "$domain => $code"
done > /tmp/curl_alive.txt

# Extract alive domains (anything not 000/timeout)
grep -v "=> 000" /tmp/curl_alive.txt | grep -v "^$" | cut -d' ' -f1 > /tmp/alive_domains.txt
echo "Alive: $(wc -l < /tmp/alive_domains.txt) / $(wc -l < /tmp/fresh_candidates.txt) candidates"
フェーズ 4 — 全テストパイプライン

次に、生存しているドメインに対して 6 ステップのパイプライン全体を実行します。大規模(20 件以上のターゲット)で最も効率的な方法は、Python のバッチスクリプトです:

#!/usr/bin/env python3
"""Batch test targets: httpx → WP REST → CORS → XMLRPC → ports"""
import subprocess, json, sys, os, re, time

OUTPUT_DIR = os.environ.get("OUTPUT_DIR", "./output") + "/sector-expansion"

def run(cmd, timeout=15):
    try:
        r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
        return r.stdout + r.stderr
    except subprocess.TimeoutExpired:
        return f"TIMEOUT ({timeout}s)"
    except Exception as ex:
        return f"ERROR: {ex}"

def test_domain(domain, sector):
    findings = {"domain": domain, "sector": sector, "wp": False, "wp_users": 0,
                "cors_reflected": False, "xmlrpc_open": False}
    
    # STEP 1: httpx probe
    r = run(f"httpx -sc -title -tech-detect -server -no-color -u https://{domain} -timeout 10")
    findings["http"] = r.strip()
    
    # STEP 2: WordPress REST API check (3 paths)
    for path in ["/wp-json/wp/v2/users", "/wp-json/", "/?rest_route=/wp/v2/users"]:
        r = run(f'curl -sk -o /dev/null -w "%{{http_code}}" --max-time 10 --connect-timeout 10 "https://{domain}{path}"')
        if r in ["200"]:
            findings["wp_rest_status"] = f"{path} => HTTP {r}"
            findings["wp"] = True
            # Enumerate users
            user_resp = run(f'curl -sk --max-time 10 --connect-timeout 10 "https://{domain}{path}"', timeout=12)
            try:
                users = json.loads(user_resp)
                if isinstance(users, list):
                    findings["wp_users"] = len(users)
                    findings["wp_users_list"] = [u.get("name","") for u in users]
            except:
                pass
            break
    
    # Fallback: check page source for WP markers
    if not findings["wp"]:
        r = run(f'curl -sk --max-time 10 --connect-timeout 10 "https://{domain}"', timeout=12)
        if "/wp-content/" in r or "/wp-includes/" in r:
            findings["wp"] = True
    
    # STEP 3: CORS test
    target_url = f"https://{domain}/wp-json/wp/v2/users" if findings["wp"] else f"https://{domain}/"
    r = run(f'curl -sk -H "Origin: https://evil.com" -D- --max-time 10 --connect-timeout 10 "{target_url}" | grep -i access-control', timeout=12)
    if "Access-Control-Allow-Origin" in r:
        findings["cors_reflected"] = True
        findings["cors_credentialed"] = "Access-Control-Allow-Credentials" in r
    
    # STEP 4: XMLRPC check
    r = run(f'curl -sk -o /dev/null -w "%{{http_code}}" --max-time 10 --connect-timeout 10 -X POST "https://{domain}/xmlrpc.php"', timeout=12)
    findings["xmlrpc_open"] = (r.strip() == "200")
    
    # STEP 5: Port scan (top 20)
    r = run(f'nmap --top-ports 20 -T4 --open -n {domain}', timeout=120)
    ports = [line.split('/')[0] for line in r.split('\n') if re.search(r'^\d+/tcp\s+open', line)]
    findings["ports"] = ports
    
    # Severity calculation
    score = sum([findings["wp"], bool(findings["wp_users"] > 0), findings["cors_reflected"], findings["xmlrpc_open"]])
    findings["vuln"] = "HIGH" if score >= 3 else ("MEDIUM" if score >= 2 else ("LOW" if score >= 1 else "NONE"))
    
    return findings

# Usage
domains = {"[TARGET_1]": "dental", "[TARGET_2]": "gym_fitness", ...}
for domain, sector in domains.items():
    f = test_domain(domain, sector)
    # Write findings.md
    content = f"""## {domain}
| Field | Value |
|-------|-------|
| HTTP | {f['http'][:200]} |
| WordPress | {'Yes' if f['wp'] else 'No'}, users: {f['wp_users']} |
| CORS | {'Reflected' + (' WITH CREDENTIALS' if f.get('cors_credentialed') else '') if f['cors_reflected'] else 'Not reflected'} |
| XMLRPC | {'Open' if f['xmlrpc_open'] else 'Closed'} |
| Ports | {', '.join(f['ports']) if f['ports'] else 'none found'} |
| Sector | {sector} |
| Vuln | {f['vuln']} |
"""
    with open(f"{OUTPUT_DIR}/{domain}_findings.md", 'w') as out:
        out.write(content)
フェーズ 5 — 結果の分析

次のような、シグナルの強いパターンが見つからないか確認します:

パターン確認方法深刻度
認証情報付き CORS/wp-json/wp/v2/users で Access-Control-Allow-Origin: http://evil.com と Access-Control-Allow-Credentials: true が設定されている高
WordPress ユーザー情報の露出/wp-json/wp/v2/users の REST API が、名前、スラッグ、アバターを含むユーザーオブジェクトの配列を返す中
ワイルドカード CORSAccess-Control-Allow-Origin: *(認証情報なし)中
XMLRPC の開放/xmlrpc.php が HTTP 200 を返す(WAF によってメソッド一覧レベルでブロックされている場合があります)低~中
標準以外のサービスのポート開放21(FTP)、3306(MySQL)、3389(RDP)、5900(VNC)、6379(Redis)さまざま
PHPInfo の露出/info.php、/test.php などで 400 件以上の設定項目が表示される低

業種別チェーンのドメイン一覧

米国の全国・地域チェーンのドメインを厳選した一覧です(2026-06 時点で稼働を確認済み):

歯科
aspendental.com  gentledental.com  dentalworks.com  coastdental.com
brightnow.com    sagedental.com    western-dental.com  castledental.com
heartlanddental.com  pacificdental.com  dentalcarealliance.com
ジム/フィットネス
[FITNESS_CHAIN]  barrys.com  crossfit.com  goldsgym.com
24hourfitness.com  lafitness.com  orangetheory.com  planetfitness.com
anytimefitness.com  snapfitness.com  crunch.com
ベーカリー
cinnabon.com  crumbl.com  insomniacookies.com  sprinkles.com
bakery.example.com  restaurant.example.com  donut.example.com
自動車板金/衝突修理
abracollision.com  serviceking.com  sterlingautobody.com  crashchampions.com
fixauto.com  true2form.com  auto-body-shop.com
calibercollision.com  carstar.com  gerbercollision.com  maaco.com
カーペットクリーニング
chemdry.com  servpro.com  rainbowintl.com  heavenlycarpet.com
stanleysteemer.com  zerorez.com  coit.com
ランドリー
poplin.com  rinse.com  washlava.com  laundryheap.com
speedqueen.com  spincyclelaundry.com
保育所
brighthorizons.com  childrenofamerica.com  kindercare.com  goddardschool.com
primroseschools.com  montessori.com  lajollamontessori.com  tutortime.com
害虫駆除
terminix.com  orkin.com  echols.com  bulwarkpest.com
arrowpest-control.example.com  dodsonbros.com  massey-services.com
樹木管理サービス
treecare.com  arborcare.com  davey.com  savatree.com
bartlett.com  allstatetree.com  firstchoicetree.com
屋根工事(地域・地方 — crt.sh で検出)
aastro[SECTOR_EXAMPLE]  abcarnes[SECTOR_EXAMPLE]  americaschoiceroofers.com  arvada[SECTOR_EXAMPLE]
bacon[SECTOR_EXAMPLE]  baker[SECTOR_EXAMPLE]  bricor[SECTOR_EXAMPLE]  capitol[SECTOR_EXAMPLE]
charlottepro[SECTOR_EXAMPLE]  coloradopro[SECTOR_EXAMPLE]  eh[SECTOR_EXAMPLE]  errdaddy[SECTOR_EXAMPLE]
fifthwall[SECTOR_EXAMPLE]  freeman[SECTOR_EXAMPLE]  ghatley.com  hambro[SECTOR_EXAMPLE]
hollis[SECTOR_EXAMPLE]  integrityrc.com  kbfamily[SECTOR_EXAMPLE]  mcas[SECTOR_EXAMPLE]
na[SECTOR_EXAMPLE]  pabco[SECTOR_EXAMPLE]  roofon.com  roofwithfoster.com
sandiegocounty[SECTOR_EXAMPLE]  springfield[SECTOR_EXAMPLE]  tomtheroofer.com
universityroof.com  vanguardroofingltd.com  wallace[SECTOR_EXAMPLE]
造園(地域・地方 — crt.sh で検出)
delliquadrilandscape.com  dundeedig.com  guardyouryardpa.com
landscapingsi.com  miles[SECTOR_EXAMPLE]  moss[SECTOR_EXAMPLE]
mountainlandscapingkc.com  sarasota[SECTOR_EXAMPLE]  trinas[SECTOR_EXAMPLE]
moss[SECTOR_EXAMPLE]  eo[SECTOR_EXAMPLE]  brucewilson[SECTOR_EXAMPLE]
HVAC(地域・地方 — crt.sh で検出)
airzonahvac.com  bigfishhvac.com  dormarhvac.com  specializedhvac.com
ゼネコンなど(crt.sh で検出)
allysonsflowers.com  [INSURANCE_CO]  canopyroofers.com
gelinc.com  rvroofrepairflorida.com  spartanroofingbc.com

注意点

  1. 「テスト済み」ドメインの一覧として、単一のファイルだけに頼らないでください。 all_targets.txt と all_massive.txt は異なる場合があります。両方を統合し、sort -u で重複を除去してください。

  2. CloudFlare は一部のサイトで WP REST API をブロックします。 /wp-json/wp/v2/users で HTTP 403 が返る場合は、/?rest_route=/wp/v2/users を試すか、ページソースで /wp-content/ のマーカーを確認してください。

  3. XMLRPC は 200 を返しても、system.listMethods をブロックする場合があります。 CloudFlare などの WAF はメソッド一覧を取得する XML ボディをブロックしながら、エンドポイントを system.multicall 用に開放している場合があります。別々にテストしてください。

  4. 301/302 を返すサイトでも CORS が設定されている場合があります。 リダイレクトレスポンスに CORS ヘッダーが含まれることがあります(brighthorizons.com は 301 レスポンスでワイルドカード CORS を返しました)。ランディングページが異なる場合は、リダイレクト先の URL もテストしてください。

  5. BusyBox の grep の制限。 最小限の環境では grep -P が使えない BusyBox が提供される場合があります。拡張パターンを使った grep -oE を使用してください。BusyBox の find には -printf もありません。代わりに -exec echo {} \; またはシェルループを使用してください。

  6. crt.sh のレート制限。 短時間に大量のクエリを送ると HTTP 502/503 が返ります。クエリの間隔を 3 秒 空け、エラーが繰り返し発生する場合は 15~30 秒待ってください。JSON API(&output=json)は広範なクエリで特に不安定です。代わりに HTML 出力モード(&excluded=expired&dedup=Y)を使用してください。

  7. ポートスキャンの所要時間。 nmap --top-ports 20 -T4 はターゲットごとに約 3~5 秒かかります。バックグラウンドで 5~7 件のターゲットを並行処理してください。

  8. WordPress 以外のサイトでの CORS。 CloudFront や S3 上の一般的な静的サイトでも CORS が反映されることがあります。必ず / と、既知の API エンドポイントの両方でテストしてください。

  9. Python の JSON 解析におけるエッジケース。 WP REST API のユーザーエンドポイントは、保護されたエンドポイントでリストではなくオブジェクトを返す場合があります(HTTP 401 と 200 のレスポンスコードの違いによるものです)。必ず isinstance(users, list) を確認してください。

検証

  1. 業種別ドメインの生成 — 業種のキーワードから候補ドメインが生成されることを確認します:
    echo 'roofing landscaping pestcontrol' | tr ' ' '
    

' | wc -l

2. **crt.sh へのアクセス** — crt.sh の CDX エンドポイントに到達できることを確認します:
```bash
curl -s --max-time 10 --connect-timeout 10 -o /dev/null -w '%{http_code}' 'https://crt.sh/?q=test&excluded=expired&dedup=Y'

すべてのテストで、業種の展開準備ができていることを確認します。


関連スキル

  • web2-recon — ターゲットごとの技術テストパイプライン(httpx、WP 列挙、CORS、XMLRPC、ポートスキャン、JS 分析)
  • recon-sector — sectors.yaml に含まれる 25 業種を対象とした、統一されたパラメーター化業種別偵察

コメント