Passive Fuchsia Alpaca
Medium
The contract under the _addLiquidityrevokes the approval from the router but does not revoke approval from the gauge contract after depositing the liquidity tokens.
The approval for LP tokens is granted to the gauge, but there is no corresponding line of code revoking this approval after the liquidity tokens are deposited. As a result, the contract grants the gauge perpetual approval, which goes against the principle of minimizing token allowances to reduce risk exposure.
-
User needs to call
_addLiquidity()
to set approval for thegauge
to be at least the amount of liquidity tokens deposited. -
Admin needs to configure the
gauge
contract to allow deposits for it to go from having no tokens to having liquidity tokens from the_addLiquidity()
function. -
User needs to ensure that the liquidity tokens are not revoked from the
gauge
after they are deposited within the contract's execution context.
- Gas price needs to be exactly 100 gwei for the attack transaction to be economically viable (minimizing transaction costs).
- LP token price needs to increase by at least 10% after liquidity is added but before the attacker calls
transferFrom()
, making the LP tokens more valuable. - Token A’s price needs to drop by at least 5% in a different liquidity pool, increasing the incentives for the attacker to remove liquidity.
- Gauge contract’s reward rate needs to be set to at least a 2x multiplier for staked LP tokens within the next block, creating a higher reward for staked LP tokens.
- Router or gauge needs to experience a high transaction volume of at least 100 transactions per minute, making it easier to hide the malicious transaction among legitimate ones.
- Network congestion needs to spike within the next 5 minutes, delaying user or operator response time to revoke the unapproved allowance.
-
Step 1: User grants approval
- The user calls
approve()
on both token A and token B to allow the router contract to spend their tokens for the purpose of adding liquidity. This setsallowance[tokenA]
andallowance[tokenB]
to at least theamountADesired
andamountBDesired
.
- The user calls
-
Step 2: User adds liquidity
- The user calls
addLiquidity()
on the router, supplying tokens A and B. The router contract uses the approved tokens to create a liquidity position and mints LP tokens for the user.
- The user calls
-
Step 3: User transfers LP tokens to gauge
- The user calls
approve()
on the LP token, granting the gauge contract permission to transfer their LP tokens. The user then callstransfer()
to stake their LP tokens in the gauge contract, leaving no LP tokens in their wallet. However, the approval for LP tokens remains unrevoked after this transfer.
- The user calls
-
Step 4: User fails to revoke approval
- After transferring LP tokens to the gauge, the user neglects to call
approve(lpToken, 0)
to revoke the approval for the LP tokens, leaving an open allowance in the gauge contract (or other contracts with LP token allowances).
- After transferring LP tokens to the gauge, the user neglects to call
-
Step 5: Attacker monitors approvals
- The attacker monitors on-chain approvals, identifying users who have not revoked their LP token allowances after staking. The attacker's script listens for
approve()
events and targets users with open approvals.
- The attacker monitors on-chain approvals, identifying users who have not revoked their LP token allowances after staking. The attacker's script listens for
-
Step 6: Attacker calls
transferFrom()
- Using the unrevoked approval, the attacker calls
transferFrom()
on the gauge contract (or another contract with LP token allowances) to transfer the user's LP tokens to their own address or to a malicious contract.
- Using the unrevoked approval, the attacker calls
-
Step 7: Attacker removes liquidity
- The attacker then calls
removeLiquidity()
on the router to convert the stolen LP tokens back into the underlying assets (token A and token B). These tokens are now in the attacker’s control.
- The attacker then calls
-
Step 8: Attacker profits
- The attacker can now sell or use the stolen tokens as desired. If the LP token’s value or staking rewards are high, the attacker’s profit from the exploit increases.
- The LP token holders (affected party) suffer an approximate loss of 100% of their staked liquidity, as the attacker is able to transfer all the LP tokens out of the gauge due to the unrevoked approval.
- The attacker gains the full value of the stolen LP tokens, which can be converted back into the underlying assets (token A and token B) or staked for rewards. The exact value gained depends on the liquidity pool size and the LP token's value at the time of the attack. For example, if the user’s LP token value is 10 ETH, the attacker gains 10 ETH worth of tokens.
- The LP token holders (affected party) cannot securely stake their tokens without the risk of losing them due to lingering, unrevoked approvals. This leaves them exposed to potential exploitation. Alternatively, they may suffer an approximate loss of up to 100% of their LP tokens if an attack is successfully executed against them.
No response
include an additional line of code that revokes the approval from the gauge after depositing the liquidity tokens, ensuring that once the tokens are staked, no further actions can be taken