contract AmmGauge don't protect itself against reentrancy attack with lock mechanism and in the middle of some logics in unstakeFor() and stakeFor() about contract token balances it makes some external calls to ammToken, if those calls somehow make another calls to attacker controlled addresses then attacker can reenter AmmGauge with stakeFor and change contract balances and then contract will increase attacker balances in contract multiple times for one deposit.
As you can see it store the value of IERC20(ammToken).balanceOf(address(this)); in oldBal and then tries to transferees user ammTokens but if that external call make some calls to attacker controlled address then attacker can reenter this contract by calling stakeFor() and stake some amount for himself and increase contract ammToken balance. then execution will return to original call and contract will check new balance of ammToken in line: uint256 newBal = IERC20(ammToken).balanceOf(address(this)); and because balance of contract increase because of multiple deposits then it's gonna count those deposits(which contract increase balance for them) as new deposits and increase attacker balance higher that it supposed to. these are the steps:
attacker calls stakeFor()
AmmGauge stores the balance of its address in ammToken and calls ammToken.transferFrom(attacker, AmmGauge address, amount)
ammToken is not ordinary ERC20 token and it transfers tokens is may call attacker addresses.
attacker call stakeFor() with bigAmount and normal execution will happen and balance of attacker in AmmGauge will increase by bigAmount
stakeFor code will then try to get new balances of AmmGauge in ammToken and then subtract oldBal from it to calculated deposited amount, but because new balance is amount + bigAmount is higher than oldBal then code will increase attacker balance by amount + bigAmount and code is counting bigAmount deposit multiple times.
the steps for unStakeFor() is similar. This is simple reentrancy attack and attacker can easily drain contract funds and the only requirments is that ammToken makes some external call to _from address
Lines of code
https://github.com/code-423n4/2022-05-backd/blob/2a5664d35cde5b036074edef3c1369b984d10010/protocol/contracts/tokenomics/AmmGauge.sol#L103-L116 https://github.com/code-423n4/2022-05-backd/blob/2a5664d35cde5b036074edef3c1369b984d10010/protocol/contracts/tokenomics/AmmGauge.sol#L124-L138
Vulnerability details
Impact
contract
AmmGauge
don't protect itself against reentrancy attack with lock mechanism and in the middle of some logics inunstakeFor()
andstakeFor()
about contract token balances it makes some external calls toammToken
, if those calls somehow make another calls to attacker controlled addresses then attacker can reenterAmmGauge
withstakeFor
and change contract balances and then contract will increase attacker balances in contract multiple times for one deposit.Proof of Concept
This is
stakeFor
code inAmmGauge
:As you can see it store the value of
IERC20(ammToken).balanceOf(address(this));
inoldBal
and then tries to transferees userammTokens
but if that external call make some calls to attacker controlled address then attacker can reenter this contract by callingstakeFor()
and stake some amount for himself and increase contractammToken
balance. then execution will return to original call and contract will check new balance ofammToken
in line:uint256 newBal = IERC20(ammToken).balanceOf(address(this));
and because balance of contract increase because of multiple deposits then it's gonna count those deposits(which contract increase balance for them) as new deposits and increase attacker balance higher that it supposed to. these are the steps:stakeFor()
AmmGauge
stores the balance of its address inammToken
and callsammToken.transferFrom(attacker, AmmGauge address, amount)
ammToken
is not ordinaryERC20
token and it transfers tokens is may call attacker addresses.stakeFor()
withbigAmount
and normal execution will happen and balance of attacker inAmmGauge
will increase bybigAmount
stakeFor
code will then try to get new balances ofAmmGauge
inammToken
and then subtractoldBal
from it to calculated deposited amount, but because new balance isamount + bigAmount
is higher thanoldBal
then code will increase attacker balance byamount + bigAmount
and code is countingbigAmount
deposit multiple times.the steps for
unStakeFor()
is similar. This is simple reentrancy attack and attacker can easily drain contract funds and the only requirments is thatammToken
makes some external call to_from
addressTools Used
VIM
Recommended Mitigation Steps
add
lock
mechanism to contract