All incidents

GPT Public LP-Burn Exploit

Share
May 24, 2023 16:00 UTCAttackLoss: 58,520.32 USDTPending manual check2 exploit txWindow: 3s
Estimated Impact
58,520.32 USDT
Label
Attack
Exploit Tx
2
Addresses
2
Attack Window
3s
May 24, 2023 16:00 UTC → May 24, 2023 16:00 UTC

Exploit Transactions

TX 1BSC
0xb77cb34cd01204bdad930d8c172af12462eef58dea16199185b77147d6533391
May 24, 2023 16:00 UTCExplorer
TX 2BSC
0x3cf0da565b2e5eb2ac7fc5b6814b738c4b164e44066a59f6c40cb1deb2305437
May 24, 2023 16:00 UTCExplorer

Victim Addresses

0xa1679abef5cd376cc9a1c4c2868acf52e08ec1b3BSC
0x77a684943aa033e2e9330f12d4a1334986bca3efBSC

Loss Breakdown

58,520.32USDT

Similar Incidents

Root Cause Analysis

GPT Public LP-Burn Exploit

1. Incident Overview TL;DR

On BSC block 28494869, attacker EOA 0x054a3574d8082112575843dd944ff42c58dda38d used helper contract 0x2a9ab219d4f82fbd9f522974bd82fe92c376f376 to borrow public DODO USDT liquidity, buy GPT, and then repeatedly transfer GPT into the GPT/USDT Pancake pair 0x77a684943aa033e2e9330f12d4a1334986bca3ef. Each transfer triggered GPT tokenomics inside the victim token 0xa1679abef5cd376cc9a1c4c2868acf52e08ec1b3, which spent GPT-owned LP, removed liquidity from the same pair, and recycled the withdrawn USDT back through PancakeRouter.

The exploit is an ACT vulnerability in GPT itself. GPT exposed protocol-owned LP management to any public transfer into its registered swap pair. Because GPT owned almost the entire pair LP supply before the exploit, repeated public triggers deterministically moved the pool price in the attacker's favor and let the attacker finish with 42973440541460242234213 USDT in the helper, later withdrawn in block 28494870.

2. Key Background

GPT is unverified, but its runtime interface and storage-backed configuration are observable on-chain. At block 28494868, swapLPAddr() returned the GPT/USDT pair 0x77a684943aa033e2e9330f12d4a1334986bca3ef, uniswapV2Router() returned PancakeRouter 0x10ed43c718714eb63d5aa57b78b54704e256024e, rate() returned 500, rateAddr() returned 0x4af79bfb0a574cfad0f973f3bcd26dbf6c8a4d84, and _swapPairList(pair) returned true. Those values establish that GPT had an internal notion of a privileged swap pair and a router-driven maintenance path.

The economic setup was unusually fragile because GPT itself owned 5424685647360298642032 of the pair's 5436144099564075293128 total LP tokens immediately before the exploit. The pair simultaneously held 307766931135248784246439 USDT and 96563091995071616250 GPT. That meant any public GPT branch that could spend GPT-owned LP was effectively allowed to move nearly the entire pool.

The attacker did not need private keys, privileged contracts, or hidden order flow. The helper contract only embedded five public DODO pools, used public flash loans, called public Pancake contracts, and interacted with publicly queryable GPT state.

3. Vulnerability Analysis & Root Cause Summary

The vulnerable component is the GPT token transfer path, not PancakePair. The pair behaves like a standard PancakeSwap V2 pair, while GPT adds side effects when tokens are transferred into the registered swap pair. Independent validation confirms that GPT's transfer-side logic is not a plain fee-on-transfer path: it invokes PancakeRouter removeLiquidity and swapTokensForExactTokens from inside the victim token when the recipient is swapLPAddr().

The broken invariant is straightforward: a public user transfer must not let an arbitrary trader spend GPT-owned LP, remove liquidity from the GPT/USDT pair, and push the withdrawn reserve asset back into the same pair. GPT violated that invariant by coupling user-triggerable pair transfers to treasury-style LP management. Because GPT itself held almost all LP, every trigger had large price impact.

The concrete breakpoint is the victim-side branch entered when the attacker helper calls GPT.transferFrom(helper, swapLPAddr(), amount). Inside that branch, GPT calls PancakeRouter selector 0xbaa2abde (removeLiquidity(address,address,uint256,uint256,uint256,address,uint256)) with tokenA=USDT, tokenB=GPT, and to=GPT, then calls selector 0x8803dbee (swapTokensForExactTokens(uint256,uint256,address[],address,uint256)) with path [USDT, GPT] and to=address(0). Those calls spend GPT-owned LP, force a PancakePair.burn(GPT), and route the withdrawn USDT back through the same GPT/USDT pair.

4. Detailed Root Cause Analysis

Victim-side selector extraction and traced behavior reconstruct the GPT branch with code-level precision:

GPT runtime selectors:
0x23b872dd -> transferFrom(address,address,uint256)
0x18887b70 -> swapLPAddr()
0x1694505e -> uniswapV2Router()
0x2c4e722e -> rate()
0xe9c3fcb9 -> rateAddr()
0x3c11100d -> _swapPairList(address)

Representative victim-side calls from the exploit call-trace show the exact sequence:

helper -> GPT    0x23b872dd  transferFrom(helper, pair, 0.5 GPT)
GPT    -> router 0xbaa2abde  removeLiquidity(USDT, GPT, lpAmount, 10, 10, GPT, deadline)
router -> pair   0x23b872dd  transferFrom(GPT, pair, lpAmount)
router -> pair   0x89afcb44  burn(GPT)
GPT    -> router 0x8803dbee  swapTokensForExactTokens(gptBurnAmount, usdtMax, [USDT,GPT], address(0), deadline)
helper -> pair   0xbc25cf77  skim(helper)

That sequence repeated 50 times across transfer sizes 0.5, 0.6, 0.72, 0.864, and 1.0368 GPT, ten transfers per bucket. The attacker's helper was responsible for orchestration only: it flash-borrowed from five DODO pools, initiated GPT transfers into the pair, collected excess GPT via skim, and performed the final sale. The victim-side harm occurs inside GPT, where public pair transfers spend GPT-owned LP and force same-pair buybacks.

The collected balance diff for tx 0xb77cb34cd01204bdad930d8c172af12462eef58dea16199185b77147d6533391 matches that mechanism exactly. The GPT contract's LP balance in the pair fell from 5424685647360298642032 to 3717956378167791658840, a decrease of 1706729269192506983192 LP tokens. The pair's USDT balance fell from 307766931135248784246439 to 249246609157484196792406, a loss of 58520321977764587454033 USDT. At the same time, GPT itself accumulated 15546881436304345219820 USDT and the attacker helper accumulated 42973440541460242234213 USDT.

The attacker then withdrew that helper-side profit in tx 0x3cf0da565b2e5eb2ac7fc5b6814b738c4b164e44066a59f6c40cb1deb2305437, which transferred exactly 42973440541460242234213 USDT from helper 0x2a9ab219d4f82fbd9f522974bd82fe92c376f376 to EOA 0x054a3574d8082112575843dd944ff42c58dda38d.

5. Adversary Flow Analysis

The adversary sequence is fully public and deterministic.

  1. In block 28494697, EOA 0x054a3574d8082112575843dd944ff42c58dda38d deployed helper contract 0x2a9ab219d4f82fbd9f522974bd82fe92c376f376 in tx 0x28c73e6ba362da36809016096ee2054093a1b9133de4a691365990fd30ec578b. The creation bytecode embeds the five DODO pools later used for flash borrowing.
  2. In block 28494869, the helper entrypoint 0x93f2976b(bytes) borrowed USDT from five public DODO pools: 0xfeafe253802b77456b4627f8c2306a9cebb5d681, 0x9ad32e3054268b849b84a8dbcc7c8f7c52e4e69a, 0x26d0c625e5f5d6de034495fbde1f6e9377185618, 0x6098a5638d8d7e9ed2f952d35b2b67c34ec6b476, and 0x81917eb96b397dfb1c6000d28a5bc08c0f05fc1d.
  3. The helper bought GPT with 100000000000000000000000 USDT through PancakeRouter, then repeatedly called GPT.transferFrom(helper, pair, amount) and pair.skim(helper). Each transfer triggered the victim-side LP-burn and same-pair buyback branch described above.
  4. After the loop, the helper sold 20594029021370009799 GPT back into the pair for 142973440541460242234213 USDT, repaid all flash loans, and ended the exploit transaction with 42973440541460242234213 USDT.
  5. In block 28494870, the attacker EOA called helper selector 0xf6e01ecd(address) to withdraw the helper's USDT balance to the EOA.

The profit accounting is also deterministic in the reference asset. Before the two-transaction exploit sequence, the adversary cluster held value equivalent to 50724862811883826509077 USDT. After the withdrawal transaction, it held 93681140124138038535997 USDT equivalent. Using PancakeRouter's block-28494868 WBNB/USDT quote to convert gas, the sequence paid 17163229206030207293 USDT-equivalent in fees and realized a net value delta of 42956277312254212026920 USDT.

6. Impact & Losses

The victim pool lost 58520321977764587454033 USDT in smallest units, with decimal=18. The attacker realized 42973440541460242234213 USDT directly, while the remaining 15546881436304345219820 USDT stayed inside the GPT token contract as a byproduct of the tokenomics path.

This was not a harmless arbitrage. Public GPT transfers were able to consume protocol-owned LP inventory and deplete the GPT/USDT pair itself. The affected parties were GPT holders and liquidity backed by pair 0x77a684943aa033e2e9330f12d4a1334986bca3ef.

7. References

  • Exploit transaction: 0xb77cb34cd01204bdad930d8c172af12462eef58dea16199185b77147d6533391
  • Profit withdrawal transaction: 0x3cf0da565b2e5eb2ac7fc5b6814b738c4b164e44066a59f6c40cb1deb2305437
  • Helper deployment transaction: 0x28c73e6ba362da36809016096ee2054093a1b9133de4a691365990fd30ec578b
  • Victim GPT token: 0xa1679abef5cd376cc9a1c4c2868acf52e08ec1b3
  • Victim GPT/USDT pair: 0x77a684943aa033e2e9330f12d4a1334986bca3ef
  • USDT token: 0x55d398326f99059ff775485246999027b3197955
  • PancakeRouter: 0x10ed43c718714eb63d5aa57b78b54704e256024e
  • Evidence artifacts: exploit trace, balance diff, victim-side call-trace reconstruction, GPT runtime selector dump, attacker helper selector dump, and deterministic profit accounting under /workspace/session/artifacts/collector/seed/56/0xb77cb34cd01204bdad930d8c172af12462eef58dea16199185b77147d6533391/ and /workspace/session/artifacts/auditor/iter_1/