# Chained signature with checkpoint usage disabled can bypass all checkpointer validation

Chained signature with checkpointer flag unset bypasses checkpointer validation, letting an evicted signer authorize transactions using stale wallet config.

- Payout: Contest reward
- Severity: high
- Program: Sequence
- Credited: hgrano
- Disclosed: 2025-11-21
- Write-up quality: 8/10
- Original: https://code4rena.com/reports/2025-10-sequence#h-01
- Library page: https://bugbountylibrary.com/r/c4%3A2025-10-sequence%3AH-01
- Tags: domain:web3 impact:funds-loss impact:integrity kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:auth-bypass vuln:signature-replay

**Target:** BaseSig.sol signature recovery library
**Root cause:** When a chained signature is used with bit 6 (0x40) unset, the checkpointer override block is skipped, leaving _checkpointer and snapshot fields zero. recoverChained then calls recover with _ignoreCheckpointer=true, so the inner signature's checkpointer data is ignored and snapshot.imageHash remains zero, bypassing the UnusedSnapshot check.
**Steps:**
1. Deploy wallet with checkpointer and config1 (Alice signer, checkpoint 1).
2. Checkpointer advances to config2 (Bob signer, checkpoint 2); Alice evicted.
3. Alice signs a payload valid under config1.
4. Construct chained signature: outer flag `0x05` (bit 6 unset), inner signature with checkpointer data removed.
5. Call `recoverPub` with chained signature and `_checkpointer=address(0)`.
6. Observe recovery succeeds and returns imageHash for config1, bypassing checkpointer.
**How found:** Code review of BaseSig.recover and recoverChained revealed that the checkpointer override block is conditional on bit 6 being set. The researcher traced the flow when bit 6 is unset in a chained signature, noticing that _checkpointer and snapshot fields remain zero and _ignoreCheckpointer=true is passed to the inner recover, leading to bypass of the UnusedSnapshot check.
**Exploitation:** An evicted signer signs a payload valid under the stale wallet configuration. They craft a chained signature with the checkpointer usage flag (bit 6) unset and strip checkpointer data from the inner signature. When the wallet recovers the signature, the checkpointer is ignored, snapshot.imageHash stays zero, and the UnusedSnapshot check is bypassed, allowing the stale signature to authorize transactions.
**Insight:** When a security-critical flag (like checkpointer usage) is optional, trace what happens when it's omitted in nested/chained contexts. Zero-valued defaults can silently disable validation checks that assume non-zero state.
