paradigm CTF 2022 solution & writeup, part 1

Silas
Cyberight Capital
Published in
3 min readSep 15, 2022

--

Rescue

The challenge description is “I accidentally sent some WETH to a contract, can you help me?”.

After downloading rescue.zip, we could 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.

Local Environment Setup & Exp

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:

  1. WETH, since we just have eth, we need some WETH to swap other token we need.
  2. tokenIn, the token we use to swap for lp token, and I choosed USDT (any erc20 token is fine except weth)
  3. 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
  1. we need to get some weth
  2. swap 1 weth for some usdt, and 15 weth for usdc
  3. transfer all of usdc to mcHelper
  4. swap usdt by calling swapTokenForPoolToken.

Congratulation! You got it.

Tips

  1. I used 10 as poolId and usdc as tokenIn at first, But I got “UniswapV2: TRANSFER_FAILED” at 0x5326f93507bd2629d3126bec9eef7124d93fcb64.

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 update.

--

--