# Rogue plugin can become unremovable and halt all staking and claiming

A rogue StakingModule plugin returning false from deactivated() and overflowing claim() sums blocks its own removal and freezes all staking/claiming.

- Payout: Contest reward
- Severity: high
- Program: Telcoin
- Credited: hyh
- Disclosed: 2023-02-15
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2023-02-telcoin-judging/issues/67
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-02-telcoin%3AH-1
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:dos

**Target:** StakingModule plugin registry (Solidity staking contract)
**Root cause:** removePlugin() requires IPlugin(plugin).deactivated() to return true, so a malicious plugin can refuse removal; it can also revert requiresNotification() or return 2**256-1 from claim() to overflow the total sum.
**Steps:**
1. Plugin is added to StakingModule via addPlugin.
2. Plugin turns malicious (bug or upgrade) and returns false from `deactivated()`.
3. Admin calls `removePlugin(index)`; `require(IPlugin(plugin).deactivated())` reverts, plugin stays.
4. Plugin returns `2**256-1` from `claim()` so `total += xClaimed` overflows and reverts `_claim`.
5. Plugin reverts `requiresNotification()` so `_notifyStakeChangeAllPlugins` reverts, blocking stake/exit paths.
**How found:** Code review of StakingModule's plugin lifecycle: noticed removePlugin depends on a plugin-controlled deactivated() return value, and that plugin return values feed unchecked arithmetic and unguarded require calls in core flows.
**Exploitation:** A compromised or upgraded plugin contract weaponizes the trust StakingModule places in plugin return values: by refusing deactivation it cannot be removed, and by reverting/overflowing in claim() and requiresNotification() it makes every staking, claiming, and exit path revert, permanently freezing all staked funds.
**Insight:** When a registry/plugin system lets the plugin control its own removal precondition (deactivated()) and feeds plugin return values into unchecked sums or bare require() calls, a single malicious plugin becomes an unremovable denial-of-service anchor for the whole contract.
