-
Notifications
You must be signed in to change notification settings - Fork 68
Open
Description
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
- Setup: The network is at View v.
- Attack: A Byzantine node takes an old, valid QC for a block at View v-k.
- Manipulation: The attacker creates a new QC struct using the same signature and block hash, but sets the View to v + 1000.
- Replay: The attacker sends this forged QC to honest replicas.
- 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
Labels
No labels