31481 - [SC - Critical] Undound FLUX accrual through reset and merge
Submitted on May 20th 2024 at 05:17:59 UTC by @DuckAstronomer for Boost | Alchemix
Report ID: #31481
Report type: Smart Contract
Report severity: Critical
Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/FluxToken.sol
Impacts:
Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Manipulation of governance voting result deviating from voted outcome and resulting in a direct change from intended effect of original results
Description
Vulnerability Details
The attacker can accrue FLUX indefinitely by following a loop: first, they call Voter.reset()
to accrue FLUX for the current veALCX, then transfer the balance from that veALCX to a new veALCX using veALCX.merge(oldId, newId)
, followed by claiming FLUX for the new veALCX. This process can be repeated multiple times to keep accruing FLUX continuously.
FLUX can be accrued by calling Voter.reset()
once in an epoch for the current tokenId.
https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/Voter.sol#L191
However, it's possible to transfer veALCX balance to a new tokeId by calling veALCX.merge()
. This allows to claim FLUX one more time with Voter.reset()
.
Impact Details
Flux token allows to boost voting power. Unbounded Flux accrual allows to significantly change the voting results.
Proof of Concept
Poc scenario:
The attacker mints the main veALCX with 1000 BAL tokens.
The attacker mints 100 more auxiliary veALCX with 10 wei of BAL each.
The loop begins.
In the loop, the attacker calls
Voter.reset()
to accrue FLUX amount.The attacker immediately transfers the balance from veALCX from step 3 to a yet unused auxiliary veALCX by calling
veALCX.merge(lastId, currentId)
.Go back to step 3.
To run the PoC, place the code below in the PoC.t.sol
file and execute the command: forge test --mp src/test/PoC.t.sol --fork-url 'URL'
.
Last updated