The Fees.sol
contract has one particular function whose goal is to accumulate the fees from peer-to-peer trading and ocasionally swap them for WETH which is then forwarded to the Staking.sol
contract which allows for users to stake TKN and accumulate rewards in WETH.
The vulnerability lies in the sellProfits()
function which is public allowing anyone to execute the fee conversion flow. The function makes an external call to a Uniswap pool for which there are a few hardcoded values: fees
, amountOutMinimum
and sqrtPriceLimitX96
. Fees is hardcoded to 3000 meaning that only pools with X_token/WETH and 3000 fee are eligible for the swap. The amountOutMinimum
is hardcoded to 0 which allows for unlimited slippage on the transaction. sellProfits
accepts only one parameter which is of type address and represents the token that will be used to be swapped for WETH as long as there is a balance of that token on the contract. The problem is that if such a pool does not exist an attacker could create a new pool with unfavourable conditions for the Fees.sol contract and execute the sellProfits
in order to essentially steal 99% of the funds of the contract. The mentioned attack can be executed within a single transaction and if necessary with the use of a flash-loan to exponentially increase the impact. Lets give a short example:
Assume there is no DAI/WETH pool with 3000 fee setting and there are 100k DAI in the Fees.sol
contract.
An attacker takes a flash loan or uses his own funds to create a DAI/WETH pool with 3000 fee where 1 Wei = 100k DAI
The attacker then proceeds to execute the sellProfits()
which will swap the 100k DAI stored in the contract for 1 Wei of WETH that will be forwarded to the staking contract
Attacker then can take out his liquidity from the pool and repay the flash loan if necessary making a significant profit on behalf of the protocol or other users
Theft of protocol/user funds
Manual review
Since it would be tricky to have a dynamic fee and calculate amountOutMin as multiple tokens can be used to be sold for WETH and considered profits, the best approach is to add AccessControl to the sellProfits()
function making it executable only by a company ran KeeperBot as example.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.