The challenge description is “I accidentally sent some WETH to a contract, can you help me?”.
After downloading rescue.zip, we cloud see that there are three solidity files here, MasterChefHelper, Setup and UniswapV2Like. Looks like this is a challenge based on sushi :P.
MasterChefHelper.sol
We could see the address of masterChef and router in source code, and we’re sure about it’s a forking mainnet challenge after searching these addresses in etherscan.
how to pass
the flag of sucess is that we need to pass the “isSolved()” check. So we need to clean up weth in mcHelper.
Setup contract transfers 10 ether to MasterChefHelper after create it
there are three function defined in mcHelper, but only one of them is external. So it’s clear that we need to break through “swapTokenForPoolToken” function.
mcHelper.sol
The logic in swapTokenForPoolToken is :1. get lp token address by poolId, and get the two token address for lp pair.
2. approve token for router to swap/addLiquidity
3. tarnsfer tokenIn from msg.sender and swap half of amout to two kind of token from lp pair.
4. using the two kind of token from swap result to add liquidity
seems normal here. But, “_addLiquidity” is not just addLiquidity by calling router.addLiquidity.
_addLiquidity
We could find that when calling router.addLiquidity(), the function will use “balanceOf(address(this))” as amount instead of the swap result. So, It’ll drain out the weth of mcHelper when we’re adding liquidity with weth as one of the token.
And if you’re familiar with Uniswap, you should know that you just need to provide with enough token1 when you want to addLiquidity with minToken0Amount.
Since the challenge is using contract deployed on mainnet, we need to fork mainnet and simulate a firing range environment. I choosed 14990000 block number as baseline in hardhat
fork mainnet with hardhat
As said before, we need three kind of token:
WETH, since we just have eth, we need some WETH to swap other token we need.
tokenIn, the token we use to swap for lp token, and I choosed USDT (any erc20 token is fine except weth)
another token used for make pair with weth, and I choosed USDC(why USDC? I’ll explain later)
And we need to find a lpPool which conclude weth. Luckily, the first pool is WETH/USDC with poolID 1.
Finally, the solution is here.
EOA solution
we need to get some weth
swap 1 weth for some usdt, and 15 weth for usdc
transfer all of usdc to mcHelper
swap usdt by calling swapTokenForPoolToken.
Congratulation! You got it.
After viewing the contract source code, I found that there is a _safeTransfer function, it’ll check whether the token transfered has a interface/selector as same as bytes4(keccak256(bytes(‘transfer(address,uint256)’))). But in pool 10, the snx token only support transfer(address,uint), that’s the reason why transfer failed.
You could get full code and repo in https://github.com/SilasZhr/paradigm-ctf-2022-solution, I’ll keep updating and contributions are welcome.
twitter: https://twitter.com/SilasYayoi
github: https://github.com/SilasZhr