#37775 [SC-High] Accounting Discrepancy in `consensus_v2.py::burn()`can potentially cause underflow and lead to temporary Denial of Service and a deliberate DOS Attack
Submitted on Dec 15th 2024 at 14:13:38 UTC by @Oxbakeng for Audit Comp | Folks: Liquid Staking
Report ID: #37775
Report Type: Smart Contract
Report severity: High
Target: https://github.com/Folks-Finance/algo-liquid-staking-contracts/blob/8bd890fde7981335e9b042a99db432e327681e1a/contracts/xalgo/consensus_v2.py
Impacts:
Temporary freezing of funds for at least 1 hour
Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Description
Brief/Intro
The burn()
function in consensus_v2.py
contains a flaw in how it updates the total_active_stake
which can accumulate discrepancies over time. This leads to situations where users cannot burn their entire xAlgo holdings, potentially locking funds indefinitely and opening a door for an adversary to orchestrate a DOS attack.
Vulnerability Details
To see how the vulnerability is introduced we first need to understand the relationship between minting xAlgo and burning xAlgo and how the value of xAlgo grows due to the rewards distributed in the proposer's accounts.
When users immediate_mint()
for the first time, the circulating supply of xAlgo and amount of Algo being held in the contract are considered to calculate the value of how much xAlgo to allocate to user based on how much Algo the user is sending into the contract->proposer's accounts, then the proportional amount of xAlgo in value is sent into the caller's address while the Algo is sent to the proposer's accounts, during this process the amount of Algo is incremented to the total_active_stake_key at that base value, the problem is introduced as more rewards are distributed to the proposer's accounts with only a small number of stakers and a relatively low circulating supply, due to the increase in Algo the value will increase anyway as "rewards" for stakers, so if a large number of stakers burn their xAlgo in order to get the rewards/more Algo per xAlgo, during the burn()
function call, algo_to_send
is always calculated by taking the total_rewards_key in to account thus leading to more Algo subtracted from total_active_stake_key than was put in.
Now when total_active_stake_key
is decremented, more than what was initially put in is decremented, which WILL eventually lead to a deficit and lead to an underflow when algo_to_send is higher than what is stored in total_active_stake_key
, the function will revert leading to temporary frozen funds up to an hour or more.
The vulnerability is primarily located at: App.globalPut(total_active_stake_key, App.globalGet(total_active_stake_key) - algo_to_send.load())
In the burn()
function:
This update method:
Temporary DoS: If a user attempts to burn a significant amount of xAlgo, particularly when there are few stakers or when one staker holds a disproportionately large amount, the total_active_stake might fall below the algo_to_send, leading to transaction reversion due to underflow.
Scalability Issue: There's no maximum burn amount check, which means large burns can exacerbate the discrepancy between total_active_stake and the actual balance of proposers.
Exploitation Risk: An adversary who noticed the flaw with enough ALGO could mint a large amount of xAlgo, wait for the reward cycle to increase the xAlgo's value, then burn, further widening the gap between total_active_stake and proposer balances. This is because:
When tokens are minted, the total_active_stake is updated based on the current ALGO value.
Over time, as rewards (which increase the value of xAlgo) are distributed into proposer balances, burning xAlgo results in more ALGO being returned than what was added to total_active_stake at the time of minting.
Each burn subtracts the full amount of ALGO returned, including the increased value from rewards, from total_active_stake, thus widening the discrepancy.
Impact Details
Temporary Denial of Service: Users might temporarily be unable to burn their entire holding of xAlgo if the total_active_stake_key is insufficient due to this accounting error which will result in the new total_active_stake_key entry being a negative number which would cause an underflow and revert the burn function call.
Potential Exploitation: An attacker could exploit this by manipulating the mint and burn cycles to increase the discrepancy, potentially causing more frequent DoS scenarios or manipulating the token economics to their benefit.
The Adversary can always ensure that whales that have staked large sums are always in a situation where they cannot burn their entire holding if need be.
As funds can be frozen/unable to burn entire amount up to an hour or more depending on external factors, as per the scope, this qualifies as a High vulnerabilty.
Note: While the contract can be upgraded/updated to mitigate this issue or by the protocol team minting xAlgo tokens to recalibrate total_active_stake, without a fix, the problem will reoccur mutiple times down the line as the accounting debt will keep catching up, especially under certain conditions or with specific user behaviors.
References
https://github.com/Folks-Finance/algo-liquid-staking-contracts/blob/main/contracts/xalgo/consensus_v2.py
Proof of Concept
Please add the describe block in the project test file