recon-sector-expansion について
複数の業種に偵察範囲を広げ、新たな企業ドメインを一括テストする、許可されたセキュリティテスト向けスキルです。既存対象の確認、証明書透明性ログや手動での発見、生存確認、テストと結果分析を扱います。
主な機能
- 01テスト済みドメインの重複排除
- 02crt.sh と検索によるドメイン発見
- 03インフラや無関係な結果の除外
- 04候補サイトの稼働確認
- 05稼働ドメインの一括テスト
- 06一般的な Web セキュリティ問題の分析
活用シーン
- 01既知の業種以外へ許可済みテストを拡張
- 02地域サービス業のテスト対象を発見
- 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 が、名前、スラッグ、アバターを含むユーザーオブジェクトの配列を返す | 中 |
| ワイルドカード CORS | Access-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
注意点
-
「テスト済み」ドメインの一覧として、単一のファイルだけに頼らないでください。
all_targets.txtとall_massive.txtは異なる場合があります。両方を統合し、sort -uで重複を除去してください。 -
CloudFlare は一部のサイトで WP REST API をブロックします。
/wp-json/wp/v2/usersで HTTP 403 が返る場合は、/?rest_route=/wp/v2/usersを試すか、ページソースで/wp-content/のマーカーを確認してください。 -
XMLRPC は 200 を返しても、system.listMethods をブロックする場合があります。 CloudFlare などの WAF はメソッド一覧を取得する XML ボディをブロックしながら、エンドポイントを
system.multicall用に開放している場合があります。別々にテストしてください。 -
301/302 を返すサイトでも CORS が設定されている場合があります。 リダイレクトレスポンスに CORS ヘッダーが含まれることがあります(brighthorizons.com は 301 レスポンスでワイルドカード CORS を返しました)。ランディングページが異なる場合は、リダイレクト先の URL もテストしてください。
-
BusyBox の grep の制限。 最小限の環境では
grep -Pが使えない BusyBox が提供される場合があります。拡張パターンを使ったgrep -oEを使用してください。BusyBox のfindには-printfもありません。代わりに-exec echo {} \;またはシェルループを使用してください。 -
crt.sh のレート制限。 短時間に大量のクエリを送ると HTTP 502/503 が返ります。クエリの間隔を 3 秒 空け、エラーが繰り返し発生する場合は 15~30 秒待ってください。JSON API(
&output=json)は広範なクエリで特に不安定です。代わりに HTML 出力モード(&excluded=expired&dedup=Y)を使用してください。 -
ポートスキャンの所要時間。
nmap --top-ports 20 -T4はターゲットごとに約 3~5 秒かかります。バックグラウンドで 5~7 件のターゲットを並行処理してください。 -
WordPress 以外のサイトでの CORS。 CloudFront や S3 上の一般的な静的サイトでも CORS が反映されることがあります。必ず
/と、既知の API エンドポイントの両方でテストしてください。 -
Python の JSON 解析におけるエッジケース。 WP REST API のユーザーエンドポイントは、保護されたエンドポイントでリストではなくオブジェクトを返す場合があります(HTTP 401 と 200 のレスポンスコードの違いによるものです)。必ず
isinstance(users, list)を確認してください。
検証
- 業種別ドメインの生成 — 業種のキーワードから候補ドメインが生成されることを確認します:
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 業種を対象とした、統一されたパラメーター化業種別偵察