Rewards delay release could cause yields steal and loss #110
Labels
2 (Med Risk)
Assets not at direct risk, but function/availability of the protocol could be impacted or leak value
bug
Something isn't working
disagree with severity
Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments)
in discussion
Discussion about this issue is ongoing, and not yet resolved.
primary issue
Highest quality submission among a set of duplicates
sponsor confirmed
Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
syncRewards sniping
Lines of code
https://github.com/corddry/ERC4626/blob/643cd044fac34bcbf64e1c3790a5126fec0dbec1/src/xERC4626.sol#L78-L97
Vulnerability details
Impact
In the current rewards accounting, vault shares in
deposit()
andredeem()
can not correctly record the spot yields generated by the staked asset. Yields are released over the next rewards cycle. As a result, malicious users can steal yields from innocent users by picking special timing todeposit()
andredeem()
.Proof of Concept
In
syncRewards()
, the current asset balance is break into 2 parts:storedTotalAssets
andlastRewardAmount/nextRewards
. ThelastRewardAmount
is the surplus balance of the asset, or the most recent yields.And in the next rewards cycle,
lastRewardAmount
will be linearly added tostoredTotalAssets
, their sum is the return value oftotalAssets()
:totalAssets()
will be referred whendeposit()
andredeem()
.Based on the above rules, there are 2 potential abuse cases:
rewardsCycleEnd
timestamp, a user can not get the yields from last rewards cycle. Since thetotalAssets()
only containstoredTotalAssets
but not the yields part. It takes 1 rewards cycle to linearly add to thestoredTotalAssets
.Assume per 10,000 asset staking generate yields of 70 for 7 days, and the reward cycle is 1 day. A malicious user Alice can do the following:
withdraw(10,000)
from account Bob, front run it withsyncRewards()
, so that the most recent yields of amount 70 from Bob will stay in the vault.redeem()
to take the yields of 70.Effectively steal the yields from Bob. The profit for Alice is not 70, because after 1 day, her own deposit also generates some yield, in this example this portion is 1. At the end, Alice steal yield of amount 60.
syncRewards()
is called. It is possible that yields from multiple rewards cycles accumulates, and being released in the next cycle.Knowing that the yields has been accumulated for 3 rewards cycles, a malicious user can
deposit()
and callsyncRewards()
to trigger the release of the rewards.redeem()
after 1 cycle.Here the malicious user gets yields of 3 cycles, lose 1 in the waiting cycle. The net profit is 2 cycle yields, and the gained yields should belong to the other users in the vault.
Tools Used
Manual analysis.
Recommended Mitigation Steps
lastRewardAmount
not released, allow the users to redeem as it is linearly released later.The text was updated successfully, but these errors were encountered: