52254 sc high arctoken theft beyond unclaimed yield during distribution
Submitted on Aug 9th 2025 at 05:30:38 UTC by @Blobism for Attackathon | Plume Network
Report ID: #52254
Report Type: Smart Contract
Report severity: High
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/arc/src/ArcToken.sol
Impacts: Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Description
Brief / Intro
An attacker controlling multiple accounts with ArcToken holdings can transfer tokens between their accounts in between calls to distributeYieldWithLimit. Because the distribution function uses current balances (no snapshot) and processes holders in chunks, the attacker can be paid twice for the same underlying tokens — once in an earlier chunk from account A, then again in a later chunk after moving the tokens to account B — causing yield distributed to exceed the intended totalAmount.
Vulnerability Details
The distributeYieldWithLimit method distributes yield in chunks to avoid gas exhaustion. It accepts a start index and a max number of holders (batchSize) to process for a given chunk. The implementation queries each holder's balance at the time of processing and uses that to compute their share, but it does not snapshot balances at the start of the distribution run or otherwise prevent balance changes between chunks.
Relevant excerpt:
function distributeYieldWithLimit(
uint256 totalAmount,
uint256 startIndex,
uint256 maxHolders
)
external
onlyRole(YIELD_DISTRIBUTOR_ROLE)
nonReentrant
returns (uint256 nextIndex, uint256 totalHolders, uint256 amountDistributed)
{
// ...
for (uint256 i = 0; i < batchSize; i++) {
uint256 holderIndex = startIndex + i;
address holder = $.holders.at(holderIndex);
if (!_isYieldAllowed(holder)) {
continue;
}
uint256 holderBalance = balanceOf(holder); // <----- no balances snapshot
if (holderBalance > 0) {
uint256 share = (totalAmount * holderBalance) / effectiveTotalSupply;
if (share > 0) {
yToken.safeTransfer(holder, share);
amountDistributed += share;
}
}
}
// ...
}Because effectiveTotalSupply and holder balances can change between chunks, an attacker can manipulate holdings between chunk calls to be counted multiple times.
Impact Details
Expected behavior: distribute up to
totalAmountamong holders, proportional to their holdings at the moment of distribution.Actual issue: by transferring tokens between attacker-controlled accounts that are processed in different chunks, the attacker can be counted multiple times and receive more yield than intended. This results in direct theft of tokens beyond the configured distribution amount.
Proof of Concept
As a result, more yield is distributed than the totalAmount provided to the distribution call sequence, enabling theft beyond intended limits.
References
Contract: https://github.com/immunefi-team/attackathon-plume-network/blob/main/arc/src/ArcToken.sol
Severity: High — an attacker controlling multiple accounts can directly extract funds beyond the configured distribution by transferring tokens between chunks during a multi-call distribution.
Was this helpful?