Skip to content

Security: Signature Scope Attack (QC View Not Protected) #289

@SherlockShemol

Description

@SherlockShemol

Security Issue: Signature Scope Attack (QC View Not Protected)

Summary

A critical security vulnerability exists in VerifyQuorumCert that allows attackers to forge QuorumCertificates (QCs) with arbitrary view numbers by replaying signatures from legitimate QCs.

Severity

Critical - This vulnerability can be exploited by Byzantine nodes to:

  • Manipulate consensus view progression
  • Forge "time-warp" proposals
  • Disrupt liveness guarantees
  • Potentially cause safety violations under certain conditions

Vulnerability Details

Root Cause

In security/cert/auth.go, the VerifyQuorumCert function only validates signatures against block.ToBytes():

func (c *Authority) VerifyQuorumCert(qc hotstuff.QuorumCert) error {
    // ... checks ...
    block, ok := c.blockchain.Get(qc.BlockHash())
    // ...
    return c.Verify(qc.Signature(), block.ToBytes())  // ← QC.View is NOT verified!
}

While block.ToBytes() includes block.View, the QC's View field is stored separately and is not cryptographically bound to the signature.

Attack Scenario: The Signature Transplant Attack

  1. Setup: Cluster runs normally, achieving consensus at View 10 for Block A

    • A legitimate QC exists: QC_Old = {View: 10, BlockHash: Hash(A), Signatures: [...]}
  2. Attack: Byzantine node extracts signatures and creates a forged QC

    QC_Fake = {
        View: 100,              // ← TAMPERED (was 10)
        BlockHash: Hash(A),     // Same as original
        Signatures: [...]       // Copied from QC_Old
    }
    
  3. Injection: Attacker constructs a "time-warp" proposal for View 101

    • Parent: Block A
    • Justification: QC_Fake (claiming View 100 consensus)
  4. Impact: Nodes accept the fake QC because:

    • Signatures are valid (they were created for Block A)
    • Block A exists in blockchain
    • QC.View field is never verified against Block.View

Concrete Attack Consequences

Attack Vector Impact
HighQC Manipulation Artificially inflate HighQC.View to arbitrary values (e.g., 999)
View Jumping Force nodes to skip legitimate view progression
Liveness Attack Cause consensus stalls by disrupting view synchronization
Proposal Forgery Create valid-looking proposals for future views

Proof of Concept

The vulnerability has been verified across all three cryptographic implementations:

=== RUN   TestSignatureScopeAttack_QCViewNotSigned/ecdsa
    🚨 SIGNATURE SCOPE ATTACK SUCCESSFUL!
    Original QC View:  10
    Fake QC View:     100 (TAMPERED)
    Verification: PASSED ← Should have FAILED!

=== RUN   TestSignatureScopeAttack_QCViewNotSigned/eddsa
    🚨 SIGNATURE SCOPE ATTACK SUCCESSFUL!

=== RUN   TestSignatureScopeAttack_QCViewNotSigned/bls12
    🚨 SIGNATURE SCOPE ATTACK SUCCESSFUL!

Affected Versions

All versions of the HotStuff implementation are affected.

Recommended Fix

Add a simple validation check in VerifyQuorumCert:

func (c *Authority) VerifyQuorumCert(qc hotstuff.QuorumCert) error {
    // ... existing checks ...

    block, ok := c.blockchain.Get(qc.BlockHash())
    if !ok {
        return fmt.Errorf("block not found: %v", qc.BlockHash())
    }

    // NEW: Prevent signature replay attacks
    if qc.View() != block.View() {
        return fmt.Errorf("QC view %d does not match block view %d (possible signature replay attack)",
            qc.View(), block.View())
    }

    return c.Verify(qc.Signature(), block.ToBytes())
}

Why This Fix Is Safe

Concern Analysis
Performance O(1) integer comparison - negligible impact
Backward Compatibility No signature format changes
Network Protocol No protocol changes required
Existing Code All legitimate QCs already satisfy QC.View == Block.View

References

  • HotStuff Paper: Section on QC verification
  • Related: View synchronization in BFT protocols

Timeline

  • Discovered: 2025-11-30
  • Reported: 2025-11-30
  • Fix Available: 2025-11-30 (branch fix/signature-scope-attack)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions