sazzad1148/sazzadul-APRSR-Framework
GitHub: sazzad1148/sazzadul-APRSR-Framework
一款高性能的被动与递归子域名侦察框架,通过多数据源并行采集、DNS 验证、云资产指纹识别和置信度引擎,为安全评估提供全面的攻击面发现能力。
Stars: 0 | Forks: 0
# sazzad007 -- 被动 + 主动子域名侦察
通过 **19 个生产就绪的数据源**进行被动子域名发现,智能多解析器
通配符过滤,**多源递归枚举**(crt.sh SAN 链式查询 +
Wayback + GitHub + 可选的主动 JS/CSP 抓取),深度排列(级别
2-8),包含 **TTL / 使用的解析器 / 响应时间 /
DNSSEC** 的 DNS 验证,按 **ASN / 组织 / 云服务商** 分组的反向 DNS,云
资产指纹识别(AWS, Azure, GCP, Cloudflare, Fastly, Akamai, Vercel,
Netlify, Heroku, GitHub Pages, ...),一个**可配置、可解释的置信度
引擎**,一个**数据源健康摘要**,一个可查询的 **SQLite 情报
数据库**,针对上一次运行的**差异对比模式**,以及整洁的 `txt/ + json/ +
reports/` 输出布局 —— 包含恢复支持、缓存、分阶段指标
和自动发现的插件架构。
请参阅 `ARCHITECTURE.md` 了解 pipeline 图表、完整的 JSON schema、插件 SDK
契约和置信度引擎公式。请参阅 `CONTRIBUTING.md` 了解开发
配置以及如何添加新数据源。请参阅 `CHANGELOG.md` 了解版本历史。
请参阅 `examples/` 获取示例输出(所有格式)、示例配置文件
(JSON/YAML/TOML)以及示例批量域名列表。
### 快速开始(安装)
```
git clone https://github.com/sazzad1148/sazzadul-APRSR-Framework.git
cd sazzadul-APRSR-Framework # the folder containing run.py
pip install -r requirements.txt --break-system-packages
# (或者,以 pip 可安装的形式:pip install -e . —— 这也会为你提供
# `passive-enum` 命令,请参阅下文的第 2/17 节)
cp .env.example .env # optional -- fill in any free API keys you have
nano .env
python3 run.py -d example.com --profile balanced
```
完整详情(配置文件、密钥、CLI 工具数据源、故障排除)位于
下面的第 1 节。
### v3.1 中的新功能(本轮)
| 功能 | 状态 |
|---|---|
| 模块化插件系统 | 完成 |
| 自动插件发现(放入一个 `.py` 文件即生效) | 完成 —— 本身已实现,已通过专门的测试验证 |
| 数据源健康摘要(每个数据源的 ok/skipped/error + 主机数 + 重复项) | 完成 |
| 置信度引擎(累加、可解释、可通过 `--config-file` 配置) | 完成 |
| 数据源归因 | 完成 |
| 丰富的 JSON schema(`confidence_breakdown`、发现路径、ASN、云、丰富信息) | 完成 |
| TXT / CSV / HTML / Markdown 报告 | 完成 |
| SQLite 情报数据库(`intel.sqlite3`:运行历史、主机历史、搜索、跨运行重复项) | 完成 |
| 差异模式(`--diff ` 或 `--diff auto`) | 完成 |
| 恢复(按阶段检查点 + 优雅的 Ctrl+C 提示信息) | 完成,阶段中恢复不在本次范围内(参见 ARCHITECTURE.md) |
| 新增/修改模块上的类型提示 | 完成;全代码库严格的 `mypy` 仍在进行中,目前还不是强制的 CI 门禁 |
| `black` / `ruff` / `mypy` / pre-commit | 已配置(`pyproject.toml`, `.pre-commit-config.yaml`) |
| 测试 | 跨 5 个文件的 25 个测试,涵盖标准化、配置、置信度引擎、数据源健康、插件发现、情报数据库、差异模式、导出器;未声明覆盖率百分比,因为在此沙箱中未运行 `coverage.py`(无网络安装) |
| CI/CD (lint -> 测试矩阵 3.11/3.12 -> 构建) | 完成 |
| pip 打包(`pyproject.toml`, `passive-enum` 控制台脚本) | 完成 |
| Async 引擎 | 未完成 —— 当前并发基于线程池(`ThreadPoolExecutor`),这对于 I/O 密集型的 HTTP/DNS 工作已足够;真正的异步重写是更大的架构变更,留待未来进行 |
| 带有图表/时间线的 HTML 仪表板 | 部分完成 —— `report.html` 是一个可交互、可排序/可过滤的表格,带有摘要统计卡片,而不是图表式仪表板 |
| 专属的 "Mr. Cool" 启动横幅(logo 艺术字 + 作者:Sazzadul / 版本 / 引擎 / 模式 / 状态面板) | 完成 |
| `--minimal` 输出模式(仅保留 `txt/final_hosts.txt` + `reports/report.json`,删除其他所有内容) | 完成 |
| 每次运行自动刷新输出(第二次运行绝不与第一次运行的残留混合;`--resume` 是唯一的退出选项) | 完成 |
| 批量模式:`-dL/--domain-list example.txt` 可在一个命令中扫描 2 个或以上的域名,每个域名生成自己的输出子文件夹 | 完成 |
### 每个数据源的原始/标准化/拒绝/重复项明细
以前,数据源的日志行只显示最终计数(例如
`assetfinder: 58 normalized hosts`)—— 如果一个数据源返回了实际数据,但
全部在标准化/范围检查中被拒绝,这与该数据源
合法地一无所获是无法区分的。首先验证了
`normalize_hostname()` 本身没有错误(针对匹配实际真实域名的 subfinder 输出的 18 个
真实主机名模式进行了测试 —— 全部正确标准化),因此这并没有掩盖解析错误 ——
但*可见性*差距是真实存在的,现在已修复:
```
[+] subfinder: 428 normalized hosts (raw=558, rejected=6, dup=124)
```
如果一个数据源确实返回了全部被拒绝的数据,现在将标记为
`WARNING`,而不是静默的 `INFO` 行,因此它不会混入正常的“一无所获”结果中:
```
[!] some-source: got 558 raw line(s) but 0 normalized -- likely a real
parsing/scope issue, not "found nothing". Run with --debug for detail.
```
`raw`、`rejected` 和 `duplicate_in_source` 也存在于 `reports/report.json` 中
每个主机的 `provider_health` 条目中,并显示在控制台的
数据源健康摘要中。新增了 2 个回归测试,涵盖了正常的
明细和异常标记行为。
### 第二个性能修复:被动数据源收集也是顺序执行的
递归阶段的修复(见下文)并不是唯一的顺序瓶颈。
**阶段 1** —— 查询所有被动数据源(此修复完成时有 21 个,
现在移除了 C99/CertDB 后是 19 个)—— 以完全相同的方式运行:
一次一个数据源,在一个普通的 `for` 循环中,每个阻塞调用(包括
像 crt.sh 这样缓慢/受限的免费 API,以及像 `findomain`/`sublist3r` 这样生成子进程的 CLI 工具)在下一个开始之前完全串行。针对
真实的数据源延迟配置,仅仅这一项在 DNS 验证、递归或排列开始之前
就需要花费几分钟 —— 几乎可以肯定
是导致数小时总运行时间的最大元凶。
采用与递归相同的方式修复:一个有界的并行工作线程池
(`source_threads` —— 快速/均衡/彻底模式下分别为 8/12/19,可通过
`--source-threads` 覆盖)。使用真实延迟配置(大多数
数据源 3-8 秒,几个慢的达到 20 秒)进行的模拟从 **148 秒的串行
变为 22 秒的并行 —— 仅此阶段就加速了 6.7 倍**;再加上
递归修复,这才是真正弥补“1-3 小时”差距的关键,而不是
添加更多的数据源或功能。新增了 3 个回归测试。
### 第三个性能修复:“仍然卡住数小时”的根本原因
并行化上述两个阶段是必要的,但**并不充分** ——
一些实际运行之后仍然会停滞数小时。真正的 bug 是:
两个阶段都使用了 `with concurrent.futures.ThreadPoolExecutor(...) as ex:`。
退出该代码块会调用 `ex.shutdown(wait=True)`,这会阻塞直到
**每一个**提交的任务完成,无论有多少任务已经完成。
一个异常缓慢的响应 —— 例如 crt.sh 查询一个与数千个无关名称共享
通配符证书的主机,这是一种真实的常见情况 ——
可能会劫持整个一轮,即使该轮中的其他 100 多个调用在几秒钟内并行完成。
工作是真正并行的;*退出路径*会悄无声息地重新串行化到
最慢的落后者身上。
通过为每个阶段设置硬性的挂钟时间上限来修复:
```
python3 run.py -d example.com --recursion-round-timeout 120 # default: 60/180/400s by profile
python3 run.py -d example.com --source-stage-timeout 120 # default: 60/180/400s by profile
```
两个阶段现在都通过 `as_completed(futures, timeout=)` 等待
,而不是无限期阻塞;超时时,保留已完成的内容,
按名称记录落后者,并且 `ex.shutdown(wait=False,
cancel_futures=True)` 立即返回,而不是等待
已经运行的线程(Python 无法强制杀死一个线程 —— 它们
在后台完成,其结果被直接丢弃)。现在一轮运行
永远不会超过其配置的上限时间,
无论单个最慢的响应有多慢。由一个专门的回归测试覆盖,如果
这再次退化为阻塞,测试将会失败。
### 可维护性/DX 优化阶段的补充内容
- **模块拆分**:将 `dns_utils.py`(445 行)拆分为 `dns_utils.py`
(通配符 + 核心验证/PTR)和 `enrichment.py`(ASN 查询、云
指纹识别、完整 DNS 记录收集)。`pipeline.py` 的递归
扩展方法已移出至 `recursion_expanders.py`,作为普通的、
可独立测试的函数。参见 `ARCHITECTURE.md` 的新“模块
布局”表。(`pipeline.py` 仍然是最大的文件,约 630 行 ——
那是 11 阶段的编排器本身;进一步按阶段拆分
是可能的,但本轮未进行。)
- **`summary.json`**:一个精简的摘要(计数、数据源健康状态、
重复项、ASN/云分组、置信度权重),与完整的
`report.json` 并存,适用于只需要数字的脚本/仪表板。
- **YAML/TOML 配置文件**:`--config-file` 现在接受 `.yaml`/`.yml`
(需要 `pip install pyyaml`)和 `.toml`(标准库,无需额外安装),
除了 `.json` 之外。
- **真正的文件日志记录**:`output//logs/run_.log` 现在
被真正创建了(之前有文档说明但从未连接到代码) ——
无论控制台详细程度如何,始终捕获完整的 DEBUG 级别
详情。新增了 `--debug`(显示数据源失败的完整回溯,包括控制台和
日志中)和 `--quiet`(仅控制台显示 WARNING 级别,
完整详情仍会写入日志文件)标志。
- **进度显示**:DNS 验证、反向 DNS、云发现和
DNS 记录收集现在显示实时进度(如果安装了 `tqdm` 进度条,否则显示周期性
日志行 —— 无论哪种方式都有速率 + 预计剩余时间)。
每个 pipeline 阶段还会记录一个 `[stage N/11] ` 标记。
### 最新全代码审查阶段的修复
- **让静默失败变得可见(“subfinder: 0 hosts 但手动运行正常”
的 bug):** 每个数据源 —— 基于 CLI 工具的(`subfinder`, `findomain`,
`assetfinder`, `sublist3r`)和基于 HTTP 的(`crt.sh` 及其他 13 个)
—— 以前都会吞掉真实的错误(超时、非零退出码、连接
被拒绝、非 2xx HTTP 响应、解析崩溃),将其变成裸露的 `except
Exception: return []`。这使得“工具实际上失败”完全无法
与“它运行良好并合法地找到了零个
子域名”区分开来 —— 这正是当 `subfinder` 通过
pipeline 报告 0,而手动运行相同的 `subfinder -d ...` 命令却发现了 558 个时发生的情况。已修复:真实结果为零的干净运行(exit 0 /
HTTP 2xx,输出为空)仍然正确地返回一个空列表 —— 这是一个
真实的答案。现在其他所有情况都会抛出异常(`CLISourceError` /
`HTTPSourceError`),`stage_passive_sources` 中现有的针对每个数据源的
try/except 已经捕获了这些异常,并将其作为正确的 `error` 条目报告(包含
真实原因 —— 退出码、stderr 片段、HTTP 状态、连接
失败)在数据源健康摘要中,而不是令人误解的 `✓ 0
hosts`。新增了 15 个测试,涵盖辅助函数和真实数据源集成。
- **严重性能 bug:** 递归枚举完全串行运行
—— 425 个主机 x 3 个数据源(一个真实的案例,`crypto.com`)意味着 1275+
次阻塞 HTTP 调用,一次一个地针对像 crt.sh 这样受限的 API,
这可能会在单轮中卡住一个小时或更长时间。已修复,使用了一个
有界的并行工作线程池(`recursion_threads`,每个配置文件
新增的可调参数)以及一个可选的每轮前沿上限
(`max_recursion_frontier_per_round`),适用于非常大的域名。请参阅
第 7 节了解修改前对比以及如何调整或禁用它们。新增了 3 个
回归测试,包括一个如果代码再次变为串行就会失败的测试。
从头到尾检查了整个代码库并修复了所有发现的问题:
- **Bug:** 同时使用 `--diff --minimal` 会静默删除
`reports/diff.json` / `reports/diff.md` —— `--minimal` 的清理只
保留了 `report.json`,因此 `--diff` 被要求生成的内容被
丢弃了。已修复:`cleanup_to_minimal()` 现在也会在存在时保留
`diff.json`/`diff.md`。新增了回归测试
(`test_cleanup_to_minimal_keeps_final_report_and_diff_output`)和一个
端到端复现,以确保这不会静默退化。
- **Bug:** 数据源健康格式化程序中的无效三元运算符
(`"✓" if hosts > 0 else "✓"` —— 两个分支相同,显然是
遗留错误)。简化为只保留一个对勾。
- **清理:** 移除了 `cli.py` 中未使用的 `import sys`。
- **强化:** 将排列主机映射回其父级(用于
`discovery_path`)时使用的递归深度防护现在完全匹配
`wordgen.generate_deep_permutations` 的边界检查(`depth < 0
or depth >= max_depth`,而不仅仅是上限) —— 这是防御性的,因为
实际上到达该代码的每个基础主机都已经在范围内。
- 全代码审查还检查了:API 密钥是否在任何地方被记录/打印
(没有 —— 仅作为不透明的字典值传递)、每个模块中未使用的导入
(启发式 + 手动检查 —— 干净)、裸露的
`except:` 子句(无 —— 每个捕获都是 `except Exception:`,因此
`KeyboardInterrupt`/`SystemExit` 仍然正确传播)。
### 安装(pip,可选)
```
pip install -e . # editable install from this repo, or `pip install .`
passive-enum -d example.com --profile balanced
```
`python3 run.py -d example.com` 仍将像以前一样完全正常工作 —— pip
控制台脚本是一个额外的入口点,而不是替代品。
**范围:** 仅对你拥有或明确
授权测试的域名运行此工具。它只查询公共/被动数据源并执行
标准 DNS 解析 —— 无漏洞利用,无凭据攻击。可选的
`--active-recursion` 标志会抓取发现的主机上的公共页面
(以挖掘 JS/CSP 获取更多主机名) —— 仍然没有漏洞利用,但它是
向目标自身服务器发出的直接 HTTP 请求,因此它是可选的且默认关闭。
**项目范围(刻意为之):** 这是一个*子域名侦察*工具 ——
寻找最大数量的有效子域名并丰富其信息(数据源、DNS
记录、ASN、云、置信度)。它有意不进行端口
扫描、漏洞扫描(nuclei)、屏幕截图、JS 密钥扫描、
目录暴力破解或漏洞利用 —— 那些是不同工具的
工作,强行加上它们会把一个快速、专注的侦察工具变成一个缓慢、
臃肿的工具。如果你需要这些功能,请将此工具的 `txt/final_hosts.txt`
通过管道传递给 `httpx`、`nuclei`、`gowitness` 等。
## 1. 安装
```
# 从项目根目录(包含 run.py 的文件夹)
pip install -r requirements.txt --break-system-packages
# 可选的外部 CLI 工具(自动检测,如果缺失则跳过):
# subfinder https://github.com/projectdiscovery/subfinder
# assetfinder https://github.com/tomnomnom/assetfinder
# findomain https://github.com/findomain/findomain
# sublist3r https://github.com/aboul3la/Sublist3r
```
检查你的系统上有哪些可用工具:
```
python3 run.py --list-plugins
name confidence available
-----------------------------------------------
alienvault_otx Medium yes
anubisdb Medium yes
assetfinder Medium no (missing key/binary)
bufferover Medium yes
censys High no (missing key/binary)
certspotter High yes
chaos High no (missing key/binary)
crt.sh High yes
findomain Medium no (missing key/binary)
fullhunt Medium no (missing key/binary)
github Medium no (missing key/binary)
hackertarget Medium yes
rapiddns Medium yes
subfinder Medium no (missing key/binary)
sublist3r Medium no (missing key/binary)
threatminer Medium yes
urlscan Medium yes
virustotal Medium no (missing key/binary)
wayback Medium yes
```
开箱即用的免密钥数据源(9 个):`crt.sh`, `bufferover`, `alienvault_otx`,
`rapiddns`, `wayback`, `urlscan`(数量少), `certspotter`(速率限制
低), `hackertarget`(免费层级限制严格), `anubisdb`,
`threatminer`。
CLI 工具数据源(单独安装,在 `PATH` 中自动检测):`subfinder`,
`assetfinder`, `findomain`, `sublist3r`。
可选 API 密钥数据源(5 个) —— 全部免费注册,无需付款。对于
其中每一个,行为都是相同的:**存在密钥 -> 运行该数据源;缺少密钥 -> 自动跳过,并在数据源健康摘要中给出明确原因,
pipeline 的其余部分正常继续。** 没有例外,没有崩溃,其他任何东西都不受影响 ——
由 `test_passive_sources_skipped_when_unavailable` 和
`stage_passive_sources` 中的“已跳过”状态路径(`Pipeline.provider_health`)确认。
| 数据源 | 免费注册地址 | CLI 标志 |
|---|---|---|
| GitHub | github.com(个人访问令牌) | `--github-token` |
| Censys | search.censys.io(社区层级) | `--censys-id` + `--censys-secret`(均需提供) |
| VirusTotal | virustotal.com | `--virustotal-key` |
| FullHunt | fullhunt.io | `--fullhunt-key` |
| Chaos | chaos.projectdiscovery.io(免费,基于资格) | `--chaos-key` |
`urlscan.io` 和 `CertSpotter` 也接受可选的密钥
(`--urlscan-key` / `--certspotter-key`)以提高其速率限制,但
它们支持免密钥使用(如上所列),而不仅仅是可选使用。
**已从此项目中移除:** C99(曾是一个**付费** API —— 不应该
放在免费/ freemium 数据源旁边)和 CertDB(曾是一个没有真实
接口的存根 —— 没有单一的标准化公共“CertDB”服务,因此它
从未真正起作用)。计算它们中的任何一个都会使数据源计数
不准确;19 是真正针对真实
数据运行的数据源数量。如果你有一个想要接入的真实付费 API 密钥(C99 或其他
任何内容),第 11 节中的插件模式使其成为一个即插即用的文件,无需更改
核心代码。
刻意未包含(仅限付费,无可用免费层级):Shodan,
BinaryEdge。刻意未包含的数据源及其原因(已停止维护、
违反服务条款的抓取,或已通过其他方式覆盖的独立工具)与
上一个 README 部分相比未做更改,并且仍然适用。
### 存储 API 密钥
```
cp .env.example .env
# 然后编辑 .env 并填入你拥有的任何 key
```
`.env` 已包含在 `.gitignore` 中。如果一个密钥在多个
地方设置,优先级为:`--flag-on-cli` > shell 环境变量 > `.env` 文件。
## 2. 基本用法
```
python3 run.py -d example.com --profile balanced
```
### 自动刷新输出(默认行为)
每次运行都会为该域名重置状态 —— 除非你传递
`--resume`,否则该域名的输出目录会在 pipeline 启动前自动清除。对同一个域名进行第二次运行永远不会与
第一次运行的遗留文件混合;你不需要记住使用 `--fresh`(为了
向后兼容保留为一个空操作标志,但只要
你不进行恢复,现在清除就是默认行为)。
```
python3 run.py -d example.com --profile fast # run 1 -> output/...
python3 run.py -d example.com --profile fast # run 2 -> output/ auto-wiped first, clean result
python3 run.py -d example.com --profile fast --resume # only this one preserves the prior output
```
### 一次扫描多个域名(`-dL` / `--domain-list`)
```
# example.txt —— 每行一个域名,空行和 #注释 会被跳过
example.com
another-domain.com
# this-one-is-commented-out.com
third-domain.com
```
```
python3 run.py -dL example.txt --profile balanced
```
针对文件中的每个域名运行完整的 pipeline(重复项
不区分大小写地进行去重)。你也可以将 `-d` 与 `-dL` 结合使用 —— 两者
都会被扫描。每个域名都有其**独立的隔离输出子文件夹**:
```
output/
example.com/
txt/ json/ reports/ ...
another-domain.com/
txt/ json/ reports/ ...
third-domain.com/
txt/ json/ reports/ ...
```
(如果只有单个 `-d` 而没有 `-dL`,输出将完全像以前一样保持在 `output/...` 的扁平结构 ——
按域名划分的子文件夹仅在批量
模式下启用,因此现有的单域名脚本/工作流不受影响。)自动刷新
适用于每个域名,并且 Ctrl+C 会停止整个批次(已完成的
域名保留其结果;被中断的域名可以使用
`--resume -d ` 单独恢复)。批量摘要
(每个域名的 OK/INTERRUPTED/FAILED)将在最后打印。
**只想要最终结果,不想要其他内容?** 使用 `--minimal` —— 运行
后,除了两个最终报告文件之外的所有内容都会被自动删除:
```
python3 run.py -d example.com --profile fast --minimal
```
只保留:
```
output/
txt/final_hosts.txt # hostnames only, one per line
reports/report.json # full structured report (IPs, sources, confidence, cloud, ...)
```
其他一切 —— 每个阶段的 `json/`,其他的 `txt/*.txt` 文件,
`report.csv`/`.html`/`.md`, `cache.sqlite3`, `intel.sqlite3`,
`metadata.json`, `checkpoints/`, `logs/` —— 都会被移除。(如果不使用
`--minimal`,默认行为保持不变:仅 `checkpoints/` 会
被自动删除,其他一切保留 —— 见第 3 节和第 18 节。)
## 3. 输出结构
```
output/
txt/
passive_hosts.txt # stage 01 -- raw candidates, pre-DNS-validation
validated_hosts.txt # stage 03 -- initial DNS-validated hosts
recursive_hosts.txt # stage 04 -- hosts after recursive expansion
permutation_hosts.txt # stage 06 -- hosts found via permutation
final_hosts.txt # FINAL validated hostnames -- ONLY hostnames, one per line
json/
01_passive_sources.json
02_wildcard_detection.json
03_initial_dns_validation.json
04_recursive_enumeration.json
05_word_extraction.json
06_permutation_validation.json
07_reverse_dns.json # PTR + ASN, per IP
08_cloud_discovery.json
09_dns_records.json
10_final_filter_validation.json # final hosts + TTL/resolver/RTT/DNSSEC
reports/
report.json # full structured report: hostname -> IPs + everything else
summary.json # lean summary only: counts, provider health, duplicates,
# ASN/cloud groupings, confidence weights -- no per-host array
report.csv
report.html # dark-themed, sortable, filterable
report.md
metadata.json # first_seen/last_seen/sources/discovery_path per host
intel.sqlite3 # run history (see section 15) -- separate from cache.sqlite3
cache.sqlite3 # API + DNS cache (persists across runs)
logs/
run_.log # full DEBUG-level log for this run, regardless of
# console verbosity (--quiet still gets a complete file)
checkpoints/ # per-stage checkpoint files, auto-deleted after a
# successful run (see "Auto-cleanup" below)
```
**`txt/final_hosts.txt` 仅包含已验证的主机名** —— 没有 IP,没有
元数据 —— 恰好是“仅主机名”列表。你要求追踪的其他每一个字段
(数据源、provider_count、置信度、记录、云、通配符、
recursive_depth、discovery_path、标签、元数据)都存在于 `reports/report.json` 中,
针对每个主机,例如:
```
{
"host": "login.example.com",
"validated": true,
"sources": ["crt.sh", "recursive-wayback"],
"provider_count": 2,
"confidence": 0.98,
"confidence_label": "High",
"records": {
"ips": ["1.2.3.4"],
"ttl": 300,
"resolver_used": "8.8.8.8",
"response_time_ms": 14.2,
"dnssec": false,
"dns_records": {"A": {"values": ["1.2.3.4"], "ttl": 300}, "MX": {...}}
},
"cloud": {"provider": "AWS", "service": "CloudFront", "evidence": "d123.cloudfront.net"},
"wildcard": false,
"recursive_depth": 1,
"discovery_path": ["example.com"],
"tags": ["recursive"],
"metadata": {
"first_seen": 1753350000.0,
"last_seen": 1753350100.0,
"validation_time": 1753350100.0,
"ptr": {"1.2.3.4": "ec2-1-2-3-4.compute-1.amazonaws.com"},
"asn": {"1.2.3.4": {"asn": "16509", "prefix": "1.2.3.0/24", "country": "US",
"registry": "arin", "org": "AMAZON-02"}}
}
}
```
### 自动清理
在运行成功完成后,`output/checkpoints/` 将被自动删除
(一旦每个阶段完成,就没有什么可恢复的了) ——
`txt/`, `json/`, `reports/`, `metadata.json`, `cache.sqlite3` 和 `logs/` 将
被保留;这些才是真正的交付物。如果你想检查它们或稍后恢复分析某次运行,请传递 `--keep-checkpoints`。
## 4. 配置文件
配置文件 | 线程数 | 最大深度 | 每级排列数 | 缓存 TTL | 检查的 DNS 解析器
---|---|---|---|---|---
fast | 30 | 2 | 500 | 1h | 系统默认
balanced | 60 | 5 | 3,000 | 6h | 8.8.8.8, 1.1.1.1
thorough | 100 | 8 | 20,000 | 24h | 8.8.8.8, 1.1.1.1, 9.9.9.9
```
python3 run.py -d example.com --profile thorough --max-depth 8
python3 run.py -d example.com --profile balanced --threads 120 --perm-limit 8000
python3 run.py -d example.com --profile balanced --config-file myconfig.json
```
`--config-file` 接受三种格式,通过扩展名选择:
```
python3 run.py -d example.com --config-file myconfig.json # stdlib, always available
python3 run.py -d example.com --config-file myconfig.toml # stdlib (tomllib, Python 3.11+), no extra install
python3 run.py -d example.com --config-file myconfig.yaml # needs: pip install pyyaml --break-system-packages
```
相同的键,任何格式 —— 例如 `myconfig.yaml`:
```
threads: 80
max_depth: 6
cache_ttl_seconds: 3600
confidence_weights:
cloud: 20
permutation_penalty: -25
```
`--config-file` 根据扩展名选择解析器 —— `.json`(始终
可用),`.toml`(标准库 `tomllib`,Python 3.11+,无需额外安装),或
`.yaml`/`.yml`(需要 `pip install pyyaml`,如果缺少则会引发明确的错误
提示你安装,而不是令人困惑的回溯):
```
// myconfig.json
{ "threads": 80, "max_depth": 6, "cache_ttl_seconds": 3600 }
```
```
# myconfig.yaml
threads: 80
max_depth: 6
cache_ttl_seconds: 3600
confidence_weights:
cloud: 20
permutation_penalty: -25
```
```
# myconfig.toml
threads = 80
max_depth = 6
cache_ttl_seconds = 3600
```
这三种方式都是等效的 —— 选择你已经在
工具链中使用的格式即可。
## 5. 更智能的通配符检测
简单的单次探测通配符检查存在两个问题: 一个
解析器的陈旧缓存或瞬态应答可能会产生误报,
子域名可以有其**独立于**根域的**自身**的通配符条目
(`*.dev.example.com` 捕获所有内容,即使 `*.example.com` 并
没有)。
此版本修复了这两个问题:
- **多探测、多解析器多数投票。** 在活动配置文件中的
每个解析器(`balanced`/`thorough` 模式下的 Google/Cloudflare/Quad9)上探测 4 个随机的、基本上
保证未注册的标签。一个 IP 只有在它出现在
**大多数**探测/解析器组合中时,才会被计为通配符签名的一部分 —— 单个偶然的异常应答不能
单独产生(或隐藏)通配符信号。这就是修复
“通配符检测总是返回 0”问题的方法:旧版本使用
单次静态检查,太容易遗漏瞬态通配符
响应,或者过于轻率地将一个 stray IP 称为通配符。
- **按级别检测**,精神上与以前相同:在
排列阶段在已验证的主机下进行更深层的扩展之前,它会
单独探测 `*.` 以获取其自身的通配符签名。发现
具有通配符的主机将保留在结果中(报告中为 `"wildcard": true`),但不会用作进一步排列的基础。
## 6. DNS 验证信息丰富
`reports/report.json` / `json/10_final_filter_validation.json` 中
每个最终主机的记录现在除了包含解析出的 IP 外,还带有:
- 解析记录的 **TTL**
- **使用的解析器**(实际应答的配置解析器中的哪一个)
- 以毫秒为单位的 **响应时间**
- **DNSSEC** —— 尽力检查 DNSSEC 感知重新查询上的 Authenticated Data (AD) 标志
## 7. 递归枚举 —— 多源扩展
旧版本仅针对每个新发现的主机重新查询 crt.sh。此
版本在每一轮中通过**每个主机多个独立的数据源**进行扩展:
```
developer.example.com
|
+--> crt.sh (certificate SAN chaining)
+--> Wayback Machine (archived URLs under the host)
+--> GitHub code search (only if --github-token is set)
+--> [opt-in, --active-recursion] JS file links + CSP header hostnames
```
每个新的主机都会在 `discovery_path` 中记录**它是
在哪个父主机下被发现的** —— 这样你就可以准确追踪
`vpn.internal.developer.example.com` 是如何被找到的(例如
`["example.com", "developer.example.com", "internal.developer.example.com"]`)。
### 性能:并行化,带有前沿上限(修复长达数小时的卡顿)
此阶段的早期版本完全**顺序**扩展 `(host, source)` 对 —— 一次一个阻塞 HTTP 调用,每次都带有自己的
重试/退避等待。针对一个拥有数百个初始验证
主机的域名(例如拥有 425 个的 `crypto.com`),这意味着 1000+ 次连续调用
访问像 crt.sh 这样的受限免费 API,这确实可能会在单轮中
卡住一个小时甚至更长时间。
通过两种方式修复:
1. **并行扩展。** `(host, source)` 对现在通过有界
线程池(`recursion_threads` —— fast/balanced/thorough 模式下为 10/20/30)
运行,而不是一次一个。特意设置得低于常规的
`--threads` 计数,这样大型域名就不会过度猛烈地冲击免费层级的 API
以至于被封 IP,同时仍然重叠每次调用的
重试/退避等待,而不是逐个主机地支付时间。
2. **前沿上限。** `max_recursion_frontier_per_round`(fast/balanced/thorough 模式下为 100/250/600)限制了在超大域名上的单轮中要扩展多少主机 ——
其余的保持验证状态并留在你的
结果中,只是在本次运行中不会被递归。每次运行覆盖或
禁用:
```
python3 run.py -d crypto.com --profile balanced --recursion-threads 30
python3 run.py -d crypto.com --profile balanced --max-recursion-frontier 0 # disable the cap
```
或通过 `--config-file`:
```
{ "recursion_threads": 25, "max_recursion_frontier_per_round": null }
```
## 8. 反向 DNS —— 按 ASN / 服务商 / 云分组
`reports/report.json["reverse_dns_groups"]`(以及 `report.md` /
`summary` 输出的 “Reverse DNS” 部分)将每个最终主机按以下方式分组:
- **ASN** —— 通过 Team Cymru 的基于 DNS 的 whois 服务进行免费、免密钥查询
(`origin.asn.cymru.com` / `asn.cymru.com`;无 API 密钥,无受限
付费依赖)
- **云服务商** —— 见下文
每个主机的 PTR + ASN 详细信息也存在于完整报告中每个主机的 `metadata.ptr` /
`metadata.asn` 下。
## 9. 云/CDN 发现
每个验证主机的 CNAME 链(最多 8 跳)都会被追踪并与
已知的服务商指纹进行匹配:
CloudFront/S3/ELB -> **AWS** | azurefd/azurewebsites/blob.core.windows.net -> **Azure**
| appspot/run.app/cloudfunctions -> **GCP** | cloudflare.net -> **Cloudflare**
| fastly.net -> **Fastly** | akamai*/edgekey -> **Akamai** |
vercel.app -> **Vercel** | netlify.app -> **Netlify** | heroku* -> **Heroku**
| github.io -> **GitHub Pages**
匹配结果按主机显示为 `"cloud": {"provider": "AWS", "service": "CloudFront", "evidence": "...", "cname_chain": [...]}`
并在 `reverse_dns_groups.by_cloud_provider` 中汇总。(它只能
报告 CNAME 链实际揭示的内容 —— 直接指向
裸 IP 且没有 CNAME 的主机将无法通过这种方式进行归因;这是
被动 CNAME 指纹识别的固有局限性,而不是 bug。)
## 10. 恢复与检查点
```
python3 run.py -d example.com --profile thorough --resume
python3 run.py -d example.com --profile thorough --fresh # force a clean re-run
```
pipeline 阶段,按顺序:`01_passive_sources`, `02_wildcard_detection`,
`03_initial_dns_validation`, `04_recursive_enumeration`, `05_word_extraction`,
`06_permutation_validation`, `07_reverse_dns`(+ ASN), `08_cloud_discovery`,
`09_dns_records`, `10_enrichment`, `11_final_filter_validation`。
## 11. 添加新数据源(插件架构)
无需更改核心 pipeline 代码。在
`subdomain_recon/sources/` 中创建一个新文件:
```
# subdomain_recon/sources/my_source.py
from .base import Source, SourceContext
class MySource(Source):
name = "my_source"
confidence = "Medium"
def fetch(self, domain, ctx: SourceContext):
return [f"host1.{domain}", f"host2.{domain}"]
```
它会被自动拾取 —— 使用 `python3 run.py --list-plugins` 验证。
## 12. 运行测试套件
```
pip install pytest dnspython --break-system-packages
pytest tests/ -v
```
涵盖:主机名标准化、范围/深度检查、配置文件加载
和覆盖、置信度评分,以及完整的 `txt/ + json/ + reports/`
导出布局(包括“自动清理仅移除检查点”
行为)。
## 13. 数据源健康摘要
每次运行都以每个数据源的明细结束 —— 打印到控制台并
嵌入在 `reports/report.md` 中:
```
Provider Summary
alienvault_otx ✓ 114 hosts
certspotter ✓ 98 hosts
github ✗ Missing key (github)
chaos ✗ Missing key (chaos)
subfinder ✓ 31 hosts
Unique Hosts : 421
Duplicates : 198
Errors : 2
```
`✗` 区分了**已跳过**(不可用 —— 缺少密钥或 CLI 二进制文件,
已明确命名)和**错误**(真正的失败 —— 非 2xx HTTP 状态码、
连接失败、非零 CLI 退出码或超时 —— 并在消息中附带实际
原因;参见上面的“让静默失败变得可见”更新日志
条目)。一个确实发现零主机的干净运行仍然正确地
显示为 `ok, 0 hosts` —— 这种区别正是修复的核心点。
### 日志标志
```
python3 run.py -d example.com --quiet # console: warnings/errors + final summary only
python3 run.py -d example.com --verbose # console: DEBUG level
python3 run.py -d example.com --debug # console: DEBUG + full tracebacks for source failures
```
无论控制台详细程度如何,完整的 DEBUG 级别日志始终会
写入 `output//logs/run_.log` —— `--quiet` 只
影响屏幕上滚动的信息,而不影响保留供以后调试的信息。
## 14. 置信度引擎
完整的公式和 JSON `confidence_breakdown` 结构在
`ARCHITECTURE.md` 中。快速版本 —— 累加分数,可通过
`--config-file` 配置:
```
{ "confidence_weights": { "cloud": 20, "permutation_penalty": -25 } }
```
默认值:2+ 个数据源 +20,DNS 有效 +20,递归 +15,云 +10,GitHub
+15,crt.sh +10,仅排列 -15。分数限制在 `[0, 100]`;高
## 15. SQLite 情报数据库 + 差异模式
每次运行都存储在 `output/intel.sqlite3` 中(与 API/DNS
`cache.sqlite3` 分开) —— 运行历史、跨运行的每个主机历史、跨运行
重复检测和主机名搜索都是针对它进行的普通 SQL 查询
(见 `subdomain_recon/intel_db.py`)。如果你不需要,使用 `--no-intel-db` 跳过此步骤。
```
# 与特定的已保存报告进行 diff
python3 run.py -d example.com --diff /path/to/old/reports/report.json
# 与 intel.sqlite3 中该域名最近一次的先前运行进行 diff
python3 run.py -d example.com --diff auto
```
写入 `reports/diff.json` + `reports/diff.md` 并记录 NEW/REMOVED
摘要。
## 16. 恢复 + Ctrl+C
```
python3 run.py -d example.com --profile thorough --resume # the ONLY way to skip the auto-wipe
python3 run.py -d example.com --profile thorough # every other invocation auto-wipes first (see section 2)
```
`--fresh` 仍然会被解析(这样旧脚本就不会中断),但现在是一个空操作 ——
只要未传递 `--resume`,清除就是默认行为。
在运行过程中按下 Ctrl+C 不再只是静默退出 —— 它会准确记录
哪些阶段已完成/检查点,并打印精确的 `--resume`
命令以继续。恢复粒度是按阶段的(参见 `ARCHITECTURE.md`
了解为什么未实现阶段中的恢复)。在批量模式(`-dL`)下,Ctrl+C
会在当前域名之后停止整个批次;已完成的域名
保留其结果,被中断的域名可以单独恢复。
## 17. 开发者工具
```
pip install -r requirements-dev.txt # pytest, black, ruff, mypy, pre-commit
pre-commit install # run lint+format+tests before every commit
black .
ruff check .
mypy .
pytest tests/ -v
```
CI(`.github/workflows/tests.yml`):lint(`ruff` + `black --check`)-> 测试
矩阵(Python 3.11, 3.12)-> 包构建,每一个都作为下一个的关卡。
## 18. 故障排除
- **`pip install ... --break-system-packages` 提示 “no such option”** ——
你使用的是较旧版本的 pip;要么升级 pip,要么去掉该标志并使用
虚拟环境。
- **未安装 dnspython** —— 基本的 A 记录解析回退到
Python 内置的 `socket` 模块,但 AAAA/CNAME/MX/TXT/NS/PTR, TTL,
DNSSEC 和 ASN 查询都需要它:`pip install dnspython --break-system-packages`。
- **`subfinder` / `assetfinder` / `findomain` / `sublist3r` 显示 “no (missing
key/binary)”** —— 这些是可选的外部 CLI 工具;安装它们并
确保它们在 `PATH` 中,或者忽略它们。
- **某个数据源在这里显示的主机少于(或为 0)手动单独运行它** ——
首先检查数据源健康摘要和 `reports/report.json` 的
`provider_health` 部分:每当这确实是
实际发生的情况时,它现在会显示带有真实
原因的 `error`(超时、退出码 + stderr、HTTP 状态),而不是令人误解的 `ok, 0 hosts`(参见上面的修复
更新日志)。如果它确实显示没有错误的 `ok, 0 hosts`,那么
两次运行可能只是遇到了不同的上游条件(速率限制、
瞬态网络问题,或独立工具自身的单独数据源
配置/缓存) —— 重新运行并比较,或者在
此工具旁边单独运行该数据源以直接比较。
- **一次运行多个域名比预期慢/不稳定** ——
如果你同时从同一台机器启动多个单独的 `python3 run.py -d ...` 进程
(每个域名一个),它们都会
同时竞争相同的免费/受限 API 和相同的出站
连接池,这可能会全面触发速率限制或
超时。请改用 `-dL/--domain-list`(第 2 节)
—— 它在单个进程内一次扫描一个域名,因此你
可以获得相同的结果,而不会造成自找的并发负载。
- **空的 `txt/final_hosts.txt`** —— 检查 `output/logs/run_*.log` 和
`reports/report.json["metrics"]` 内部的 `errors` 列表 —— 最常见的原因是没有
数据源可达(无网络 / 无密钥 / 无 CLI 工具),或者
该域名具有激进的通配符 DNS 过滤掉了每个候选者(检查
`report.json["wildcard_ips"]`)。
- **缓存似乎过时** —— 通过 `--config-file` 降低 `cache_ttl_seconds`,或
删除 `output/cache.sqlite3`。
## LICENSE
MIT —— 见 `LICENSE`。此工具仅供授权的安全测试
和研究使用。仅对你拥有或具有
明确书面测试许可的域名和系统使用它。作者对
滥用不承担任何责任。
标签:GitHub, Python, Python安全, 子域名枚举, 实时处理, 无后门, 无服务器架构, 系统安全, 资产测绘, 逆向工具