
GoBalance onion 로드 밸런서에 대한 보안 패치 및 개념 증명으로, blindedSign을 통한 마스터 키 복구와 위조된 디스크립터 수용을 다루며, 회귀 테스트를 포함합니다.
gitlab.com/n0tr1v/gobalance용 권고 패키지 - master 브랜치, 커밋 bb1b0f3 ("fix crash").
상태: 치명적 - 두 가지 독립적인 전체 탈취 경로. 업스트림 patch1 브랜치는 둘 다 수정하지 않습니다.
이 패키지에는 완전한 보안 패치, 두 공격 경로에 대한 엔드투엔드 개념 증명, 그리고 수정 사항이 유지됨을 입증하는 회귀 테스트가 포함되어 있습니다. 두 포럼에 영향을 미친 최근 onion 도메인 탈취 사건과 관련하여 작성된 전체 취약점 분석 보고서("Laporan Analisis Keamanan GoBalance")와 함께 제공됩니다. 단계별 빌드 및 테스트 가이드는 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 서비스의 마스터 신원 키를 복구할 수 있습니다. 서버 접근, 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 ← 패치 요약 (인도네시아어)
├── 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
# 옵션 A - 새로운 업스트림 체크아웃에 패치 적용
git clone https://gitlab.com/n0tr1v/gobalance && cd gobalance
git apply /path/to/gobalance-security.patch
go build ./... && go test ./...
# 옵션 B - 번들된 사전 패치된 트리 사용 (가장 빠름)
cd gobalance-patched
go build ./...
go test ./poc/ -v # 공격 데모: 취약한 스냅샷에 대해 성공, 패치에 대해 실패
go test ./... # 전체 스위트: 8개 패키지 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개 실패, 회귀 없음 (기존
업스트림 테스트 포함).