# Stale flag replay after cleanup

Flags set once and never cleared on offboard, cleanup, or re-registration, letting old state replay privileged actions

_Area: Smart contract access control and control flow. Seen in 3 disclosed reports; latest 2024-02-22. Updated 2026-11-01._

## How it works
A flag or status is set when an object is onboarded, approved, or claimed, and is meant to be cleared when that object leaves. The assumption that breaks: that the clearing path always runs before the object can return. When an object is re-registered, re-onboarded, or a proposal window outlives cleanup, the old flag survives and still gates a privileged action. Anyone can then replay the stale state to force that action without the normal approval.

## Where it shows up
- A boolean or status mapping (`canOffboard[term]`, `claimed[user]`) that gates a privileged action and is reset in only one function.
- Reset functions gated on a condition that may not hold (all loans closed, root unchanged) before re-registration is possible.
- Proposal/poll state with a time window that outlives the flag it produced; a vote function that re-sets the flag without checking current object state.
- Merkle distributors storing a boolean claimed flag rather than claimed amounts.
- Batch executors that revert atomically on partial failure and persist no per-item attempted state.

## How to review
For every flag that gates a privileged action, enumerate all paths that re-activate the guarded object and confirm each one clears or re-validates the flag first. The invariant: a flag may only be set while the object is in the state that justified it, and any transition out of that state (offboard, cleanup, root update, re-onboarding) must invalidate it. Check that cleanup also invalidates the underlying poll/proposal that can regenerate the flag, and that time windows cannot outlive the state they authorize. For claim gates, verify per-user amounts are tracked, not a boolean. For executors, verify per-item execution state is persisted so failed items are not indefinitely replayable.

## How finders spotted it
- Read the offboard/cleanup lifecycle and noticed `cleanup()` is the only reset for `canOffboard`, gated on loan closure, leaving a window where re-onboarding keeps the flag [E1](https://bugbountylibrary.com/r/c4%3A2023-12-ethereumcreditguild%3AM-05.md).
- Noticed `claimed` used as a boolean gate rather than tracking amounts, combined with the expectation the merkle root may be updated [E2](https://bugbountylibrary.com/r/c4%3A2023-07-arcade%3AM-05.md).
- Saw `CallLib` revert entirely on any failed call and the executor record no attempted state, then reasoned about relayer retries [E3](https://bugbountylibrary.com/r/c4%3A2022-12-pooltogether%3AM-03.md).
- Noticed `cleanup()` resets the flag but the ~7-day poll persists, and `supportOffboard()` re-sets the flag without checking term state [E4](https://bugbountylibrary.com/r/c4%3A2023-12-ethereumcreditguild%3AM-06.md).

## Variants seen
- Stale `canOffboard` surviving re-onboarding before cleanup runs [E1](https://bugbountylibrary.com/r/c4%3A2023-12-ethereumcreditguild%3AM-05.md).
- Boolean `claimed` flag blocking legitimate re-claims after a root update [E2](https://bugbountylibrary.com/r/c4%3A2023-07-arcade%3AM-05.md).
- No attempted-execution state, so failed calls stay pending and replayable [E3](https://bugbountylibrary.com/r/c4%3A2022-12-pooltogether%3AM-03.md).
- Stale offboarding poll re-triggering the flag after cleanup [E4](https://bugbountylibrary.com/r/c4%3A2023-12-ethereumcreditguild%3AM-06.md).

## Impact and severity
Forced offboarding bypasses a DAO vote and slashes stakers' principal and rewards [E1](https://bugbountylibrary.com/r/c4%3A2023-12-ethereumcreditguild%3AM-05.md)[E4](https://bugbountylibrary.com/r/c4%3A2023-12-ethereumcreditguild%3AM-06.md); users are permanently locked out of increased rewards with tokens stuck [E2](https://bugbountylibrary.com/r/c4%3A2023-07-arcade%3AM-05.md); relayers are gas-drained and stale calls execute against changed state [E3](https://bugbountylibrary.com/r/c4%3A2022-12-pooltogether%3AM-03.md). Impact rises when the stale action is irreversible (slashing, forced loan closure) and when timing after re-onboarding maximizes losses [E1](https://bugbountylibrary.com/r/c4%3A2023-12-ethereumcreditguild%3AM-05.md).

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2023-12-ethereumcreditguild%3AM-05.md
- E2: https://bugbountylibrary.com/r/c4%3A2023-07-arcade%3AM-05.md
- E3: https://bugbountylibrary.com/r/c4%3A2022-12-pooltogether%3AM-03.md
- E4: https://bugbountylibrary.com/r/c4%3A2023-12-ethereumcreditguild%3AM-06.md
