# attacker can perform malicious transactions in the safe because reentrancy is not implemented correctly in the checkTransaction() and checkAfterExecution() function in HSG

HatsSignerGate's reentrancy guard counter is incremented in checkTransaction and decremented in checkAfterExecution, allowing nested execTransaction calls to bypass the guard and swap the Safe guard/threshold.

- Payout: Contest reward
- Severity: medium
- Program: Hats
- Credited: cducrest-brainbot
- Disclosed: 2023-02-15
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2023-02-hats-judging/issues/124
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-02-hats%3AM-1
- Tags: domain:web3 impact:integrity kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:chained technique:source-review vuln:access-control vuln:reentrancy

**Target:** HatsSignerGateBase
**Root cause:** The _guardEntries counter is incremented in checkTransaction and decremented in checkAfterExecution, so a reentrant execTransaction during execution increments again and the single decrement in the outer checkAfterExecution does not underflow, leaving the guard bypassed.
**Steps:**
1. Deploy/use Safe with HatsSignerGate as guard and threshold set.
2. Tx1 (valid signers): sets guard to address(0) and calls `safe.execTransaction(Tx2)`.
3. Tx2: resets guard to HSG address.
4. Call `safe.execTransaction(Tx1)`; checkTransaction increments `_guardEntries` to 1, then Tx1 sets guard to 0 and executes Tx2 which restores guard.
5. checkAfterExecution decrements `_guardEntries`; no underflow, guard/threshold changed during execution.
**How found:** Code review of the guard's counter logic: noticed increment in checkTransaction and decrement in checkAfterExecution, reasoned that nested execTransaction would double-increment and single-decrement, avoiding the intended underflow revert.
**Exploitation:** Attacker with valid signer threshold submits a transaction that temporarily removes the guard, executes a nested malicious transaction resetting guard/threshold, then restores the guard so the post-execution check passes.
**Insight:** When a reentrancy guard uses a counter with increment-before/decrement-after around an external call, check whether nested calls can increment more than once and be balanced by a single decrement; the guard should be set to a nonzero sentinel before the call and cleared after, not merely counted.
