coolhandle01/oamx
GitHub: coolhandle01/oamx
一个零依赖的只读工具,用于从 OWASP Amass 的 SQLite 资产数据库中提取结构化数据并管道传递给下游侦察流水线工具,解决 v5 不再输出文本文件导致的流水线断裂问题。
Stars: 1 | Forks: 0
# oamx
**读取 OWASP Amass 资产数据库,并将其管道化传递给其他所有工具。**
零依赖。只读。兼容 Amass v4 和 v5。
## 问题所在
这是侦察(recon)领域的单行命令。在过去五年编写的每一篇 Amass 教程、博客文章和速查表中,都会出现它的某种版本:
```
amass enum -passive -d example.com -o subs.txt
httpx -l subs.txt -silent | nuclei -severity high,critical
```
自从 Amass v5(2025 年 8 月)起,它会生成一个**空**的 `subs.txt`。
v5 将结果移到了资产数据库中,并停止填充文本输出。
扫描在运行。数据就在那里。但是 `-o` 什么也没产生,httpx 什么也没探测到,nucleus 什么也没找到,而且流水线 **exits 0**。它没有报错——
它成功地什么也没做,悄无声息地,按计划执行着,可能持续几个月。
Amass 是一个没有消费者机制的生产者。文档中记载的解决方法是
`amass subs -names -d example.com`,它只给你一个扁平的主机名列表,
除此之外什么都没有——没有 IP、没有网段、没有端口、没有溯源、没有作用域控制、也没有“自昨天以来发生了什么变化”。其他人都在从 GitHub issues 中复制粘贴 `sqlite3 json_extract` 的咒语。
`oamx` 就是那个消费者。
```
oamx names -d example.com --resolved-only | httpx -silent | nuclei -severity high,critical
```
## 安装
```
pipx install oamx # or: pip install oamx
```
Python 3.10+。没有运行时依赖——这是有意为之的。一个因为传递依赖解析失败而崩溃的侦察流水线,比没有工具还要糟糕。
这里有两个入口点,它们是可以互换的:
```
oamx names -d example.com # the console script
python -m oamx names -d example.com # when PATH is not set up for you
```
第二个入口点能够在容器、`pip install --target` 布局或任何 scripts 目录不在 `PATH` 中的环境下正常运行。这两个入口在每次发布时都会与构建好的 wheel 包进行核对。
## 快速开始
```
# 我的数据库中实际有什么?
oamx doctor
# 限定在单个 target 上且实际解析的名称
oamx names -d example.com --resolved-only
# Amass 发现的所有监听服务,已准备好供 httpx 或 nuclei 使用
oamx targets -d example.com --urls
# 过去一天内真正出现的内容
oamx names -d example.com --new --since 24h
# 完整的全貌(包含 provenance),用于存储或供 agent 进行推理
oamx json -d example.com > assets.jsonl
```
当扫描“什么也没发现”时,`oamx doctor` 是应该首先运行的命令:
```
database /home/you/.config/amass/amass.sqlite
layout v5 (entities/edges)
provenance yes
assets 22
FQDN 8
Service 3
IPAddress 3
Identifier 2
URL 1
TLSCertificate 1
SomeFutureAssetType 1
Netblock 1
ContactRecord 1
AutonomousSystem 1
```
这是来自测试夹具的真实输出,而不是演示,`database` 行下方的所有内容都经过了与实际运行结果的断言校验——参见 `tests/test_readme.py`。`SomeFutureAssetType` 是故意放在夹具中的:即使遇到当前版本从未见过的资产类型,它仍然会被统计,并且依然会通过 `oamx json` 输出带有可用值的记录。
## 命令
| 命令 | 输出 |
| --- | --- |
| `names` | 完全限定域名 |
| `ips` | IP 地址(`--ipv4` / `--ipv6`) |
| `cidrs` | CIDR 表示法表示的网段 |
| `asns` | 自治系统编号(ASN) |
| `urls` | 发现的 URL |
| `certs` | TLS 证书 |
| `services` | 响应的网络服务 |
| `orgs` | 组织机构 |
| `emails` | 电子邮件地址 |
| `targets` | `host:port` 对,或使用 `--urls` 输出 `scheme://host:port` |
| `dns` | `nameRRTYPEtarget` 三元组 |
| `json` | 每个匹配的资产输出为 JSONL 格式,包含溯源信息 |
| `graph` | 每个匹配的关系输出为 JSONL 格式 |
| `stats` | 按资产类型统计数量 |
| `doctor` | 数据库是什么以及里面有什么 |
## 过滤器
每个命令都采用相同的参数集:
| 标志 | 效果 |
| --- | --- |
| `--db PATH` | 数据库路径(默认:自动发现各平台常用的位置) |
| `-d, --domain` | 将作用域限定在某个根域名;支持重复使用和逗号分隔 |
| `--scope-depth N` | 用于限定非名称资产作用域的图遍历跳数(默认为 2,`0` 表示禁用) |
| `--since DUR` | 在 `24h`、`7d`、`2w` ... 时间内最后被发现的记录 |
| `--new` | 与 `--since` 配合使用,匹配*首次*被发现——真正的新资产,而非被重新确认的资产 |
| `--resolved-only` | 仅限具有 DNS 记录的名称 |
| `--source NAME` | 仅限由此 Amass 插件断言的资产 |
| `--exclude-source NAME` | 丢弃*唯一*来源是这些的资产 |
| `--min-confidence N` | 丢弃低于此来源置信度(0–100)的资产 |
| `--json` | 带有溯源信息的 JSONL 格式,而非纯值输出 |
| `--count` | 仅输出数量 |
| `--fail-empty` | 如果没有匹配项则 exit 1,用于 CI |
## 为什么它这样工作
有四个值得了解的设计决策,因为它们会改变你获取到的结果。
**它通过自省 schema 来工作,而不是绑定到特定版本。**
Amass 至少重命名过三次它的存储方式——v3 的 graph 文件、v4 的 `assets`/`relations`、v5 的 `entities`/`edges`,外加 v5 小版本更新中的列名变动。`oamx` 会读取它找到的任何表和列,并将它们映射到一个逻辑模型中。即使是编写此代码时还不存在的资产类型,依然会输出一个可用的值,而不是抛出异常。适配器吸收了这些变动,这样你的流水线就不用管了。
**作用域遵循图结构,但名称通过后缀匹配。**
`-d example.com` 以主机名后缀为种子,然后向外遍历 `--scope-depth` 跳,以获取附加在这些名称上的 IP、网段和证书。但是,通过图邻近关系到达的*主机名*永远不会被拉入作用域。如果没有这条规则,一个指向共享 CDN 的 CNAME 就会把该 CDN 的其他客户拖入你的目标列表中。使用 `--scope-depth 1` 可以只保留直接解析的地址,或者设为 `0` 表示仅限名称。
**化简后相同的记录会被合并。**
来自证书 SAN 的 `API.Example.COM.` 和来自 DNS 解析结果的 `api.example.com` 是数据库中的两行记录,但对应的是同一个主机。`oamx` 会合并它们:溯源信息会被合并取并集,采用较高的置信度,并且发现时间窗口会扩大以覆盖所有促成合并的记录。合并在时间和来源过滤器*之前*进行,因此一个主机永远不会仅根据其某一次被发现的情况来评估。
**重新发现不是新发现。**
`--new --since 24h` 报告的是在该时间窗口内首次发现的主机。一个你已经知道了一个月的主机,即使刚刚被第二个数据源重新确认,也不算新发现,也不会发出警报。正是这类误报,导致人们渐渐无视他们的监控系统。
## 输出 schema
`--json` 每行输出一个对象,带有版本标识,这样即使底层的 Amass 发生变化,下游代码依然可以依赖于它:
```
{
"schema": "oamx/1",
"kind": "asset",
"id": 3,
"type": "FQDN",
"value": "api.example.com",
"first_seen": "2026-06-24 12:00:00.000000000+00:00",
"last_seen": "2026-07-24 10:00:00.000000000+00:00",
"sources": [{"name": "crtsh", "confidence": 95}, {"name": "DNS-IP", "confidence": 100}],
"attrs": {"name": "api.example.com"}
}
```
`graph` 输出包含 `from`、`to`、`label` 以及解码后的 DNS 记录类型的 `"kind": "edge"` 记录(`rr_type: 1` 会变为 `rr_name: "A"`,因为没人想去 grep 搜索 `5`)。
## 作为库使用
```
from oamx.integrations import query, values
hosts = values("names", domains=["example.com"], resolved_only=True)
records = query("all", domains=["example.com"], since="7d")
```
`query` 返回与 `oamx json` 输出相同的记录,以 dict 的形式。`values` 仅返回值。两者在发生错误时都会抛出 `OamxError`,并附带易读的错误信息。
它只负责读取:数据库以 `mode=ro` 模式打开,因此这里的任何操作都不会启动扫描或发送网络流量。如果你打算把它交给一个 agent,这是一个很有用的特性——但是 oamx 故意没有提供任何 agent 框架适配器。请在你自己的代码环境中构建它,那里有你自己的工具 schema 和错误处理约定。
该包附带了 `py.typed`,因此类型注解会保留给任何导入它的代码。
## 已知限制
在此明确说明,因为一个夸大其词的侦察工具比没用更糟糕。
- **仅支持 SQLite。** Amass 也支持 PostgreSQL 和 Neo4j。读取器是在接口背后编写的,因此添加这些支持很容易,但提供未经测试的数据库驱动程序违背了其初衷。
- **字段名称针对 `FQDN`、`IPAddress`、`Service` 和 `Identifier` 进行了验证**,这是基于 Open Asset Model 文档进行的;对于 `Identifier`,还基于 `owasp-amass/open-asset-model` 本身中的结构体进行了验证。其余约 17 种资产类型使用尽力而为的键列表,并提供通用的后备方案。如果其中某个类型为你提取了错误的字段,这在 `model.py` 中只是一个单行修复,并且非常欢迎你提交 issue——`Identifier` 刚好就是这种情况,它之前报告的是 `unique_id` 去重键,而实际值存储在 `id` 中。
- **整个图会被加载到内存中**,以确保作用域控制和合并处理的正确性。对于针对特定目标的数据库来说这没问题;如果你的数据库有数百万个实体,这将需要采用流式处理路径。
- **不支持 Amass v3 的 graph 文件。** 仅支持 SQLite 资产数据库。
## 兼容性
| Amass | 布局 | 状态 |
| --- | --- | --- |
| v5.x | `entities` / `edges` | 已测试 |
| v4.x | `assets` / `relations` | 已测试 |
| v3.x | graph 文件 | 不支持 |
两种布局都由基于文档化 schema 构建的测试夹具覆盖,而不是基于猜测。
## 开发
```
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest
```
大约 1 秒内运行 122 个测试,以分支覆盖率而非代码行覆盖率作为门槛——因为这个代码库主要由分支逻辑组成,而代码行覆盖率会把只测试了一半的 `if` 语句也视为完全覆盖。`ruff`、`mypy --strict` 和 `pylint` 在同一个 `dev` extra 中运行,因此 CI 运行的内容和你本地运行的内容不会发生偏差;有关执行顺序请参见 `CONTRIBUTING.md`。
开发依赖项不属于零依赖承诺的一部分,该承诺关乎的是*已安装的* oamx 引入了什么。CI 通过单独安装该包并断言没有带入任何第三方依赖来单独证明了这一点。
如果你添加了新行为,请先故意破坏它,并检查是否会出现测试失败。一个从未见过失败的测试,并不能证明它测试了任何东西。
## 上游
该工具填补的空白,可以说应该是一个 Amass 本身的问题,而不是第三方的问题。如果该项目希望拥有一个能输出结构化、受作用域限制且带有溯源信息输出的 `amass export` 命令,那么 `oamx/model.py` 中的 schema 是一个合理的起点,届时这个工具也就没有必要存在了。那将会是一个好结果。
采用 Apache-2.0 许可,与 Amass 生态系统保持一致。
标签:Blue Team, GitHub, Python, TShark, 攻击面映射, 无后门, 逆向工具