
GoBalance onion load balancer のセキュリティパッチと概念実証。blindedSign によるマスターキー復元と偽造ディスクリプタの受理を対象とし、回帰テストを含む。
gitlab.com/n0tr1v/gobalance 向けアドバイザリパッケージ - master ブランチ、コミット bb1b0f3("fix crash")。
ステータス: CRITICAL - 2つの独立した完全乗っ取り経路。 上流の patch1 ブランチはどちらも修正していない。
本パッケージには、完全なセキュリティパッチ、両方の攻撃経路に対するエンドツーエンドの概念実証、および修正が有効であることを証明する回帰テストが含まれている。これは、2つのフォーラムに影響を与えた最近の onion ドメイン乗っ取りインシデントに関連して作成された完全な脆弱性分析レポート("Laporan Analisis Keamanan GoBalance")に付随するものである。ステップバイステップのビルド & テストガイドは USAGE.md にある。
| # | 脆弱性 | 深刻度 | 影響 | ステータス |
|---|---|---|---|---|
| 1 | マスター識別鍵が blindedSign を通じて漏洩(定数 nonce プレフィックス) | CRITICAL | 単一の公開ディスクリプタから onion アイデンティティを完全復元 | 修正済み |
| 2 | RegisterDescriptor が偽造インスタンスディスクリプタを受け入れる(署名 / バインディング検証なし) | CRITICAL | 任意の GoBalance フロントエンドのトラフィックハイジャック | 修正済み |
| 3 | pkg/brand における決定論的で時刻シードの RNG 経路 | HIGH(footgun) | これを使用するあらゆるものに対して予測可能な鍵素材 | 削除済み |
| 4 | イントロダクションポイントのシャッフルが math/rand を使用 | LOW | プロトコル隣接コードにおける弱い乱数 | 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>)
は公開定数となった。結果: 公開されたディスクリプタを 1 つでも読める者は誰でも nonce r を再計算し、ブラインドされたスカラー
s' = (S − r) · H(R‖PK‖M)⁻¹ mod L を解き、公開乗数でアンブラインドできる -
onion サービスのマスター識別鍵を復元できる。サーバアクセスも MitM もブルートフォースも不要である。これはサイレントなドメイン乗っ取りプリミティブであり、最近のフォーラムハイジャックで観測されたメカニズムと一致する。
修正: ディスパッチャは完全な拡張鍵
(gobpk.PrivateKey.PrivKey()) を転送するようになった。BlindedSignWithTorKey は正確に 64 バイトでない鍵に対してパニックする。blindedSignP2 は多層防御として ESK 長を独立に強制する。gobpk.New はロード時に切り詰められた Tor 鍵を拒否する。
NewReceivedDescriptor() はネットワークから渡されたものを解析し、信頼していた。
サブクレデンシャルはディスクリプタ自体の中に含まれるブラインド鍵から導出されるため、攻撃者は自身の鍵を使って他人の onion アドレスに対する暗号学的に自己整合的なディスクリプタを鋳造できた。フロントエンドはその後、被害者のアイデンティティの下で攻撃者のイントロダクションポイントを再公開してしまう - 鍵復元を一切必要としない完全なトラフィックハイジャックである。
修正: 新しい
VerifyHiddenServiceDescriptorV3() における 3 層検証: (1) ブラインド鍵の下での証明書署名、(2) 証明された署名鍵の下でのディスクリプタ署名、および
(3) バインディング - ブラインド鍵は、フロントエンドがコンセンサス (GetBlindingParam + 時間期間) とインスタンスアドレスから独立に計算する値と等しくなければならない。RegisterDescriptor はフェイルクローズドである: ライブコンセンサスがなければ、盲目的に信頼するのではなく登録を拒否する。
gobalance-patch/
├── README.md ← このファイル(英語)
├── USAGE.md ← ステップバイステップのビルド & テストガイド(英語)
├── README_ID.md ← ringkasan patch(インドネシア語)
├── gobalance-security.patch ← master@bb1b0f3 に対する unified 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 ./... OK。go test ./... → 8 パッケージ ok、失敗 0、回帰なし(既存の上流テストを含む)。