# NOON: public _transfer drains Uniswap pair reserves

NOON token's _transfer was externally callable, letting an attacker drain the NO/WETH pair and swap for ~$2K WETH.

- Payout: Real-world hack
- Severity: high
- Program: NOON (NO)
- Analysis by: DeFiHackLabs
- Disclosed: 2023-05-29
- Write-up quality: 6/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2023-05/NOON_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20230529-noon
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** ERC20 token contract and Uniswap V2 NO/WETH pair
**Root cause:** The internal _transfer function was declared external/public, so anyone could move tokens out of any address, including the Uniswap pair. Token also had a non-standard transfer implementation.
**Steps:**
1. Call `NO._transfer(address(Pair), address(this), NO.balanceOf(address(Pair)) - 1)` to move nearly all NO out of the pair to the attacker.
2. Call `Pair.sync()` to update reserves to the drained state.
3. Call `NO.transfer(address(Pair), NO.balanceOf(address(this)))` to deposit the stolen NO back into the pair.
4. Read reserves via `Pair.getReserves()` and compute `Router.getAmountOut(NO.balanceOf(address(Pair)) - 1, NOReserve, WETHReserve)`.
5. Call `Pair.swap(0, amount1Out, address(this), "")` to extract WETH.
**How found:** Closed-source contract; analysis inferred wrong _transfer visibility and non-standard ERC20 implementation from on-chain behavior.
**Exploitation:** Attacker used the publicly callable _transfer to empty the pair's NO balance, synced reserves, re-deposited NO, then swapped to drain WETH from the pair for ~$2K profit.
**Insight:** On closed-source or unverified tokens, enumerate externally callable functions that look internal (leading underscore, transfer/mint/burn helpers) and check whether they let you move balances from arbitrary addresses like a pool.

