实现原理:指纹库匹配
对应代码:
pkg/fpmatch.go· 入口MatchFingerprints/matchHash
采集到的 JARM/JA3/cert/SPKI 哈希怎么匹配到已知服务?本页讲库结构、精确匹配算法与自定义扩展。
FingerprintMatch 结构体
type FingerprintMatch struct {
Type string // "jarm" | "ja3" | "cert_sha256" | "spki"
Hash string
Label string // "Cloudflare" / "Cobalt Strike Default" / ...
Category string // cdn/cloud/c2/vpn/web/mail/database/other
Confidence float64 // 0.0-1.0
Source string // builtin / custom / file:<path>
}内置库分布
fingerprintDB(fpmatch.go L40-122)共 47 条 builtin,source 全为 "builtin":
| Category | 条数 | 代表条目 |
|---|---|---|
| cdn | 7 | Cloudflare, AWS CloudFront, Azure Front Door, Akamai, Fastly, Sucuri |
| cloud | 10 | AWS ALB/ELB, GCP, Azure, DigitalOcean, Linode, Vultr, Hetzner, OVHcloud |
| c2 | 7 | Cobalt Strike, Metasploit, Sliver, Havoc, Brute Ratel, Mythic, PoshC2 |
| vpn | 5 | OpenVPN, WireGuard, NordVPN, ExpressVPN, Tor(obfs4) |
| web | 9 | Nginx, Apache, Caddy, LiteSpeed, Tomcat, Traefik, HAProxy, IIS, Envoy |
| 5 | Postfix, Dovecot, Exim, Sendmail, Exchange | |
| database | 6 | MySQL, PostgreSQL, Redis, MSSQL, MongoDB, Elasticsearch |
| other | 10 | DigiCert Root, Let's Encrypt ISRG X1, Sectigo, GlobalSign, Entrust;Chrome/Firefox/Safari/Edge/Python/Go/curl 等 JA3 |
按 Type 分布:JARM 35 条、JA3 10 条、SPKI 5 条。
匹配算法:精确等值
matchHash(L175-193)——精确等值匹配,非模糊:
normalized := strings.ToLower(strings.ReplaceAll(hash, ":", ""))
// 对 db entry 同样归一化后比较精确等值,非模糊匹配
matchHash 是精确等值匹配,不是相似度匹配。所以指纹库的价值在覆盖广度——这也是为什么支持加载自定义指纹:你私有资产的特征录进库才能命中。
MatchFingerprints 全流程
四路指纹(JARM/JA3/cert_sha256/SPKI)顺序采集,每路成功才追加 matches;cert 取 Fingerprints["sha256"](→cert_sha256)与 ["public_key_sha256"](→spki)。
SPKI 计算
func ComputeCertSPKIHash(cert *x509.Certificate) string {
return sha256(cert.RawSubjectPublicKeyInfo) // SPKI 的 DER 编码做 SHA-256
}这正是 HPKP(HTTP Public Key Pinning) 的标准算法——对 SubjectPublicKeyInfo 的 DER 做 SHA-256。空 Raw 返回 ""。
SPKI 字段主要给自定义库用
内置库的 SPKI 哈希多为示例占位,真实生产匹配以 JARM/JA3 为主。SPKI 字段的真正价值在自定义库——把你内部资产的 SPKI 录入,做内部识别与证书绑定监控。
自定义扩展
LoadFingerprintDB(JSON 字节)/ LoadFingerprintDBFromFile 追加自定义条目:
去重 key(isDuplicateFingerprint L199-218):三项全等才算重复——Type + 归一化 hash + ToLower(Label)。
[
{
"type": "jarm",
"hash": "07d14d16d21d21d07c42d41d00041d24de...",
"label": "Internal App Gateway",
"category": "web",
"confidence": 0.95,
"source": "internal-asset-inventory"
}
]加载后立即对所有后续匹配生效。LoadFingerprintDBFromFile 返回新增计数。
统计:GetFingerprintDBStats
type FingerprintDBStats struct {
Total int
ByType map[string]int
ByCategory map[string]int
BySource map[string]int
BuiltinCount int
CustomCount int
}BySource 把 "file:..." 前缀归一为 "file";BuiltinCount 统计 src=="builtin",其余计入 CustomCount。对应 fp-db stats 命令。
输出
type FingerprintMatchResult struct {
Matches []FingerprintMatch
JARMHash string
JA3Hash string
CertHash string
SPKIHash string
}红队场景
JARM 命中 c2 ≠ 一定是 C2
同款合法软件默认配置会撞同一个 JARM。JARM 命中 c2 类别只是强信号,不是定罪证据——需结合置信度、上下文、其他证据(端口行为、JA3、证书特征)综合判断。误报会把合法运维人员当攻击者。详见 证书事件应急取证工作流。
下一步
- 指纹库手册 → 指纹库
- JARM 怎么采集 → JARM 指纹
- 取证编排 → 证书事件应急取证工作流