Skip to content

Vulnerability: High-View QC Replay Attack in UpdateHighQC #291

@SherlockShemol

Description

@SherlockShemol

Vulnerability Description

A Byzantine leader can cause a liveness failure (lockout) by replaying a valid Quorum Certificate (QC) from an old view but artificially inflating the View field in the QC structure.

The issue lies in the UpdateHighQC function in protocol/viewstates.go. The function checks if the block exists and if the new view is higher than the current HighQC, but it fails to verify that the QC's View matches the View of the block it certifies.

Vulnerable Code

Location: protocol/viewstates.go

func (s *ViewStates) UpdateHighQC(qc hotstuff.QuorumCert) (bool, error) {
    newBlock, ok := s.blockchain.Get(qc.BlockHash())
    if !ok {
        return false, fmt.Errorf("block %x not found for QC@view %d", qc.BlockHash(), qc.View())
    }
    s.mut.Lock()
    defer s.mut.Unlock()
    
    // DEFECT: Checks if newBlock.View() <= current HighQC, but effectively accepts qc.View()
    // simply because the function was called, updating s.highQC to the manipulated 'qc' object.
    if newBlock.View() <= s.highQC.View() {
        return false, nil
    }
    s.highQC = qc // <--- Updates HighQC with the manipulated View from the input 'qc'
    return true, nil
}

Attack Scenario

  1. Setup: The network is at View v.
  2. Attack: A Byzantine node takes an old, valid QC for a block at View v-k.
  3. Manipulation: The attacker creates a new QC struct using the same signature and block hash, but sets the View to v + 1000.
  4. Replay: The attacker sends this forged QC to honest replicas.
  5. Impact:
    The node effectively accepts that it is locked on a QC for View 1000, even though the block justifying that QC is only at View v-k. This can cause liveness issues as the node believes it has a much higher QC than it actually does.

Remediation

Enforce consistency between the QC view and the Block view:

if qc.View() != newBlock.View() {
    return false, fmt.Errorf("QC view %d does not match block view %d", qc.View(), newBlock.View())
}

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