Replicated empty notarization lets a previous-epoch Telock become the new epoch's chain tip
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 38/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- go
- Lĩnh vực
- distributed-systems, security
Hướng nghiên cứu
Start with processQuorumRound and QuorumRound.GetEpoch in simplex/epoch.go and common/msg.go, then trace the existing quorum verification and replicated response handling described in the issue. Check how the block, notarization, finalization, and empty notarization epochs are validated before any state is stored or persisted. Done means a mixed epoch quorum round is rejected; the issue’s test sketch expects the WAL to remain unchanged and Metadata().Prev to stay unchanged.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Details
A validator of the new epoch can halt the whole chain for every validator, and a restart does not fix it. It needs the old epoch's notarization for its first Telock, and enough validator overlap for that old certificate to verify. The cause is processQuorumRound, which takes the epoch of a replicated round from its empty notarization and never checks the block's own epoch.
flowchart TD
n1["attacker validator of the new epoch"]
n2["processQuorumRound"]
n3["honest validators' chain state"]
n4["epoch comparison of block, notarization and empty notarization"]
n5["ReplicationResponse handling in HandleMessage"]
n1 -->|"sends mixed quorum round"| n5
n5 -->|"skips the epoch filter"| n2
n2 -->|"stores old Telock as chain tip"| n3
n4 -->|"not applied"| n2
classDef actor stroke:#2563eb,stroke-width:2px
classDef vulnerable_code stroke:#dc2626,stroke-width:2px
classDef existing_control stroke:#16a34a,stroke-width:2px
classDef missing_control stroke:#dc2626,stroke-width:2px,stroke-dasharray:5 5
classDef asset stroke:#9333ea,stroke-width:2px
classDef component stroke:#6b7280,stroke-width:1px
class n1 actor
class n2 vulnerable_code
class n3 asset
class n4 missing_control
class n5 component
- attacker validator builds a mixed
ReplicationResponsequorum round. It combines the previous epoch's Telock block and its notarization with a genuine new-epoch empty notarization for the same round. - attacker validator sends the response to the validators that are already in the new epoch. Replication responses skip the per-message epoch filter in
HandleMessage, so no epoch check runs at this point. (simplex/epoch.go:186-190) processQuorumRoundaccepts the quorum round after one epoch check.QuorumRound.GetEpochreturns the empty notarization's epoch first, so the current epoch hides the old epoch of the block. (simplex/epoch.go:3628-3633)verifyQuorumRoundverifies the old notarization against the current validator set. It passes when the signers are still validators with the same keys and form a quorum of the new set, andVerifyQCConsistentWithBlockonly compares rounds, not epochs. (simplex/epoch.go:3576-3601)createNotarizedBlockVerificationTaskstores the old-epoch Telock as the newest notarized block and restarts the round. It writes the block and notarization toe.roundsand the WAL, andVerify(OnlyVMVerifyOpt)returns at once for a Telock. (simplex/epoch.go:2376-2457)metadata()builds the next proposal on the injected Telock at seq k+2. The new epoch's chain is now built on the Telock instead of on the sealing block. (simplex/epoch.go:2749-2777)indexFinalizationswaits forever for seq k+1 after the new block is finalized.CallbackStorage.Indexskips Telocks, so seq k+1 is never committed. Blocks keep being finalized but none is committed. (simplex/epoch.go:1503-1544)
Evidence
- simplex/epoch.go:3628–3633
- common/msg.go:351–361
- common/msg.go:383–421
- simplex/epoch.go:3576–3601
- simplex/epoch.go:186–190
- simplex/epoch.go:2376–2457
- external.go:62–79
- simplex/epoch.go:2749–2777
- simplex/epoch.go:682–706
- msm/msm.go:1469–1489
- simplex/epoch.go:1503–1544
- adapters.go:59–75
- simplex/epoch.go:3492–3522
Impact
The chain halts for every validator, because no block can be committed once a block built on the Telock is finalized. A restart does not recover it, since the WAL filter keeps the new-epoch blocks built on the Telock. No conflicting block is committed and nothing is disclosed, so the harm to integrity is limited to corrupted consensus state.
Reproduction steps
- The attacker must be a validator of the new epoch. It must hold the old epoch's notarization, or the individual votes, for the old epoch's first Telock. Conditions outside its control: that Telock was notarized, and the old certificate verifies against the new validator set. The second condition needs validator overlap, which an incremental validator-set change gives. The attacker can re-aggregate the old votes to pick signers that fit. The new epoch's first round must also get an empty notarization, and the attacker can force this when it leads that round. As the empty notarization forms, it sends the mixed response to every validator. The next leader builds on the Telock, and the poisoned validators notarize and finalize that block. No user action is needed. A variant with a finalized Telock sets
e.lastBlockto the Telock directly. Poisoned honest nodes also serve the mixed round to peers. Replies for seq k+1 carry no empty notarization and are dropped.
Recommended fix
processQuorumRound relies on QuorumRound.GetEpoch, which lets a current-epoch empty notarization vouch for a block, notarization or finalization of another epoch. VerifyQCConsistentWithBlock never compares the empty notarization's epoch with the block's. The old certificate then verifies against the current validators.
Fix criteria: Every part of a replicated quorum round (block header, notarization, finalization and empty notarization) carries the node's current epoch. Otherwise the whole quorum round is dropped before anything is stored, persisted, voted on or used by metadata(). The test sketch confirms it: an epoch-4 block plus an epoch-5 empty notarization delivered to an epoch-5 node leaves the WAL without its records and Metadata().Prev unchanged.
Status on main
Re-checked against main at 90995e0 on 2026-10-26; still present:
processQuorumRound (simplex/epoch.go:3618-3653) checks only QuorumRound.GetEpoch(), which returns the empty notarization's epoch first (common/msg.go:351-361). VerifyQCConsistentWithBlock (common/msg.go:383-421) compares round and digest/header but never compares the empty notarization's epoch with the block's epoch. verifyQuorumRound (epoch.go:3576-3601) verifies the old-epoch notarization QC against the current validator set (VerifyQC in util.go:53-76 does no epoch check). createNotarizedBlockVerificationTask (epoch.go:2376-2457) also has no block-epoch check. Rounds continue across epochs (setMetadataFromStorage sets e.round = lastBlock.Round+1, epoch.go:688-689), so Telock rounds can collide with new-epoch rounds. Exploitability needs validator overlap.
Severity: MEDIUM
Status: Open
Category: Capture-replay
CWE: CWE-294
Repository: ava-labs/Simplex
Branch: main
Date created: 2026-10-26
Claude Security finding: https://claude.ai/security/findings/find_01Txb4brf7Qje2X7Lr7cVe1E
- Ngôn ngữ chính
- Go
- Star
- 22
- Fork
- 4
- Merge trung bình
- 1 ngày 17 giờ
- Pull request đã merge (30 ngày)
- 32
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của ava-labs/Simplex
-
low security
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Replicated notarized block dropped when the leader refills the round, stalling the validatorĐang mởmedium security
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Epoch discovery accepts a sealing block on a head count, letting low-stake validators forge epochsĐang mởmedium security
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Replicated certificate for a different block deletes the voted round, allowing a second voteCó thể đã có người làm @yacovm đã nhận 1 ngày trước. Đang mởmedium security
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
ava-labs/Simplex#662 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Replayed Telock leaves unsatisfiable verification dependency, stalling non-validator foreverCó thể đã có người làm @yacovm đã nhận 1 ngày trước. Đang mởmedium security
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
ava-labs/Simplex#659 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của ava-labs/Simplex
Issue tương tự
-
area:web p3
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Lets-Parley/Parley#912 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement help wanted
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Git-Hospital/githealthcheck#61 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
afreidah/s3-orchestrator#1824 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Type: Bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
projectdiscovery/notify#569 ·
-
internal
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
Maintainer thường phản hồi trong vòng 1 ngày