# GDC: chained token bugs drain 35 BNB from pair

GDC token's extcodesize-only EOA gate, buy-as-remove misclassification, burn-vs-input mismatch, and reentrant tax-free exit let an attacker drain ~35.34 BNB from the GDC/WBNB pair.

- Payout: Real-world hack
- Severity: high
- Program: GDC
- Credited: DeFiHackLabs
- Disclosed: 2026-09-11
- Write-up quality: 8/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2026-09/GDC_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20260911-gdc
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:business-logic vuln:fee-bypass vuln:flash-loan vuln:reentrancy

**Target:** GDCToken ERC20 and GDC/WBNB PancakeV2 pair on BNB Chain
**Root cause:** GDCToken's isContract() uses extcodesize only, so a helper depositing in its constructor passes the EOA gate; _update then misclassifies any pair->deposited-address transfer as a remove (skipping BuyingProhibited), and _burnSellAgainstPair burns the full nominal sell amount from the pair reserve while only 30% is forwarded, collapsing the GDC reserve.
**Steps:**
1. Deploy helper d1: in its constructor call GDC with 0.1 BNB (extcodesize==0 so isContract()==false), setting addLiquidityUnlockTime[d1]>0 and hasDeposited.
2. Deploy helper f95: bindReferrer(d1), then deposit 0.1 BNB in constructor so effectiveReferralCount[d1]=1 and d1 becomes f95's level-1 referrer.
3. Take a Moolah flash loan of WBNB to fund the hub.
4. Window 1: hub buys 221.24M GDC out to f95 via raw pair.swap (bug 2). f95 sells bag-1 GDC; swapSellAward runs with swapping==true and pays d1 the level-1 referral via _sendBNB raw call.
5. Inside d1's receive() callback, d1 spends 365.56 WBNB to buy a 1.63e9 GDC bag out of the pair (bug 2), loading the pair with WBNB.
6. After f95's sell returns, _burnSellAgainstPair has burned the full nominal amount from the pair reserve (bug 3); hub swaps f95's leftover ~66.37M GDC in the pair for 230.83 WBNB.
7. Window 2: f95 sells its last 1 GDC -> d1 callback -> d1 dumps its whole 1.63e9 bag straight into the pair and swaps out 176.77 WBNB to the hub (bug 4 + collapsed reserve).
8. Repay flash loan; net ~35.34 WBNB remains with attacker.
**How found:** Source review of GDCToken: noticed isContract() is extcodesize-only (constructor bypass), that _update flags any pair->deposited-address transfer as isRemove, that _burnSellAgainstPair burns full nominal while only 30% is forwarded, and that swapping==true skips the taxed branch while _sendBNB makes a raw call to the inviter mid-body.
**Exploitation:** One Moolah flash loan funds working capital; two reentrancy windows via referral _sendBNB callbacks let the attacker buy GDC out of the pair tax-free (deposit-record misclassification) and dump a pre-bought bag tax-free while swapping==true, against a reserve collapsed by the burn-vs-input mismatch. Profit is the WBNB backing of the 221.24M GDC burned out of the pair for free, captured across both dumps.
**Escalation:** Chained four independent token bugs (EOA gate, buy-as-remove, burn mismatch, reentrant tax-free exit) so that each individually loss-making leg nets positive together.
**Insight:** When a token gates EOA-only behavior with extcodesize, deposit from a constructor to bypass it; then look for state flags (swapping, isRemove) that are set for a whole function body and for raw .call value transfers to user-controlled addresses inside that window - those are reentrancy entry points into the untaxed path.
