Skip to content
#16 largest Reentrancy 2022

The Fei Protocol / Rari Capital Fuse hack — $80M lost

Loss$80M
Date30 Apr 2022
ChainEthereum
Failure classReentrancy
In assetsthe protocol shut down after
Targetisolated lending pools
1

What happened

Fuse's cEther sent the borrowed ETH to the borrower before writing the loan into storage — the classic violation of checks-effects-interactions. The ETH transfer hands control to the borrower's contract, and from inside that callback the attacker called exitMarket(). Since his borrow had not been recorded yet, the collateral check saw an account with no debt and released 100% of his collateral. He kept the loan and the collateral, and repeated it across pool after pool.

2

How the attack ran

  1. Deposit collateral, then borrowborrowFresh sends the ETH out first
  2. The ETH hits your receive()Control is yours before the loan is written to storage
  3. exitMarket() sees no debtA different function, sharing state but not a lock
  4. Collateral back, loan keptRepeated pool after pool — $80M
3

The code

CToken.sol — borrowFresh, effects after interaction
function borrowFresh(address payable borrower, uint borrowAmount) internal returns (uint) {
    ...
    doTransferOut(borrower, borrowAmount);          // ❶ external call FIRST
    accountBorrows[borrower].principal = vars.accountBorrowsNew;   // ❷ state after
    accountBorrows[borrower].interestIndex = borrowIndex;
    totalBorrows = vars.totalBorrowsNew;
    ...
}

// inside the attacker's receive():
comptroller.exitMarket(cToken);
    → "does this account have outstanding borrows?"
    → not written yet → no → collateral released

// ✅ two independent fixes, and you want both:
//    1. move doTransferOut() to the LAST line (checks-effects-interactions)
//    2. a shared reentrancy guard that also covers exitMarket()
4

What would have caught it

What an audit looks for: reentrancy is not only "the same function called twice." Cross-function reentrancy — leaving through one function and coming back through another that reads the state you have not written yet — is the version that still gets people. Map which functions share state, and make sure they share a lock.
6

Sources

Every figure on this page comes from the post-mortems above, not from us. Losses are US dollars at the time of the incident.

Check your own contract for this

Reentrancy is one of the 203 classes the SaferICO scanner checks for. It will not review your signing process — but it will read your Solidity.

Run the scanner See how it is attacked Read the docs