31488 - [SC - Critical] Merging tokens allows multiple Flux accruals wi...
Submitted on May 20th 2024 at 10:37:06 UTC by @Holterhus for Boost | Alchemix
Report ID: #31488
Report type: Smart Contract
Report severity: Critical
Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/VotingEscrow.sol
Impacts:
Theft of unclaimed yield
Description
Brief/Intro
A token that has claimed Flux in the current epoch can be merged with a token that has not yet claimed Flux. This allows the already claimed token to transfer its Flux and reuse its voting power through the non-voted token. An attacker can exploit this by repeatedly merging voted tokens with fresh, non-claimed tokens to perpetually claim Flux using the same initial voting power. This allows an attacker to accrue unlimited Flux instantly.
Vulnerability Details
Every token is allowed to accrue Flux once per epoch. The amount of claimable Flux is calculated in the claimableFlux()
function, which multiplies the current voting power of the token by fluxPerVeALCX
.
In the VotingEscrow
contract, the merge()
function allows a user to combine the voting power of token from
and token to
, with the from
token ultimately burned while the to
token continues with the combined voting power. Nothing prevents someone from claiming Flux with the from
token, transferring its voting power to a fresh to
token, and claiming Flux again. This will double count the underlying voting power and allow claiming multiple times in an epoch.
Impact Details
An attacker can repeat the accrual of their Flux multiple times in an epoch using the same underlying voting power. This allows for an infinite mint of Flux.
References
See the PoC below.
Proof of Concept
I have created the following test file and added it to the tests/
directory:
Running the command forge test -vvv --match-test testUnlimitedAccrueFluxBug --rpc-url $ETH_RPC_URL
gives the following result:
Since 39432419229826226642800 / 98583512563800063892800
is approximately 3.999
, this specific example shows that the Flux from tokenIds[0]
can be quadruple counted by using 4 fresh token ids.
Last updated