# Rari Capital/Fei: reentrancy in exitMarket drains fETH pool

Reentrancy in Rari/Fei's Comptroller exitMarket during ETH borrow let attacker double-count collateral and drain fETH pool via Balancer flash loan.

- Payout: Real-world hack
- Severity: critical
- Program: Rari Capital/Fei Protocol
- Analysis by: DeFiHackLabs
- Disclosed: 2022-04-30
- Write-up quality: 6/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2022-04/Rari_exp.sol
- Library page: http://bugbountylibrary.com/r/dhl%3A20220430-rari
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:critical tech:evm tech:solidity vuln:accounting-error vuln:flash-loan vuln:reentrancy

**Target:** Rari Fuse Comptroller / fETH and fUSDC cToken markets
**Root cause:** Comptroller.exitMarket was callable reentrantly from the ETH receive hook during fETH.borrow, letting the attacker unwind the fUSDC collateral check while the borrow was still in flight, so the protocol released ETH without properly accounting the collateral.
**Steps:**
1. Fork mainnet at block 14684813; send contract ETH to blackhole.
2. Balancer flash loan `150_000_000 * 10**6` USDC via `vault.flashLoan`.
3. In `receiveFlashLoan`: approve fUSDC, `fusdc_127.accrueInterest()`, `fusdc_127.mint(15_000_000_000_000)`.
4. `rari_Comptroller.enterMarkets([fusdc_127])` then `fETH_127.borrow(1977 ether)`.
5. In `receive()` hook, call `rari_Comptroller.exitMarket(address(fusdc_127))` reentrantly.
6. `fusdc_127.redeemUnderlying(15_000_000_000_000)` to recover USDC, repay Balancer.
**Exploitation:** Attacker flash-borrowed 150M USDC from Balancer, minted fUSDC as collateral, entered the market, borrowed ~1977 ETH from fETH, and used the ETH receive callback to reenter exitMarket and unwind the fUSDC position before the borrow settled, walking away with the ETH while repaying the flash loan.
**Escalation:** Reentrancy via ETH receive hook turned a normal borrow into an uncollateralized drain of the fETH pool.
**Insight:** cToken ETH markets that call out to the borrower via receive() during borrow are reentrancy surfaces against Comptroller accounting; check whether exitMarket/redeem can be invoked mid-borrow to desync collateral checks.

