# HatsSignerGateBase: valid signer threshold can be bypassed because HSG checks signatures differently from Safe which allows exploitation

HatsSignerGate counts valid signatures across all provided sigs while Safe only checks the first threshold, letting a revoked signer append a stale valid signature to pass both checks.

- Payout: Contest reward
- Severity: high
- Program: Hats
- Credited: bin2chen
- Disclosed: 2023-02-15
- Write-up quality: 8/10
- Original: https://github.com/sherlock-audit/2023-02-hats-judging/issues/50
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-02-hats%3AH-3
- Tags: domain:web3 impact:privesc kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:access-control vuln:auth-bypass vuln:signature-replay

**Target:** HatsSignerGateBase guard on Safe (Zodiac module)
**Root cause:** Safe.checkNSignatures validates only the first `threshold` signatures, but HSG.countValidSignatures iterates over all `signatures.length/65` entries and counts any valid signer, so the two checks apply to different signature sets.
**Steps:**
1. Setup: Safe with 4 owners, threshold=3, 3 valid signers; Bob is an owner whose signer hat was revoked but not yet removed from owners.
2. Bob obtains 2 signatures from valid signers on a malicious tx.
3. Bob appends his own signature (3rd) plus a stale signature from a valid signer from a previous tx (4th), so signatures bytes contain 4 entries.
4. Call `Safe.execTransaction(...)` with the 4-signature blob.
5. Safe.checkNSignatures validates first 3 (2 valid + Bob) -> passes.
6. HSG.countValidSignatures scans all 4, finds 3 valid signers (2 fresh + 1 stale) -> passes.
7. Transaction executes despite Bob being an invalid signer.
**How found:** Code-reading comparison of Safe.checkNSignatures (loops i<requiredSignatures) against HSG.countValidSignatures (loops i<sigCount=signatures.length/65); noticed the two guards validate different subsets and that v==0/v==1 signature types don't verify signed data, enabling replay of stale signatures.
**Exploitation:** A revoked-but-still-owner signer collects threshold-1 signatures from valid signers, appends their own signature plus a replayed stale signature (v==0 contract sig or v==1 approved hash, which don't bind to tx data), and submits execTransaction; Safe validates the first threshold sigs while HSG counts any valid signers across the full blob, so the tx executes.
**Insight:** When a guard/module re-implements signature validation on top of Safe, verify it checks the exact same signature subset (first `threshold`) as Safe; any divergence plus non-data-binding sig types (v==0 contract sig, v==1 approved hash) lets you pad the blob with stale valid signatures.
