针对 gitlab.com/n0tr1v/gobalance 的公告包 - master 分支,提交 bb1b0f3(“修复崩溃”)。
状态:严重 - 两条独立的完全接管路径。 上游 patch1 分支均未修复其中任何一条。
本包包含完整的安全补丁、针对两条攻击路径的端到端概念验证,以及证明修复有效的回归测试。它随附完整的漏洞分析报告(“Laporan Analisis Keamanan GoBalance”),该报告与近期影响两个论坛的 onion 域名接管事件相关。分步构建与测试指南见 USAGE.md。
| # | 漏洞 | 严重性 | 影响 | 状态 |
|---|---|---|---|---|
| 1 | 主身份密钥通过 blindedSign 泄露(常量 nonce 前缀) | 严重 | 从单个公开描述符即可完全恢复 onion 身份 | 已修复 |
| 2 | RegisterDescriptor 接受伪造的实例描述符(无签名/绑定验证) | 严重 | 任意 GoBalance 前端的流量劫持 | 已修复 |
| 3 | pkg/brand 中确定性的、基于时间种子的 RNG 路径 | 高(隐患) | 任何使用它的地方都会产生可预测的密钥材料 | 已移除 |
| 4 | 引入点洗牌使用 math/rand | 低 | 协议相关代码中的弱随机性 | 已替换为 crypto/rand |
pkg/stem/descriptor/hidden_service.go 中的调度器 blindedSign() 将
identityKey.Seed()——即原始 32 字节标量 a——传入了
BlindedSignWithTorKey()。Tor 格式密钥是扩展密钥:64 字节
(a || h),其中 h 是派生每次签名 nonce 前缀的 PRF 密钥。由于缺少 h,nonce 派生输入为空,于是
kPrime = SHA512("Derive temporary signing key hash input" || <empty>)
变成了一个公开常量。后果:任何能读取一个已发布描述符的人都可以重新计算 nonce r,解出盲化标量
s' = (S − r) · H(R‖PK‖M)⁻¹ mod L,并用公开乘数去盲——从而恢复该 onion 服务的主身份密钥。无需服务器访问、无需中间人攻击、无需暴力破解。这是一个静默的域名接管原语,并且与近期论坛劫持事件中观察到的机制一致。
修复: 调度器现在转发完整的扩展密钥
(gobpk.PrivateKey.PrivKey());BlindedSignWithTorKey 对任何不是恰好 64 字节的密钥都会 panic;blindedSignP2 独立强制检查 ESK 长度,作为纵深防御;gobpk.New 在加载时拒绝被截断的 Tor 密钥。
NewReceivedDescriptor() 会解析并信任网络交给它的任何内容。
由于子凭证是从描述符自身内部携带的盲化密钥派生而来,攻击者可以使用自己的密钥为别人的 onion 地址铸造出密码学上自洽的描述符。随后,前端会在受害者身份下重新发布攻击者的引入点——这是一种完全无需密钥恢复的完整流量劫持。
修复: 在新的
VerifyHiddenServiceDescriptorV3() 中实施三层验证:(1) 盲化密钥下的证书签名,(2) 经认证签名密钥下的描述符签名,以及
(3) 绑定——盲化密钥必须等于前端根据共识(GetBlindingParam + 时间周期)和实例地址独立计算出的值。RegisterDescriptor 采用故障关闭策略:没有实时共识时,它拒绝注册,而不是盲目信任。
gobalance-patch/
├── README.md ← 本文件(英文)
├── USAGE.md ← 分步构建与测试指南(英文)
├── README_ID.md ← ringkasan patch(印度尼西亚语)
├── gobalance-security.patch ← 针对 master@bb1b0f3 的统一 diff(7 个文件,+360/−94)
├── gobalance-patched/ ← 完整的预打补丁源码树(可直接替换)
│ ├── go.mod / go.sum / main.go
│ ├── pkg/… ← 已打补丁的库,包括回归测试
│ ├── poc/ ← 端到端攻击演示 + 易受攻击代码快照
│ ├── cmd/gbdemo/ ← 独立恢复演示 CLI(+ E2E 测试)- USAGE #13
│ └── tools/ ← pem2tor.py(PEM→Tor 密钥转换器)、get_desc.py(描述符获取)
└── gobalance-v1/ ← 社区分支 “GoBalance Enhanced v1.0”(Dread),已打包
为分发版本以供测试 - 仍然易受攻击 - USAGE #14
# Option A - patch a fresh upstream checkout
git clone https://gitlab.com/n0tr1v/gobalance && cd gobalance
git apply /path/to/gobalance-security.patch
go build ./... && go test ./...
# Option B - use the bundled pre-patched tree (fastest)
cd gobalance-patched
go build ./...
go test ./poc/ -v # attack demo: succeeds vs vulnerable snapshot, fails vs patch
go test ./... # full suite: 8 packages ok
完整演练及预期输出见 USAGE.md。
Test01_Vulnerable_MasterKeyRecoveredFromSingleDescriptor。Test02_Patched_TruncatedTorKeyRejected。Test03_Patched_TorPathSignaturesVerifyAsStdEd25519。Test04_Patched_AttackMathYieldsGarbage。TestForgedSelfConsistentDescriptorIsDetected。TestNewReceivedDescriptor_AcceptsHonestDescriptor、
_RejectsTamperedSignature、_RejectsWrongIdentityBinding。PoC 中的所有密钥均在测试时本地生成。未针对任何真实服务。
TestBlindedSign 仍然
通过)。patch1 分支并未修复漏洞 #1 或 #2。 本补丁
必须应用在 master 之上(或使用打包的源码树)。go.mod 声明 go 1.18)。go vet ./... 无问题;go build ./... 正常。go test ./... → 8 个包 ok,0 失败,无回归(包括
预先存在的上游测试)。