Skip to content

Latest commit

 

History

History
127 lines (90 loc) · 6.9 KB

071.md

File metadata and controls

127 lines (90 loc) · 6.9 KB

Macho Merlot Squirrel

High

Malicious actors will extract private keys from validators using randomness reuse in EOTS signatures

Summary

Insufficient protection against randomness reuse in the EOTS (Extractable One-Time Signature) implementation will cause a catastrophic security breach for Babylon validators as attackers will extract private keys when the same randomness is used to sign different messages, allowing complete control over staked funds and signing privileges.

Root Cause

The EOTS implementation inherently allows private key extraction when randomness is reused across signatures for different messages. While this is an intentional design feature for slashing validators who double-sign the same block height, it becomes a critical vulnerability when randomness is accidentally reused in other contexts.

In babylon/crypto/eots/eots.go:199-246, the Extract and extractFromHashes functions enable key extraction from signatures that use the same randomness, which is a necessary security mechanism for the protocol but becomes catastrophic if randomness reuse occurs due to implementation errors, entropy failures, or targeted attacks.

This vulnerability warrants a HIGH severity rating because:

  • Impact: Complete compromise of validator private keys, allowing theft of staked funds and impersonation
  • Exploitability: Straightforward mathematical extraction when conditions are met
  • Defense Difficulty: Relies entirely on perfect randomness generation, a historically problematic area
  • Historical Precedent: Similar issues have caused major security breaches (Sony PlayStation 3, Android Bitcoin wallets)

The vulnerability's primary limitation is that it requires randomness reuse, which shouldn't happen with proper implementation - but entropy failures, VM snapshots, and implementation bugs have repeatedly caused such issues in production systems.

Internal Pre-conditions

  1. A validator must generate two or more signatures using the same randomness value for different messages
  2. The attacker must be able to observe both signatures, their associated messages, and the public randomness value

External Pre-conditions

  1. The validator's system must have a flaw in its random number generation (poor entropy, implementation bugs, VM snapshots, etc.)
  2. OR the validator's system must be susceptible to side-channel attacks allowing randomness prediction/control

Attack Path

  1. Attacker identifies a validator with a flawed RNG implementation, as demonstrated by FaultyRandomReader in eots_vulnerability_test.go (PoC)
  2. When the validator signs messages, they use eots.RandGen(randSource) at eots_vulnerability_test.go to generate randomness
  3. Due to the faulty RNG, the same randomness value is reused across different messages (block1Hash and block2Hash) at eots_vulnerability_test.go
  4. Both signatures (sig1 and sig2) are individually valid and can be verified using eots.Verify()
  5. The attacker observes these signatures and uses the eots.Extract() function at eots_vulnerability_test.go to compute:
extractedPrivKey, err := eots.Extract(pubKey, randPub1, block1Hash, sig1, block2Hash, sig2)
  1. This calls into eots.go where the mathematical extraction of (sig1 - sig2) / (e1 - e2) is performed
  2. The attacker now has the validator's private key and can sign arbitrary messages as demonstrated in eots_vulnerability_test.go

Impact

The affected validator suffers a complete compromise of their private key, resulting in:

  1. Total loss of staked funds under their control
  2. Ability for attackers to impersonate the validator and sign malicious messages
  3. Potential consensus manipulation in the Babylon protocol
  4. Loss of reputation and trust in the validator

PoC

// file location: babylon/crypto/eots/eots_vulnerability_test.go

func Test_RandomnessReuseVulnerability(t *testing.T) {
    // Create a validator's private key
    privKey, err := secp256k1.GeneratePrivateKey()
    require.NoError(t, err)
    pubKey := privKey.PubKey()

    // Create a faulty random number generator
    faultyRandReader := NewFaultyRandomReader()

    // Sign two different messages with the same randomness
    block1Hash := []byte("block hash at height 100: 0x1234...")
    block2Hash := []byte("different block hash at height 100: 0x5678...")
    
    rand1, randPub1, err := eots.RandGen(faultyRandReader)
    require.NoError(t, err)
    
    sig1, err := eots.Sign(privKey, rand1, block1Hash)
    require.NoError(t, err)
    
    sig2, err := eots.Sign(privKey, rand1, block2Hash)
    require.NoError(t, err)

    // Extract private key from signatures
    extractedPrivKey, err := eots.Extract(pubKey, randPub1, block1Hash, sig1, block2Hash, sig2)
    require.NoError(t, err)
    
    // Confirm extracted key equals original
    require.Equal(t, privKey.Serialize(), extractedPrivKey.Serialize())
    
    // Sign arbitrary message with extracted key
    attackerSig, err := eots.Sign(extractedPrivKey, rand1, []byte("malicious message"))
    require.NoError(t, err)
    
    // Verify signature is valid
    err = eots.Verify(pubKey, randPub1, []byte("malicious message"), attackerSig)
    require.NoError(t, err)
}

Result example:

go test -v ./crypto/eots/eots_vulnerability_test.go
=== RUN   Test_RandomnessReuseVulnerability
Validator signing two different blocks...
First signature verified successfully
Second signature verified successfully
Attacker attempting to extract private key from the two signatures...
VULNERABILITY CONFIRMED: Private key successfully extracted!
Attacker successfully signed a malicious message using the extracted key!
--- PASS: Test_RandomnessReuseVulnerability (0.02s)
PASS
ok      command-line-arguments  0.463s

Mitigation

  1. Implement deterministic nonce generation based on RFC 6979, which derives randomness from the private key and message, eliminating the risk of randomness reuse
  2. Add nonce tracking to detect and prevent reuse of randomness values
  3. Enhance entropy sources for randomness generation to minimize risk of low-entropy environments
  4. Add pre-signature checks to verify that randomness hasn't been used before
  5. Consider implementing additional cryptographic safeguards against RNG failures, such as mixing multiple sources of entropy

The most effective mitigation would be implementing RFC 6979 for deterministic signature generation, which would completely eliminate the vulnerability while maintaining the desired property of key extraction for double-signing.