su6osec/graphite
GitHub: su6osec/graphite
Graphite 是一款集成了 39 个 OSINT 数据源,并通过主动验证与置信度评分机制实现零误报的快速子域名发现工具。
Stars: 0 | Forks: 0
# graphite
[](https://github.com/su6osec/graphite/actions/workflows/ci.yml)
[](https://goreportcard.com/report/github.com/su6osec/graphite)
[](LICENSE)
[](go.mod)
[](https://github.com/su6osec/graphite/releases)
```
██████╗ ██████╗ █████╗ ██████╗ ██╗ ██╗██╗████████╗███████╗
██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██║ ██║██║╚══██╔══╝██╔════╝
██║ ███╗██████╔╝███████║██████╔╝███████║██║ ██║ █████╗
██║ ██║██╔══██╗██╔══██║██╔═══╝ ██╔══██║██║ ██║ ██╔══╝
╚██████╔╝██║ ██║██║ ██║██║ ██║ ██║██║ ██║ ███████╗
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝
```
Graphite 聚合了 **25+ 个免费的被动 OSINT 来源**,对结果进行去重,
并以确定性 **置信度评分** 系统运行快速并发的 **DNS / TLS / HTTP** 验证
— 默认仅报告已验证的子域名。
## 快速开始
**1. 安装**(需要 Go 1.21+)
```
go install github.com/su6osec/graphite/cmd/graphite@latest
```
**2. 运行**
```
graphite -d example.com
```
就这样。一条命令,一个参数。
## 快速安装
| 方法 | 命令 |
|--------|---------|
| go install | `go install github.com/su6osec/graphite/cmd/graphite@latest` |
| 预编译二进制文件 | [发布页面](https://github.com/su6osec/graphite/releases) |
| 从源码构建 | `git clone … && make build` |
### 可选的 Python TUI
**通过 PyPI:**
```
pip install graphite-tui
```
**从源码:**
```
pip install -r tui/requirements.txt
```
## 单命令示例
```
graphite -d hackerone.com
```
```
graphite domain=hackerone.com workers=50 min-confidence=60
[*] Validating [========================================] 143/143
# Subdomain Score Confidence DNS TLS HTTP Status Sources
─── ──────────────────────────── ─────── ──────────── ───── ───── ────── ──────── ──────────────
1 api.hackerone.com 95 high ✓ ✓ ✓ 200 crtsh,urlscan
2 docs.hackerone.com 92 high ✓ ✓ ✓ 200 crtsh,wayback
3 hacktivity.hackerone.com 88 high ✓ ✓ ✓ 200 crtsh,anubis
4 gslink.hackerone.com 72 medium ✓ ✓ ✓ 301 hackertarget
...
→ 89 subdomain(s) discovered
```
## CLI 参考
| Flag | Short | Default | Description |
|------|-------|---------|-------------|
| `--domain` | `-d` | *(required)* | 目标域名 |
| `--output` | `-o` | `table` | 格式 (`table`\|`json`\|`csv`\|`txt`) **或** 文件路径 (例如 `subs.txt`, `~/recon/zoho.json`) |
| `--confidence` | `-c` | `60` | 最低置信度分数 (0–100) |
| `--timeout` | `-t` | `5m` | 全局超时 (例如 `3m`, `10m`) |
| `--workers` | `-w` | `50` | 并发验证工作线程数 |
| `--verbosity` | | `1` | 详细程度:`0`=静默,`1`=正常,`2`=调试 |
| `--no-ui` | | `false` | 禁用 Python TUI 自动启动 |
| `--ports` | | `false` | 启用端口检查 (80/443/8080/8443) |
| `--show-all` | | `false` | 包含低置信度 / 未验证的结果 |
| `--disable-source` | | | 按名称禁用来源 (可重复使用) |
| `--version` | `-v` | | 显示版本 + 更新状态 (绿色=最新,红色=过时) |
| `--update` | `-u` | | 更新 graphite 到最新版本 |
### 子命令
```
graphite sources
```
```
graphite tui -d example.com
```
```
graphite version
```
```
graphite update
```
### 使用示例
```
graphite -d example.com
```
```
graphite -d example.com -o json | jq '.[] | select(.score > 80)'
```
```
graphite -d example.com -o results.json
```
```
graphite -d example.com -o ~/bugbounty/zoho/subs.txt
```
```
graphite -d example.com --verbosity 2 -c 40
```
```
graphite -d example.com --verbosity 0 -o json -t 2m
```
```
graphite -d example.com --show-all -c 0
```
```
graphite -d example.com --ports
```
```
graphite -d example.com --disable-source dnsdumpster --disable-source sitedossier
```
```
graphite tui -d example.com
```
```
graphite -v
```
```
graphite update
```
## JSON 输出示例
```
[
{
"subdomain": "api.example.com",
"score": 95,
"confidence": "high",
"verified": true,
"passive_sources": ["crtsh", "hackertarget", "urlscan", "alienvault"],
"ips": ["93.184.216.34"],
"tls_valid": true,
"tls_subject": "api.example.com",
"http_status": 200,
"http_title": "API Gateway",
"http_server": "nginx/1.24.0",
"open_ports": [80, 443]
}
]
```
## 架构
```
graphite -d example.com
│
▼
┌─────────────────────────────────────────────────────────┐
│ Engine (internal/engine) │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Passive Collection (concurrent goroutines) │ │
│ │ │ │
│ │ crtsh certspotter hackertarget alienvault │ │
│ │ urlscan threatcrowd threatminer rapiddns │ │
│ │ anubis robtex dnsdumpster wayback │ │
│ │ commoncrawl virustotal github bufferover │ │
│ │ bevigil leakix omnisint chaos recondev │ │
│ │ netcraft securitytrails phonebook riddler │ │
│ │ shrewdeye dnsrepo fullhunt c99 … │ │
│ └──────────────────────┬──────────────────────────┘ │
│ │ deduplicated candidates │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ Wildcard / Sinkhole Baseline Probe │ │
│ │ (xn--random-probe. → detect wildcard) │ │
│ └──────────────────────┬─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ Validation Worker Pool (–w 50 default) │ │
│ │ │ │
│ │ DNS validator → A/AAAA/CNAME (retries x2) │ │
│ │ TLS validator → handshake + SAN check │ │
│ │ HTTP validator → GET/HEAD + title + hash │ │
│ │ Port validator → connect(80/443/8080/8443) │ │
│ └──────────────────────┬─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ Confidence Scoring + FP Suppression │ │
│ └──────────────────────┬─────────────────────────┘ │
│ │ │
│ ▼ │
│ Output: table | json | csv │
└─────────────────────────────────────────────────────────┘
│
▼ (optional, parallel)
┌─────────────────────────────────────────────────────────┐
│ Python Textual TUI (tui/) │
│ Reads JSON stream from Go core over stdout │
│ Live table, filter by score, open-in-browser, copy │
└─────────────────────────────────────────────────────────┘
```
## 被动来源
| 名称 | 描述 | 需要密钥 |
|------|-------------|:---:|
| `crtsh` | 通过 crt.sh 的 Certificate Transparency | 否 |
| `certspotter` | 通过 certspotter.com 的 CT 日志 | 否 |
| `hackertarget` | HackerTarget 免费主机搜索 | 否 |
| `alienvault` | AlienVault OTX 被动 DNS | 否 |
| `urlscan` | urlscan.io 公共扫描结果 | 否 |
| `threatcrowd` | ThreatCrowd 域名 API | 否 |
| `threatminer` | ThreatMiner 被动 DNS | 否 |
| `rapiddns` | RapidDNS.io HTML 抓取 | 否 |
| `anubis` | Anubis-DB (jldc.me) | 否 |
| `robtex` | Robtex NDJSON API | 否 |
| `dnsdumpster` | DNSDumpster 表单抓取 | 否 |
| `wayback` | Wayback Machine CDX API | 否 |
| `commoncrawl` | Common Crawl 最新索引 | 否 |
| `virustotal` | VirusTotal 公共端点 | 否 |
| `github` | GitHub 公共代码搜索 | 否 |
| `bufferover` | dns.bufferover.run 数据集 | 否 |
| `tlsbufferover` | tls.bufferover.run CT 数据 | 否 |
| `bevigil` | BeVigil 移动应用 OSINT | 否 |
| `leakix` | LeakIX 主机搜索 | 否 |
| `omnisint` | Project Sonar FDNS 数据 | 否 |
| `chaos` | ProjectDiscovery Chaos 数据集 | 否 |
| `recondev` | Recon.dev 免费API | 否 |
| `netcraft` | Netcraft 搜索 DNS | 否 |
| `securitytrails` | SecurityTrails 免费端点 | 否 |
| `phonebook` | Phonebook.cz / IntelX 搜索 | 否 |
| `riddler` | Riddler.io CN/SAN 搜索 | 否 |
| `shrewdeye` | ShrewdEye.app 数据库 | 否 |
| `dnsrepo` | dnsrepo.noc.org 被动 DNS | 否 |
| `sitedossier` | SiteDossier.com 页面 | 否 |
| `fullhunt` | FullHunt.io 攻击面 | 否 |
| `subdomaincenter` | subdomain.center API | 否 |
| `c99` | c99.nl 子域名查找器 | 否 |
| `synapsint` | Synapsint OSINT 聚合器 | 否 |
| `whoisxmlsubs` | WhoisXML API 子域名查找 | 否 |
| `digitorus` | Digitorus CT 搜索 | 否 |
| `googlect` | Google CT 透明度报告 | 否 |
| `archivesubs` | Archive.org 唯一域名 | 否 |
| `passivetotal` | RiskIQ PassiveTotal 社区 | 否 |
| `shodan` | Shodan 免费 DNS 数据集 | 否 |
运行时查看所有来源:`graphite sources`
## 验证流程
```
Candidate subdomain
│
▼
1. DNS A/AAAA/CNAME ──fail──▶ score += 0 (passive only)
│ success
▼
2. TLS Handshake ──fail──▶ score += 0 for TLS
│ success + SAN match
▼
3. HTTP GET/HEAD ──fail──▶ score += 0 for HTTP
│ success
▼
4. Response fingerprint ──▶ wildcard / sinkhole check
│
▼
5. Port connect (opt.) ──▶ score += 10 if any open
│
▼
Confidence score → filter → output
```
## 置信度评分算法
```
score = passive_score + dns_score + tls_score + http_score + port_score
passive_score = min(len(passive_sources) × 5, 30)
dns_score = 25 if DNS A/AAAA resolved
tls_score = 20 if TLS handshake succeeded AND cert SAN covers subdomain
http_score = 15 if HTTP 2xx response
= 8 if any HTTP response (3xx/4xx/5xx)
port_score = 10 if at least one port open (requires --ports)
max total = 30 + 25 + 20 + 15 + 10 = 100
```
| 等级 | 分数 | 含义 |
|------|-------|---------|
| **高** | 80–100 | DNS + TLS + HTTP 均通过,多来源 |
| **中** | 60–79 | DNS 解析 + 至少一个其他信号 |
| **低** | 40–59 | 仅 DNS 或仅 HTTP |
| **推测** | 0–39 | 仅被动来源,无主动验证 |
默认最低显示阈值:`--confidence 60`(中及以上)。
## 误报缓解
Graphite 采用多层次的误报抑制机制:
1. **泛解析 DNS 检测** — 在验证候选子域名之前,解析一个随机探测
(`xn--graphite-probe-zz9h42.`)。如果它返回 IP,
这些 IP 会被标记为泛解析 IP,任何解析到这些 IP 的候选子域名
都会被抑制。
2. **HTTP 泛解析指纹识别** — 如果启用了泛解析 DNS,会向随机主机发送 HTTP 探测。
任何响应体哈希与泛解析响应匹配的候选子域名都会被抑制。
3. **已知黑洞 IP** — 内置知名黑洞/占位符
IP(0.0.0.0, 127.0.0.1 等)列表,抑制匹配的候选子域名。
4. **CDN/停放页面检测** — 匹配 "domain for sale"、"account suspended"、"parking"
等模式的 HTTP 标题字符串会被抑制。
5. **TLS SAN 验证** — 只有当证书 SAN 明确覆盖子域名时才授予 TLS 分数
(正确处理通配符证书)。
6. `verified=true` 至少需要一个主动检查 — 仅被动来源
结果仍可通过 `--show-all` 出现,但绝不会标记为已验证。
## 速率限制与缓存
- 每个来源适配器声明一个 `RateLimit()` 时长;引擎会在
对同一来源的连续请求之间插入礼貌性延迟。
- 结果缓存在 `~/.cache/graphite/` 中,以 JSON 文件形式存储,
键为 `sha256(domain|source)`。默认 TTL:24 小时。
- 清除陈旧缓存:`rm -rf ~/.cache/graphite/`
## Python TUI
```
# 启动 TUI (除非指定 --no-ui 否则自动启动)
graphite tui -d example.com
```
```
┌─ graphite ──────────────────────────────────────────────────────────────────┐
│ Filter: [______________] total=143 verified=89 high=61│
├─────────────────────────────────────────────────────────────────────────────┤
│ # Subdomain Score DNS TLS HTTP Status Title │
│ ─── ───────────────────────────── ──── ──── ───── ─────── ──────────────── │
│ 1 api.example.com 95 ✓ 🔒 ✓ 200 API Gateway │
│ 2 docs.example.com 92 ✓ 🔒 ✓ 200 Documentation │
│ ▶ 3 mail.example.com 88 ✓ 🔒 ✓ 301 Redirecting... │
│ 4 staging.example.com 71 ✓ ~ 403 Forbidden │
├─────────────────────────────────────────────────────────────────────────────┤
│ api.example.com │
│ │
│ Score: 95 / 100 (high) │
│ Verified: yes │
│ TLS: valid │
│ HTTP status: 200 │
│ HTTP title: API Gateway │
│ Server: nginx/1.24.0 │
│ IPs: 93.184.216.34 │
│ Sources: crtsh, hackertarget, urlscan, alienvault │
└─────────────────────────────────────────────────────────────────────────────┘
q Quit o Open browser c Copy / Filter Esc Clear filter
```
**TUI 键盘快捷键:**
| 键 | 操作 |
|-----|--------|
| `q` | 退出 |
| `o` | 在浏览器中打开选定的子域名 |
| `c` | 复制子域名到剪贴板 |
| `/` | 聚焦过滤器输入 |
| `Esc` | 清除过滤器 |
**通信模型:**
TUI 将 `graphite -d -o json --no-ui` 作为子进程启动
并解析 JSON 数组输出。它也可以离线加载预保存的 JSON 文件:
```
from graphite_tui.app import GraphiteApp
app = GraphiteApp()
app.load_json_file("results.json")
app.run()
```
## 如何添加被动来源适配器
在 `internal/sources/` 中放入一个新文件:
```
// internal/sources/myapi.go
package sources
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
)
// Register at package init – no changes needed elsewhere.
func init() { Register(&MyAPI{}) }
type MyAPI struct{}
func (m *MyAPI) Name() string { return "myapi" }
func (m *MyAPI) Description() string { return "MyAPI.io free subdomain endpoint" }
func (m *MyAPI) RateLimit() time.Duration { return 1 * time.Second }
func (m *MyAPI) NeedsKey() bool { return false }
func (m *MyAPI) Enumerate(ctx context.Context, domain string) (<-chan Result, error) {
ch := make(chan Result, 256)
go func() {
defer close(ch)
url := fmt.Sprintf("https://api.myapi.io/subdomains/%s", domain)
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
resp, err := defaultClient(20 * time.Second).Do(req)
if err != nil {
return
}
defer resp.Body.Close()
var subs []string
if err := json.NewDecoder(resp.Body).Decode(&subs); err != nil {
return
}
for _, sub := range subs {
SendResult(ctx, ch, m.Name(), sub, domain)
}
}()
return ch, nil
}
```
就这样 — 引擎通过 `init()` 自动发现它。
通过以下命令验证:`graphite sources | grep myapi`
## 运行测试
**仅单元测试(不需要网络)**
```
make test-short
```
**所有测试包括网络(DNS/HTTP)**
```
make test
```
**使用竞态检测器 + 覆盖率**
```
make test-cover
```
测试位置:
- `tests/sources_test.go` — 来源注册表,规范化
- `tests/scoring_test.go` — 置信度评分公式
- `tests/validators_test.go` — DNS/HTTP/TLS/端口 + 缓存往返
- `tests/filter_test.go` — 泛解析/黑洞/CDN 抑制
- `tests/output_test.go` — JSON/CSV/table 格式化器正确性
## 构建与发布
**为当前平台构建**
```
make build
```
```
./graphite -d example.com
```
**交叉编译(所有平台)**
```
make build-all
```
**发布(需要 goreleaser + git tag)**
```
git tag v1.0.0
make release
```
CI/CD 在每次 push 和 pull request 时通过
`.github/workflows/ci.yml` 自动运行(lint → 测试矩阵 → 跨平台构建 →
打标签时 goreleaser)。
## 负责任地使用
- 在扫描您不拥有的域名之前,务必获得书面许可。
- 在运行主动探测之前,检查并遵守您组织的安全策略。
- Graphite 遵守速率限制;不要修改它们以冲击第三方 API。
- 不要使用 Graphite 枚举目标以进行未经授权的访问、DoS 或
任何非法目的。
- 发现的子域名可能暴露敏感的基础设施 — 负责任地处理结果,
未经授权请勿分享。
Graphite 绝不会外泄或上传发现的数据到任何第三方
服务。所有结果均保留在本地。
## 路线图
- [x] 39 个被动 OSINT 来源(零 API 密钥)
- [x] 主动 DNS / TLS / HTTP / 端口验证
- [x] 置信度评分 + 误报抑制
- [x] 输出到文件:JSON, CSV, 纯 TXT(每行一个子域名)
- [x] 自更新 (`graphite update`)
- [x] 版本检查,带有彩色过时/最新徽章
- [ ] 子域名接管检测
- [ ] 基于 ASN 的子域名关联
- [ ] MX / NS / TXT 记录收集
- [ ] DNSSEC 验证标志
- [ ] 流式 NDJSON 输出模式
- [ ] Docker 镜像
- [ ] 自定义验证器的插件系统
## 贡献
请参阅 [CONTRIBUTING.md](CONTRIBUTING.md) 了解指南、适配器骨架以及
拉取请求检查清单。
## 安全
要报告漏洞,**请勿**打开公共 GitHub issue。请打开
一个 [GitHub Security Advisory](https://github.com/su6osec/graphite/security/advisories/new)
或直接邮件联系维护者。
## 许可证
[MIT](LICENSE) © 2024 [su6osec](https://github.com/su6osec)
标签:DNS解析, ESC4, EVTX分析, Go语言, HTTP探针, OSINT, Snort++, TLS验证, 去重, 域名安全, 子域名枚举, 并发验证, 开源项目, 文档结构分析, 日志审计, 程序破解, 系统安全, 网络安全, 置信度评分, 被动扫描, 资产测绘, 逆向工具, 隐私保护, 零误报