
Sicherheitspatch und Proof-of-Concept für den GoBalance-Onion-Load-Balancer, der die Wiederherstellung des Master-Keys über blindedSign und die Annahme gefälschter Descriptors abdeckt, mit Regressionstests.
Advisory-Paket für gitlab.com/n0tr1v/gobalance - master-Branch, Commit bb1b0f3 ("fix crash").
Status: KRITISCH - zwei unabhängige vollständige Übernahmepfade. Der Upstream-Branch patch1 behebt keinen von beiden.
Dieses Paket enthält einen vollständigen Sicherheitspatch, einen End-to-End-Proof-of-Concept für beide Angriffspfade und Regressionstests, die belegen, dass die Fixes greifen. Es begleitet den vollständigen Schwachstellenanalysebericht ("Laporan Analisis Keamanan GoBalance"), der im Zusammenhang mit den jüngsten Onion-Domain-Übernahmevorfällen erstellt wurde, von denen zwei Foren betroffen waren. Eine Schritt-für-Schritt-Anleitung zum Bauen und Testen findet sich in USAGE.md.
| # | Schwachstelle | Schweregrad | Auswirkung | Status |
|---|---|---|---|---|
| 1 | Master-Identitätsschlüssel leckt durch blindedSign (konstantes Nonce-Präfix) | KRITISCH | Vollständige Onion-Identitätswiederherstellung aus einem einzigen öffentlichen Deskriptor | Behoben |
| 2 | RegisterDescriptor akzeptiert gefälschte Instanz-Deskriptoren (keine Signatur-/Bindungsverifikation) | KRITISCH | Traffic-Hijacking jedes GoBalance-Frontends | Behoben |
| 3 | Deterministischer, zeitbasierter RNG-Pfad in pkg/brand | HOCH (Fußangel) | Vorhersagbares Schlüsselmaterial für alles, was ihn verwendet | Entfernt |
| 4 | Introduction-Point-Shuffle verwendet math/rand | NIEDRIG | Schwache Zufälligkeit in protokollnahem Code | Ersetzt durch crypto/rand |
Der Dispatcher blindedSign() in pkg/stem/descriptor/hidden_service.go übergab
identityKey.Seed() - den rohen 32-Byte-Skalar a - an
BlindedSignWithTorKey(). Schlüssel im Tor-Format sind erweiterte Schlüssel: 64 Bytes
(a || h), wobei h der PRF-Schlüssel ist, der das Nonce-Präfix pro Signatur ableitet. Da h fehlte, war die Eingabe für die Nonce-Ableitung leer und
kPrime = SHA512("Derive temporary signing key hash input" || <empty>)
wurde zu einer öffentlichen Konstante. Konsequenz: Jeder, der EINEN veröffentlichten
Deskriptor lesen kann, kann das Nonce r neu berechnen, den geblindeten Skalar
s' = (S − r) · H(R‖PK‖M)⁻¹ mod L auflösen und ihn mit einem öffentlichen Multiplikator
entblinden - wodurch der Master-Identitätsschlüssel des Onion-Dienstes wiederhergestellt wird. Kein Serverzugriff, kein MitM, keine Brute Force. Dies ist ein stiller Domain-Übernahme-Primitiv und steht im Einklang mit dem Mechanismus, der bei den jüngsten Forum-Hijacks beobachtet wurde.
Fix: Der Dispatcher leitet nun den vollständigen erweiterten Schlüssel weiter
(gobpk.PrivateKey.PrivKey()); BlindedSignWithTorKey löst bei jedem Schlüssel, der
nicht genau 64 Bytes lang ist, eine Panik aus; blindedSignP2 erzwingt unabhängig die ESK-Länge
als Defense in Depth; gobpk.New lehnt abgeschnittene Tor-Schlüssel beim Laden ab.
NewReceivedDescriptor() parste und vertraute allem, was das Netzwerk ihm übergab.
Da Subcredentials aus dem geblindeten Schlüssel abgeleitet werden, der im
Deskriptor selbst mitgeführt wird, konnte ein Angreifer kryptografisch selbstkonsistente
Deskriptoren für die Onion-Adresse eines anderen mit seinen eigenen Schlüsseln erzeugen. Das
Frontend würde dann die Introduction Points des Angreifers unter der Identität des Opfers
erneut veröffentlichen - ein vollständiges Traffic-Hijacking, das überhaupt keine Schlüsselwiederherstellung erfordert.
Fix: dreischichtige Verifikation im neuen
VerifyHiddenServiceDescriptorV3(): (1) Zertifikatssignatur unter dem
geblindeten Schlüssel, (2) Deskriptorsignatur unter dem zertifizierten Signaturschlüssel und
(3) Bindung - der geblindete Schlüssel muss dem Wert entsprechen, den das Frontend unabhängig
aus dem Konsens (GetBlindingParam + Zeitperiode) und der
Instanzadresse berechnet. RegisterDescriptor ist fail-closed: ohne einen lebenden
Konsens verweigert es die Registrierung, statt blind zu vertrauen.
gobalance-patch/
├── README.md ← this file (English)
├── USAGE.md ← step-by-step build & test guide (English)
├── README_ID.md ← ringkasan patch (Bahasa Indonesia)
├── gobalance-security.patch ← unified diff against master@bb1b0f3 (7 files, +360/−94)
├── gobalance-patched/ ← full pre-patched source tree (drop-in)
│ ├── go.mod / go.sum / main.go
│ ├── pkg/… ← patched libraries, incl. regression tests
│ ├── poc/ ← end-to-end attack demo + vulnerable code snapshot
│ ├── cmd/gbdemo/ ← standalone recovery demo CLI (+ E2E tests) - USAGE #13
│ └── tools/ ← pem2tor.py (PEM→Tor key converter), get_desc.py (descriptor fetch)
└── gobalance-v1/ ← community fork "GoBalance Enhanced v1.0" (Dread), bundled
as distributed for testing - still VULNERABLE - 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
Siehe USAGE.md für die vollständige Schritt-für-Schritt-Anleitung mit erwarteter Ausgabe.
Test01_Vulnerable_MasterKeyRecoveredFromSingleDescriptor.Test02_Patched_TruncatedTorKeyRejected.Test03_Patched_TorPathSignaturesVerifyAsStdEd25519.Test04_Patched_AttackMathYieldsGarbage.TestForgedSelfConsistentDescriptorIsDetected.TestNewReceivedDescriptor_AcceptsHonestDescriptor,
_RejectsTamperedSignature, _RejectsWrongIdentityBinding.Alle Schlüssel im PoC werden lokal zur Testzeit generiert. Es wurden keine echten Dienste angegriffen.