Lucene search

K
code423n4Code4renaCODE423N4:2022-02-ANCHOR-FINDINGS-ISSUES-24
HistoryMar 09, 2022 - 12:00 a.m.

bEth Rewards May Be Depleted By Flashloans or Whales

2022-03-0900:00:00
Code4rena
github.com
6

Lines of code

Vulnerability details

Impact

Rewards are dispersed to users as a percentage of the user’s balance vs total balance (of bEth). Rewards are accumulated each time a user calls execute_decrease_balance(), execute_increase_balance() or execute_claim_rewards() as these functions will in term call update_global_index() before any balance adjustments.

There is an attack vector where a user may arbitrarily increase their bEth balance either by flashloan of purchase of wormhole eth, then converting it bEth via anchor_beth_converter. The attacker may then have a significant portion of the total balance. This use can then call the contract which pushes rewards to anchor_beth_reward this will increase the reward_balance of the contract. Since the attacker controls a large portion of the bEth balance they will receive a higher portion of the rewards. They may then convert the bEth back to wormhole eth and close their position.

The result is the user has not contributed anything to the system but still gained a high portion of the rewards.

Proof of Concept

The logic for update_global_index() does not account for users suddenly increasing the balance.

fn update_global_index(state: &mut State, reward_balance: Uint128) -> StdResult<()> {
    // Zero staking balance check
    if state.total_balance.is_zero() {
        // nothing balance, skip update
        return Ok(());
    }

    // No change check
    if state.prev_reward_balance == reward_balance {
        // balance didnt change, skip update
        return Ok(());
    }

    // claimed_rewards = current_balance - prev_balance;
    let claimed_rewards = reward_balance.checked_sub(state.prev_reward_balance)?;

    // update state
    state.prev_reward_balance = reward_balance;
    // global_index += claimed_rewards / total_balance;
    state.global_index = decimal_summation_in_256(
        state.global_index,
        Decimal::from_ratio(claimed_rewards, state.total_balance),
    );

    Ok(())
}

Steps:
a) Flashloan (or buy) a significant portion of wormhole Eth
b) Convert to Anchor bEth via anchor_beth_converter
c) Push rewards on anchor_beth_rewards
d) anchor_beth_rewards::execute_claim_rewards()
e) Convert Anchor bEth to wormhole Eth
f) Repay flashloan (or sell wormhole eth)

Recommended Mitigation Steps

There are multiple possible mitigations to this issue.

First, option is to only allow the global_index to be updated once per block. In addition to this, cap the amount of rewards that may be paid per block (keeping the remaining rewards for the next block). This would reduce the effectiveness of the attack and limit the amount they may earn per block.

Another option is to induce a wait time before the user may begin earning rewards. However, this would require a second transaction from the user to begin collection their reward which may hurt UX.


The text was updated successfully, but these errors were encountered:

All reactions