# InitcodeFactoryFees: fake token1 drains ETH via collectFees

InitcodeFactoryFees.collectFees trusted token1 as WETH without checking, letting a fake token with no-op withdraw() steal real ETH from the Factory.

- Payout: Real-world hack
- Severity: high
- Program: InitcodeFactoryFees
- Credited: DeFiHackLabs
- Disclosed: 2025-06-28
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2025-06/InitcodeFactoryFees_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20250628-initcodefactoryfees
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high technique:chained vuln:access-control vuln:business-logic

**Target:** Factory contract collectFees function
**Root cause:** collectFees authorizes caller via token0.creator() and assumes token1 is WETH without verifying token1 == WETH. A fake token1 can implement a no-op withdraw(), causing the Factory to send real ETH to the fake token creator.
**Steps:**
1. Deploy two fake ERC20 tokens (FakeCreatorToken) with a withdraw() no-op and a balanceOf that returns inflated value when account is the victim Factory.
2. Sort tokens by address to determine token0 and token1.
3. Approve PositionManager and SwapRouter for both tokens.
4. Call createAndInitializePoolIfNecessary(token0, token1, 500, SQRT_PRICE_1_1) to create a Uniswap V3 pool.
5. Mint a full-range LP position via PositionManager.mint with recipient = VICTIM_FACTORY, amount0Desired = amount1Desired = 100 ether.
6. Swap token1 for token0 via SwapRouter.exactInputSingle with recipient = VICTIM_FACTORY, amountIn = 10 ether, to generate fake fees.
7. Call VICTIM_FACTORY.collectFees(tokenId) to trigger ETH transfer to the fake token creator (attacker).
8. Transfer profit to attacker.
**Exploitation:** Attacker deployed fake tokens, created a Uniswap V3 pool, minted a fee-earning NFT position to the victim Factory, generated fake token fees, then called Factory.collectFees(tokenId). The fake token reported an inflated victim balance, and its no-op withdraw() allowed the Factory to send real ETH to the attacker.
**Insight:** When a contract assumes a specific token (e.g., WETH) without verifying its address, an attacker can supply a malicious token that mimics the expected interface but with no-op or inflated behavior to extract real funds.
