30800 - [SC - Critical] Stealing FLUX by claiming then merging position...
Submitted on May 6th 2024 at 11:30:27 UTC by @infosec_us_team for Boost | Alchemix
Report ID: #30800
Report type: Smart Contract
Report severity: Critical
Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/VotingEscrow.sol
Impacts:
Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Protocol insolvency
Manipulation of governance voting result deviating from voted outcome and resulting in a direct change from intended effect of original results
Description
Brief/Intro
Attackers can claim more FLUX than accrued.
Vulnerability Details
The attack vector is easy to understand.
ATTACK VECTOR
1- The attacker creates 2 (or more) locks in the VotingEscrow.
For the sake of simplicity let's imagine he only created 2. We'll refer to the ids of the NFTs minted as
tokenId1
andtokenId1
2- Move forward in time to the next epoch.
3- The attacker calls Voter.reset(tokenId1)
and accrues rewards for his token with id tokenId1
based on the balance of BPT he deposited when minting the NFT.
4- The attacker claims the FLUX accrued by calling flux.claimFlux(tokenId1, amount)
5- The attacker merges the balance inside tokenId1
with the balance inside tokenId2
by calling veALCX.merge(tokenId1, tokenId2);
6- Now that his second NFT (tokenId2
) has the double of BPT balance inside, he calls Voter.reset(tokenId2)
and accrues rewards for his token with id tokenId2
.
7- The attacker now claims the inflated FLUX accrued in tokenId2
by calling flux.claimFlux(tokenId2, inflated_amount)
Here's a sequence diagram:
After creating both locks the attacker must wait until the next epoch to proceed, we omitted the "wait" step in this diagram for simplicity.
If instead of doing it with 2 tokens, it is done with 3, after the last step the attacker can merge tokenId2
with tokenId3
, reset 3, and claim even more FLUX (a value relative to the full balance of tokenId1
+ the balance of tokenId2
+ the balance of tokenId3
).
Recommendation
When merging the balance of tokenId1
into tokenId2
, make sure to claim all tokenId2
's rewards if we are in a new epoch, before increasing the internal balance of tokenId2
.
Impact Details
FLUX can be used to boost the voting power of an NFT holder or as an ERC20 token that can be traded in the open market.
The consequences of gaming the system to claim more FLUX than accrued are:
Protocol insolvency.
Manipulation of governance voting.
Direct theft of funds.
Proof of Concept
Remember to run the code with the correct block number (the one used in your tests: 17133822)
We run the test executing:
Replace {API_KEY} with your API key.
To test how much FLUX the user should have accrued, add a comment to the "veALCX.merge(tokenId1, tokenId2);
"
The code for the test:
Last updated