LBP exploit on BSC ~145k drained
Root cause: token balanceOf(pair) was computed dynamically from pair.getReserves() instead of returning the raw balance.
Calling sync() overwrites reserve1 with that manipulated value.
Exec flow:
- flashloan 2.2M USDT from PCS V3
- buy 500k USDT -> VICTIM
- donate USDT + VICTIM equal to reserves to the pair
- skim() recovers USDT, VICTIM stays trapped (balanceOf lies)
- dust + raw pair.swap() pulls back the trapped VICTIM
- dust + sync() reserve1 collapses from 1,262 to 3.79 VICTIM
- dump at the inflated price -> ~645K USDT out
- repay flash, walk away with ~145K
TUB/BSC drained for ~45 BNB
single tx, zero capital
Pledge contract claim function (0xb45c9928) takes reward amount as user input.
no validation. no accounting.
attacker staked 1 LP token, claimed 4.77×10²⁷ TUB
basically: tranfer(msg.sender, userInput)
⚡️JUDAO Postmortem ~227k exploit
Root cause: AMM reserve desync via token-side burn hooks
_update() runs two ‘drain hooks’ whenever a transfer touches PancakeV2 Pair.
isBurnPair -> burns JUDAO from pair
Mining hook -> burns again, then calls pair.sync()
sync() freezes a falsified reserve1 into pair storage while balance1 still carries the attacker just deposited JUDAO.
Exec flow:
- flashloan 2.29M USDT from Moolah
- router buy -> 5.47M JUDAO
- transfer() those JUDAO directly to the pair -> _update() burns ~3.02M out of reserves + sync()
- pair.swap(2.52M USDT, 0, …) directly
- repay Moolah, keep the delta
PancakeV2 computes amount1In = balance1 - reserve1
After the hook: reserve1 is artificialy low (post-burn) AND balance1 also dropped, but the swap K invariant is now solved against a fake skewed ratio.
Pair pays USDT that no longer exists in reserves.
NET: 36 BNB + 205,259 USDT to attacker EOA.
https://t.co/2JhMr35RXX