krutarthshukla/subdomain-recon
GitHub: krutarthshukla/subdomain-recon
一款基于 Claude Code 的自动化子域名侦察技能,通过多阶段流水线实现从组织名称到完整外部资产足迹的端到端发现与测绘。
Stars: 0 | Forks: 0
# 子域名侦察
给它一个组织名称——或者仅仅是一个或两个域名——它就能测绘出整个外部足迹:该组织拥有的所有根域名、它能找到的每一个子域名,以及哪些子域名是实际在线的。交给它一个**组织名称**,它会先发现并验证拥有的根域名;直接交给它**域名**,它会直接开始枚举其子域名。它首先从被动源中提取信息,然后叠加更激进的技术,最后通过探测存活状态并生成报告。
在实际应用中,它发现的资产通常明显多于单独运行 `subfinder` 或 `amass` 所得到的结果,这主要是因为它不仅仅停留在证书透明度上——它还会利用 favicons、TLS 证书、source maps、ASN 范围以及一些机器学习和暴力破解手段进行信息拓展。
## 如何使用它
这是一个 Claude Code 技能,所以运行它的最简单方法就是用通俗的英语直接提问:
- `recon acme.com`
- `find all subdomains of Acme`
- `map the attack surface of acme.com, acme.io`
给它一个**组织名称**,它会先发现并验证拥有的根域名;给它一个或多个**域名**,它会直接枚举这些域名。你不需要选择模式——它是根据你输入的内容自动检测的。在新机器上首次运行时会自动安装工具链(需要几分钟,仅此一次),每次运行都会在 `~/Desktop/` 下生成一个独立的文件夹。
更喜欢使用终端?同样可以通过单个命令运行——请参阅下方的[快速入门](#quick-start)。
## 功能介绍
- **查找根域名,而不仅仅是子域名。** 从组织名称开始,扩展到收购的公司、品牌域名和 ccTLD 变体。它还会保留仅具有 NS 记录(无 A 记录)的域名,而简单的扫描可能会丢弃这些域名。
- **在枚举之前检查所有权。** 能够解析的域名并不意味着该组织拥有它——许多域名已被停放或被抢注。此步骤将根域名分类为 拥有 / 不确定 / 拒绝,这样你就不会浪费时间枚举别人的停放页面。被拒绝的域名会被记录下来,而不是被直接丢弃。
- **27 个被动信息源。** Merklemap、crt.sh、Columbus、LeakIX、Netlas、host.io、C99.nl、`uncover`(它本身封装了 18 个引擎)、asnmap + PTR 等等。
- **25 种高级技术。** Favicon-hash 关联、`.well-known` 应用关联文件、source-map 提取、Azure 租户枚举、SaaS 平台模式、Postman 工作区搜索、BBOT、katana JS 爬虫、`subwiz` 机器学习预测、BadDNS 接管检查以及 JARM/TLS-SAN 关联。
- **主动枚举。** DNS 暴力破解(默认使用 top-110k 字典;可选 n0kovo 3M),使用 `alterx` 进行排列丰富,以及 TLS SAN-on-CIDR —— 按域名并行运行,并配有各域名的看门狗。
- **存活探测与报告。** 探测它找到的所有内容,并按源域名分组编写文本报告。
- **持续监控(可选)。** 订阅 CT 日志,并在出现新子域名时收到警报。
## 环境要求
- Python 3.9 或更高版本
- 安装程序设置的工具(完整列表见下文)
- API 密钥是可选的——免费额度通常就足够了。建议获取:Merklemap、LeakIX、Netlas、Validin、host.io、C99.nl
`install_tools.sh` 是幂等的且跨平台的(macOS + Linux),并且如果缺少任何内容,`run_all.sh` 会自动调用它——因此你不必手动运行它。系统上已有的工具会被检测到并且永远不会重新安装。
### 它安装/使用的工具
| 组别 | 工具 |
|-------|-------|
| 被动枚举 | `subfinder`, `assetfinder`, `chaos`, `findomain`, `amass`, `theHarvester`, `uncover` |
| DNS 解析 / 暴力破解 | `dnsx`, `puredns`, `massdns`, `shuffledns`, `alterx` |
| 证书 / TLS / ASN 关联 | `tlsx`, `asnmap`, `mapcidr`, `cdncheck`, `cero` |
| 爬虫 / 归档 | `katana`, `gau`, `waybackurls`, `sourcemapper` |
| 探测 / 实用工具 | `httpx`, `anew` |
| CT 监控 | `gungnir` |
| 机器学习 / 接管 (Python) | `bbot`, `subwiz`, `baddns` |
| Python 库 | `requests`, `mmh3`, `pyyaml` |
| 字典 | n0kovo 3M 子域名 |
如果无法在给定主机上安装某个工具,该技术将被跳过,但运行的其余部分将继续执行。
## 安装说明
你无需运行此命令——`run_all.sh` 会自动安装任何缺失的内容。但如果需要手动设置(或刷新)工具链:
```
bash scripts/install_tools.sh
source ~/.recon-tools/activate.sh
# 可选,但推荐 — 添加免费 API keys
nano ~/.config/subdomain-recon/api_keys.yaml
```
如果你只想添加几个密钥,这些将是付出最少努力获得最大回报的选择:
| 密钥 | 费用 | 为什么值得 |
|-----|------|-------------------|
| Merklemap | 免费层 | 1000 亿+ CT 行,近乎即时 |
| LeakIX | 免费(需注册) | 独立爬虫,不仅仅是 CT |
| Netlas | 每天 50 次请求免费 | 深层嵌套,正则表达式搜索 |
| Validin | 免费社区版 | 最好的免费 RiskIQ 替代品 |
| C99.nl | 约 $5/月 | 最便宜的商业子域名 API |
| host.io | 每月 1000 次免费 | 反向 IP / NS / MX 查询 |
Merklemap、Validin、LeakIX 和 Netlas 加起来每月花费不到 $100,覆盖范围比 SecurityTrails 还要广。
## 快速入门
一条命令即可运行整个流程。给它**要么**一个组织名称**要么**一个或
多个域名——模式会自动检测:
```
# 组织名称 → 发现所有拥有的根域,然后枚举它们全部
bash scripts/run_all.sh "Acme Corp"
# 单个域名 → 仅枚举该域名的子域名
bash scripts/run_all.sh "acme.com"
# 多个域名 → 准确枚举这些域名(不进行根域发现)
bash scripts/run_all.sh "acme.com,acquired-co.com,product.io"
```
每次运行都在 `~/Desktop/_/` 下独立运行:报告
(`_domains.txt`,活跃主机在 `# LIVE HOSTS` 部分下),完整的
`run.log`,`work/` 中的分阶段中间产物,以及(组织模式下)`rejected_domains.txt`。
## 工作原理
分为八个阶段。每个阶段为下一个阶段提供数据,如果你只需要 pipeline 的一部分,你可以单独运行其中任何一个阶段。
```
org name + seed domains
│
┌──────────────▼───────────────┐
│ Phase 1 Domain Discovery │ expand org → acquisitions/brands/ccTLDs
│ │ + NS-existence sweep
└──────────────┬───────────────┘
│ confirmed roots
┌──────────────▼───────────────┐
│ Phase 1.5 Ownership Validation│ owned / uncertain / rejected (parked)
│ │ rejected logged, never silently dropped
└──────────────┬───────────────┘
│ owned roots
┌──────────────▼───────────────┐
│ Phase 2 Passive Enumeration │ 27 CT/API sources, no target contact
│ │
└──────────────┬───────────────┘
│ passive subdomains
┌──────────────▼───────────────┐
│ Phase 3 Advanced Techniques │ 25 asset-pivot techniques
│ │ (favicon/JARM/sourcemap/BBOT/ML…)
└──────────────┬───────────────┘
│ advanced subdomains
┌──────────────▼───────────────┐
│ Phase 4 Active Enumeration │ brute (top-110k, parallel+watchdog),
│ │ permutation, TLS SAN-on-CIDR
└──────────────┬───────────────┘
│ brute / permuted subdomains
┌──────────────▼───────────────┐
│ Phase 5 Merge + Dedupe │ union all sources, normalise, cache
│ │
└──────────────┬───────────────┘
│ merged subdomains
┌──────────────▼───────────────┐
│ Phase 6 Live Probe │ concurrent HTTP probe + tech detect
│ │
└──────────────┬───────────────┘
│ live hosts
┌──────────────▼───────────────┐
│ Phase 7 Report │ grouped report → ~/Desktop
│ │
└───────────────────────────────┘
```
| 阶段 | 输入 | 产出 | 处理内容 |
|-------|-------|----------|--------------|
| **1 — 域名发现** | 组织名称、收购公司、缩写 | 确认的根域名 | 将组织转化为候选根域名列表——品牌、ccTLD 变体、收购公司——并保留任何具有存活 NS 记录的域名,即使它没有 A 记录,因为这些很容易被遗漏。 |
| **1.5 — 所有权验证** | 确认的根域名 | 拥有的根域名,拒绝的根域名 | 决定每个根域名是否真正属于该组织。拥有的(受信任或具有明确的所有权信号)和不确定的(无信号,但以防万一予以保留)都会继续下一步;停放或待售的域名会被搁置并记录。 |
| **2 — 被动枚举** | 拥有的根域名 | 被动子域名 | 查询 27 个被动来源(Merklemap、crt.sh、Columbus、LeakIX、Netlas、host.io、C99.nl、`uncover` 的 18 个引擎、asnmap + PTR 等)。绝不直接触碰目标。 |
| **3 — 高级技术** | 根域名 + 被动结果 | 高级子域名 | 运行 25 种关联技术:favicon hashes、`.well-known` 应用关联、source maps、Azure 租户、17 种 SaaS 模式、Postman 搜索、BBOT、katana JS 爬取、`subwiz` 机器学习预测、BadDNS 接管检查以及 JARM/TLS-SAN 关联。 |
| **4 — 主动枚举** | 拥有的根域名 + 被动结果 | 暴力破解 / 排列生成的子域名 | DNS 暴力破解(默认使用 top-110k;可选 n0kovo 3M)、排列丰富以及 TLS SAN-on-CIDR —— 按域名并行运行,并配有各域名的看门狗。会先测试泛解析,以免被误报淹没。 |
| **5 — 合并 + 去重** | 上述所有内容 | 合并的子域名集合 | 结合所有来源,规范化大小写,移除重复项,并更新各组织的缓存。 |
| **6 — 存活探测** | 合并的子域名 | 存活主机 | 通过 HTTP/HTTPS(300 个线程)探测所有内容,并记录状态、标题和检测到的技术。 |
| **7 — 报告** | 合并的子域名 + 拒绝的根域名 | `~/Desktop/_domains.txt` | 编写按源域名分组的最终报告,存活主机列在末尾。 |
如果你需要持续覆盖,你还可以订阅 CT 日志,并与上一次运行进行差异比对,以便在新子域名出现时捕获它们——请参阅[持续监控](#continuous-monitoring)。
### 运行单个阶段
```
# Phase 1 — 根域发现
python3 scripts/domain_discovery.py \
--org "Acme Corp" \
--acquisitions "SubsidiaryA,SubsidiaryB,SubsidiaryC" \
--abbreviations "acme,acmex" \
--output /tmp/confirmed_domains.txt
# Phase 1.5 — 所有权验证
python3 scripts/validate_ownership.py \
--input /tmp/confirmed_domains.txt \
--trusted acme.com \
--slugs "acme,acmex" \
--output /tmp/owned_domains.txt \
--rejected /tmp/rejected_domains.txt
# Phase 2 — 被动枚举
python3 scripts/passive_enum.py \
--domains "acme.com,acquired-co.com" \
--output /tmp/passive.txt \
--keys ~/.config/subdomain-recon/api_keys.yaml
# Phase 3 — 高级技术(一次一个域名)
python3 scripts/advanced_techniques.py \
--domain acme.com --org "Acme Corp" \
--known-subs /tmp/passive.txt \
--output /tmp/advanced.txt
# Phase 6 — 存活探测
python3 scripts/probe_live.py \
--input /tmp/all.txt --output /tmp/live.txt --threads 300 --timeout 5
# Phase 7 — 报告
python3 scripts/write_report.py \
--org "Acme Corp" \
--domain-map "acme.com:Primary,acquired-co.com:Acquisition 2023" \
--subdomain-files /tmp/all.txt \
--rejected-file /tmp/rejected_domains.txt \
--output ~/Desktop/Acme_domains.txt
```
## 选项参考
| 标志 | 是否必需 | 描述 |
|------|----------|-------------|
| `--org` | 是 | 组织名称 |
| `--acquisitions` | 否 | 逗号分隔的子公司/品牌名称 |
| `--abbreviations` | 否 | 逗号分隔的待展开短标签 |
| `--output` | 是 | 确认的根域名的输出文件 |
| 标志 | 是否必需 | 描述 |
|------|----------|-------------|
| `--input` | 是 | 确认的根域名文件 |
| `--trusted` | 否 | 逗号分隔的、被视为绝对拥有的根域名 |
| `--slugs` | 否 | 用于所有权信号的品牌标签(默认为受信任根域名的标签) |
| `--output` | 是 | 拥有 + 不确定的根域名 |
| `--rejected` | 否 | 停放/待售拒绝域名的存放文件(记录,而非删除) |
| `--timeout` | 否 | 每个请求的超时时间(默认 10 秒) |
| 标志 | 是否必需 | 描述 |
|------|----------|-------------|
| `--domains` | 是 | 逗号分隔的域名 |
| `--output` | 是 | 输出文件 |
| `--keys` | 否 | `api_keys.yaml` 的路径 |
| 标志 | 是否必需 | 描述 |
|------|----------|-------------|
| `--domain` | 是 | 目标域名 |
| `--org` | 否 | 组织名称 |
| `--output` | 是 | 输出文件 |
| `--probe-ips` | 否 | 用于 TLS 关联的已知 IP |
| `--known-subs` | 否 | 用于 `subwiz` 机器学习的已知子域名文件 |
`probe_live.py`: `--input` `--output` `--threads` (300) `--timeout` (5)
`write_report.py`: `--org` `--domain-map` `--subdomain-files` `--rejected-file` `--output`
## 持续监控
```
# 监控 CT logs 并获取新子域名的通知
gungnir -d acme.com -o /tmp/ct_stream.txt &
# 查看自上次扫描以来的新内容
comm -13 <(sort /tmp/previous.txt) <(sort /tmp/all.txt) > /tmp/new_subdomains.txt
```
## 注意事项
- n0kovo 3M 字典在家庭网络下运行很慢。请使用 `--wildcard-batch 1000000` 运行它。
- JARM 会建立真实的 TLS 连接(每台主机 10 次握手),因此请注意速率限制。
- 自 2025 年年中以来,Postman 工作区抓取的返回结果比以前少,但仍然能找到一些东西。
- Merklemap 未涵盖 Sunlight CT(2025 年的拆分)。如果你需要这方面的数据,请添加 `certstream-server-rust`。
- 云 IP 接管信号(如 EC2)通常影响较小——值得标记,但不值得深究。
## 关于范围的通知
仅对你拥有的资产或已获得明确测试授权的资产运行此操作。在未经授权的情况下扫描系统在你所在的地区可能是非法的。
## 作者
Krutarth Shukla
domain_discovery.py
| 标志 | 是否必需 | 描述 |
|------|----------|-------------|
| `--org` | 是 | 组织名称 |
| `--acquisitions` | 否 | 逗号分隔的子公司/品牌名称 |
| `--abbreviations` | 否 | 逗号分隔的待展开短标签 |
| `--output` | 是 | 确认的根域名的输出文件 |
validate_ownership.py
| 标志 | 是否必需 | 描述 |
|------|----------|-------------|
| `--input` | 是 | 确认的根域名文件 |
| `--trusted` | 否 | 逗号分隔的、被视为绝对拥有的根域名 |
| `--slugs` | 否 | 用于所有权信号的品牌标签(默认为受信任根域名的标签) |
| `--output` | 是 | 拥有 + 不确定的根域名 |
| `--rejected` | 否 | 停放/待售拒绝域名的存放文件(记录,而非删除) |
| `--timeout` | 否 | 每个请求的超时时间(默认 10 秒) |
passive_enum.py
| 标志 | 是否必需 | 描述 |
|------|----------|-------------|
| `--domains` | 是 | 逗号分隔的域名 |
| `--output` | 是 | 输出文件 |
| `--keys` | 否 | `api_keys.yaml` 的路径 |
advanced_techniques.py
| 标志 | 是否必需 | 描述 |
|------|----------|-------------|
| `--domain` | 是 | 目标域名 |
| `--org` | 否 | 组织名称 |
| `--output` | 是 | 输出文件 |
| `--probe-ips` | 否 | 用于 TLS 关联的已知 IP |
| `--known-subs` | 否 | 用于 `subwiz` 机器学习的已知子域名文件 |
probe_live.py / write_report.py
`probe_live.py`: `--input` `--output` `--threads` (300) `--timeout` (5)
`write_report.py`: `--org` `--domain-map` `--subdomain-files` `--rejected-file` `--output`
标签:GitHub, 子域名枚举, 安全侦察, 实时处理, 应用安全, 系统安全, 资产测绘, 运行时操纵, 逆向工具