57717 sc medium attacker can spam tiny stakes to a victim and make their withdrawal run out of gas griefing dos

Submitted on Oct 28th 2025 at 12:17:54 UTC by @manvi for Audit Comp | Belongarrow-up-right

  • Report ID: #57717

  • Report Type: Smart Contract

  • Report severity: Medium

  • Target: https://github.com/immunefi-team/audit-comp-belong/blob/main/contracts/v2/periphery/Staking.sol

Impacts:

  • Temporary freezing of NFTs for at least 24 hour

  • Griefing (attacker has no profit motive, but harms users or the protocol)

  • Unbounded gas consumption

Description

Brief / Intro

The Staking flow creates a fresh stake entry for every deposit(assets, receiver). Withdraw/redeem scans and pops across all of a user's stake entries. Because ERC-4626 allows deposit(…, receiver) by anyone, an attacker can spam thousands of tiny deposits to a victim, making the victim’s withdraw path O(n) and practically fail under normal gas — temporarily freezing their funds.

Vulnerability Details

While reviewing contracts/v2/periphery/Staking.sol:

  • Each call to _deposit(...) appends a new stake entry. There is no merge/min-size/cap per user.

  • The withdraw path (via the unlocked-shares consumer) iterates and pops entries until it gathers enough unlocked shares.

  • ERC-4626 allows deposit(…, receiver) by anyone, so an attacker can create thousands of entries for the victim without their consent.

  • From tests, once a victim's stake entry count is large (e.g., 5k–10k), withdraw/redeem becomes gas-bounded and reverts with typical gas settings. With fewer entries, the same call succeeds — this is a gas-scaling DoS rather than a logic bug.

Impact Details

  • Attacker can make a victim's withdraw uneconomical or revert due to gas limits.

  • Victim cannot withdraw under normal gas caps and would need exceptional gas or state changes.

  • Cost/effort for the attacker scales linearly with the number of attacker-created entries; the attack is cheap since attacker only needs to pay spam gas and needs no approvals or roles.

File referenced: contracts/v2/periphery/Staking.sol

Proof of Concept

I wrote a Foundry test against the repo that:

  • Deploys Staking and a simple ERC-20 mock.

  • Victim stakes normally.

  • Attacker loops deposit(1, victim) thousands of times, creating thousands of victim entries.

  • Advance time past the lock.

  • Call withdraw from the victim with a low/typical gas cap → it reverts due to the O(n) scan; with a very high gas cap, it succeeds, proving gas-based DoS.

File location: poc/PoC_Staking_Griefing.t.sol

chevron-rightPoC file content (click to expand)hashtag
chevron-rightHow to run the PoC (click to expand)hashtag

Run from repo root:

Console output (excerpt):

What the PoC proved

  • An arbitrary attacker can repeatedly call deposit(…, receiver=victim) to create thousands of tiny stake entries for the victim (no merge/min-size guard in _deposit).

  • When the victim later tries to withdraw, the contract's _consumeUnlockedSharesOrRevert must iterate across entries (O(n)) to assemble unlocked shares.

  • The test shows that, after spam, a victim's withdraw reverts under a reasonable gas cap — demonstrating a practical griefing DoS.

  • The attacker needs no approvals/roles and does not take custody of the victim's funds — only pays spam gas, making the attack cheap and repeatable.

circle-exclamation

Was this helpful?