# Biswap V3Migrator: fake pair/token params drain victim LP

Biswap V3Migrator trusted caller-supplied pair/token params, letting an attacker burn a victim's LP and mint fake-token liquidity to drain ~$72k.

- Payout: Real-world hack
- Severity: high
- Program: Biswap
- Analysis by: DeFiHackLabs
- Disclosed: 2023-06-30
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2023-06/Biswap_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20230630-biswap
- 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:** V3Migrator contract on BSC
**Root cause:** migrate() accepts arbitrary pair, token0/token1 and recipient from the caller without verifying they match the actual LP pair or that msg.sender owns the liquidity, so a fake pair's burn() return values are trusted as real token amounts.
**Steps:**
1. Deploy FakeToken0/FakeToken1 and FakePair (ERC20 with burn() returning attacker-set token0Amount/token1Amount).
2. Call `biswapV3.newPool(fakeToken1, fakeToken0, 150, 1)` and transfer 1e9*1e18 of each fake token to the migrator.
3. Read victim's LP balance via `pairToMigrate.balanceOf(victimAddress)`.
4. Call `migrator.migrate(params)` with pair=victim pair, liquidityToMigrate=victim balance, token0=fakeToken1, token1=fakeToken0, recipient=victimAddress — burns victim LP and adds fake-token liquidity.
5. Read real token0/token1 balances now held by migrator; call `fakePair.update(token0Balance, token1Balance)`.
6. Transfer fakePair to self, approve migrator, call `migrator.migrate(params2)` with pair=fakePair, token0/token1=real tokens, recipient=self — fakePair.burn() returns the real balances, migrator pays them out.
**Exploitation:** Attacker needs no capital: uses victim's existing LP balance as liquidityToMigrate, seeds migrator with worthless fake tokens, then a second migrate() against a fake pair whose burn() returns the real token amounts the migrator accumulated, redirecting them to the attacker.
**Insight:** Migrator-style contracts that take pair/token addresses as calldata and trust pair.burn() return values are drainable: check whether the contract verifies the pair matches token0/token1 and that the caller owns the LP, then substitute a fake pair returning arbitrary amounts.

