57932 sc critical attacker can bypass stake lock
Submitted on Oct 29th 2025 at 14:06:41 UTC by @danial for Audit Comp | Belong
Report ID: #57932
Report Type: Smart Contract
Report severity: Critical
Target: https://github.com/immunefi-team/audit-comp-belong/blob/main/contracts/v2/periphery/Staking.sol
Impacts:
Protocol insolvency
Description
Brief/Intro
An attacker can remove their assets while keeping large locked stakes untouched, effectively bypassing the minimum stake period and unlocking funds early. This can break internal accounting and cause protocol insolvency.
Vulnerability Details
Each deposit creates a new locked stake record:
stakes[to].push(Stake({shares: shares, timestamp: block.timestamp}));Withdrawal uses _consumeUnlockedSharesOrRevert() to ensure minStakePeriod has passed. However, in emergencyWithdraw(), the contract calls:
_removeAnySharesFor(_owner, shares);
_burn(_owner, shares);The function _removeAnySharesFor removes stake records:
The issue arises when the attacker holds multiple stakes and relies on transfers not creating stake entries. The sequence below demonstrates how an attacker can bypass the lock while keeping stake records intact.
Emergency withdraw consumes transferred shares
B calls
emergencyWithdraw(102e18, to=B, owner=B)_removeAnySharesForremoves {2,tB} and consumes transferred shares, paying ~90% (~92e18) to B. stakes[A] entry remains.Attacker now holds ~92e18 tokens and can use these tokens near expiry while having the stake records.
Reuse stake records to withdraw locked stake + rewards later
Near original lock expiry, Wallet C deposits 100e18 → stakes[C] = {100, tC}.
Attacker transfers those 100 shares from C → A (transfer creates no stake entry).
Now A’s balance includes 100 shares, while stakes[A] still records the old {100, tA}.
Attacker withdraws from A after lock period and redeems 100e18 + rewards.
Impact Details
Attacker can transfer shares to get most of their tokens with minimal penalty while keeping stake records intact, then later get rewards without respecting the lock. This undermines the contract’s intended staking behavior and can lead to protocol insolvency.
Mitigation
Suggested mitigation (disable transfers of shares):
Proof of Concept
Note: for simplicity, this PoC is not using a proxy (the constructor in Staking.sol can be commented out for testing).
Run the test with: forge test -vv
Was this helpful?