All incidents

Carson Pair Reserve Siphon

Share
Jul 26, 2023 17:35 UTCAttackLoss: 100,677.05 USDTPending manual check1 exploit txWindow: Atomic
Estimated Impact
100,677.05 USDT
Label
Attack
Exploit Tx
1
Addresses
1
Attack Window
Atomic
Jul 26, 2023 17:35 UTC → Jul 26, 2023 17:35 UTC

Exploit Transactions

TX 1BSC
0x37d921a6bb0ecdd8f1ec918d795f9c354727a3ff6b0dba98a512fceb9662a3ac
Jul 26, 2023 17:35 UTCExplorer

Victim Addresses

0xe0a3e0f7e4c789747eb29a212ea915a7a1e1ac69BSC

Loss Breakdown

100,677.05USDT

Similar Incidents

Root Cause Analysis

Carson Pair Reserve Siphon

1. Incident Overview TL;DR

On BSC block 30306325, transaction 0x37d921a6bb0ecdd8f1ec918d795f9c354727a3ff6b0dba98a512fceb9662a3ac exploited the Carson/USDT pair at 0xe0a3e0f7e4c789747eb29a212ea915a7a1e1ac69. An unprivileged EOA, 0x25bcbbb92c2ae9d0c6f4db814e46fd5c632e2bd3, called attacker contract 0x9cffc95e742d22c1446a3d22e656bb23835a38ac, sourced public USDT liquidity through DODO flash loans, bought Carson, and then sold Carson back into the pair in 51 chunks.

The root cause is a broken swap path in Carson's custom pair design. The pair prices Carson-to-USDT sells using the full Carson balance increase, but before it syncs reserves it calls helper 0xD092A69Dd806bd0f49E69739770784CDa896c07b and transfers 60% of that Carson from the pair balance itself to the dead address and the marketing address. That reserve shrink is not reflected in the quoted USDT output, so repeated sells become artificially profitable. The transaction drained 100677.049295746860016399 USDT net from LP-held liquidity and cost the sender exactly 56095692000000000 wei-BNB in gas.

2. Key Background

Carson does not use a vanilla PancakeSwap pair. Its pair contract is an unverified but bytecode-identifiable Uniswap-V2-style contract whose selector table includes swap(uint256,uint256,address,bytes), token0(), token1(), and factory(). On-chain reads tie the pair to Carson token 0x0acd5019edc8ff765517e2e691c5eef6f9c08830, USDT 0x55d398326f99059ff775485246999027b3197955, and helper/factory 0xD092A69Dd806bd0f49E69739770784CDa896c07b.

Carson itself is fee-on-transfer. Normal buys and sells already route portions of Carson to reward trackers, the dead address, and a marketing address. The exploitable behavior here is different: the pair itself pays extra Carson out of its own reserves after pricing a swap. That means the pair's internal accounting path matters more than the token's transfer tax alone.

The helper contract is also unverified, but its selector table exposes 0x64a115b4(address) and 0x918b6c8d(address,address) alongside factory-like methods such as createPair(address,address) and getPair(address,address). Direct calls against (pair, Carson) return:

Origin: helper fee configuration call

recipient0 = 0x000000000000000000000000000000000000dEaD
fee0      = 300000000000000000
recipient1 = 0x4D9bD49b7383B88477724428556c7fe36d7e6bc4
fee1      = 300000000000000000

The same helper call against (pair, USDT) returns zeros, so only the Carson side is siphoned from reserves.

3. Vulnerability Analysis & Root Cause Summary

This incident is an ATTACK, not MEV arbitrage and not privileged abuse. The protocol bug is a reserve-accounting error inside the pair's swap path. For a Carson-to-USDT sell, once the pair has measured the Carson input and computed amount1Out, the pair should only sync reserves consistent with that priced input, less the AMM fee already embedded in the invariant formula. Instead, the pair calls the helper, decodes two Carson fee recipients plus two 0.3e18 fee rates, divides those fee rates by 1e18, and then executes two ERC-20 transfer(address,uint256) calls from the pair balance itself before Sync. This creates a mismatch between the reserves used for pricing and the reserves that remain after the swap finishes.

The vulnerable components are:

  • Pair 0xe0a3e0f7e4c789747eb29a212ea915a7a1e1ac69, specifically the Carson-side swap logic around bytecode offsets 0x1c25-0x1f53.
  • Helper 0xd092a69dd806bd0f49e69739770784cda896c07b, specifically 0x918b6c8d(address,address) at bytecode offset 0x0475, which returns the fee recipients and rates used by the pair.

The explicit invariant is: after pricing a Carson sell from amount0In, the pair's Carson reserve must not be reduced by extra pair-funded transfers before reserves are synced. The concrete breakpoint is the helper lookup followed by Carson.transfer(dead, fee) and Carson.transfer(marketing, fee) from the pair balance.

4. Detailed Root Cause Analysis

4.1 Pair-Side Code Path

The pair disassembly shows the Carson-side swap path building a call to helper selector 0x918b6c8d, performing a STATICCALL, decoding four returned words, dividing the two fee rates by 1e18, and then preparing ERC-20 transfer calls:

Origin: Carson pair disassembly

00001c25: PUSH4 0x918b6c8d
...
00001c7d: STATICCALL
...
00001ca6: DUP1
00001ca7: MLOAD
...
00001cc2: PUSH8 0x0de0b6b3a7640000
...
00001d05: DIV
00001d0e: PUSH1 0x06
00001d10: SLOAD
00001d15: PUSH4 0xa9059cbb
...
00001d3d: PUSH4 0xa9059cbb

The helper side confirms that 0x918b6c8d reads four contiguous words from a nested mapping rooted at slot 5:

Origin: helper disassembly

00000475: JUMPDEST
00000476: PUSH1 0x05
...
00000486: KECCAK256
0000048e: KECCAK256
00000490: SLOAD
00000495: SLOAD
0000049a: SLOAD
000004a0: SLOAD

That bytecode-level evidence matches the direct helper call output for (pair, Carson): dead address, 0.3e18, marketing address, 0.3e18. It also matches the pair behavior seen in the trace.

4.2 Economic Effect on the First Sell

After the initial Carson buy, the pair held 36785.567997789766543734 Carson and 1643796.192676695188781042 USDT. The attacker then started the sell loop with a 5000 Carson sell. Carson's transfer tax meant only 4650 Carson reached the pair. The pair nevertheless priced the USDT output from that full effective input and paid 183979.325468778887158891 USDT.

Immediately afterward, the pair used the helper-derived Carson fee configuration to transfer 1395 Carson to the dead address and another 1395 Carson to marketing from the pair balance itself. The pair therefore synced to a Carson reserve that was 2790 Carson lower than the reserve level implied by the priced input.

The traced sell path shows the relevant sequence:

Origin: seed transaction trace, sell-loop excerpt

0xD092A69Dd806bd0f49E69739770784CDa896c07b::918b6c8d(pair, Carson) [staticcall]
0x0aCD5019EdC8ff765517e2e691C5EeF6f9c08830::transfer(0x000000000000000000000000000000000000dEaD, 1395000000000000000000)
0x0aCD5019EdC8ff765517e2e691C5EeF6f9c08830::transfer(0x4D9bD49b7383B88477724428556c7fe36d7e6bc4, 1395000000000000000000)
emit Sync(: 124205567997789766543734, : 84374052691489810528083)

The reportable invariant violation is now deterministic:

  • Pricing step: the pair behaves as though it keeps the full Carson input, net of AMM fee.
  • Reserve step: the pair then removes 60% of that Carson from reserves before Sync.
  • Outcome: the price is ratcheted upward for the next sell, but the already-paid USDT output is not clawed back.

4.3 Why Split Sells Become Profitable

Because only 40% of each effective Carson sell remains in reserves, the pair's Carson side grows much more slowly than the pricing math assumes. That makes the quote side appear scarcer on each iteration than it should be under a correct invariant. The attacker exploited that by repeating the sell path 50 times with 5000 Carson sells and once with a final 105794675801160498373731 Carson sell. A sequential simulation using the traced reserve updates and the helper fee configuration reproduces the exact gross extraction of 1600677049295746860016399 wei-USDT.

This exploit is ACT because every required input is public:

  • Carson/USDT liquidity is on-chain and public.
  • DODO flash-loan pools are public and permissionless.
  • Pair/helper behavior is publicly observable via trace, bytecode, and direct eth_call.

5. Adversary Flow Analysis

The adversary cluster consists of:

  • EOA 0x25bcbbb92c2ae9d0c6f4db814e46fd5c632e2bd3, which submitted the transaction and received the final profit.
  • Exploit contract 0x9cffc95e742d22c1446a3d22e656bb23835a38ac, which executed the buy and sell loop.
  • Flash-loan helper 0x55e741bace7d48ce29c514e5688eabb02493c8f2, which nested public DODO flash loans.

The end-to-end execution flow is:

  1. Flash-loan funding. Helper 0x55e741... borrowed USDT from five public DODO pools in the same transaction: 641735356277648165889865, 190522893034905101776323, 676888608756592594683579, 81511918330743653235156, and 149185234339130108353560 wei-USDT.
  2. Price pump. The exploit contract spent 1500000000000000000000000 wei-USDT to buy 382574920216301611154550 Carson, moving reserves to 36785567997789766543734 Carson and 1643796192676695188781042 USDT.
  3. Broken sell loop. The attacker sold Carson back to the pair in 51 chunks. Every sell was followed by helper lookup 0x918b6c8d(pair, Carson) and pair-funded transfers to the dead and marketing addresses before Sync.
  4. Repayment and profit. Contract 0x9cff... repaid 1739844010739019623938483 wei-USDT to 0x55e741..., then transferred 100677049295746860016399 wei-USDT to the sender EOA.

The attacker-side bytecode is consistent with that flow. Selector extraction from 0x55e741... includes DPPFlashLoanCall(address,uint256,uint256,bytes), matching the trace, while selector extraction from 0x9cff... shows a generic exploit entrypoint rather than any privileged protocol-only interface.

6. Impact & Losses

The measurable loss is:

  • Token: USDT
  • Raw amount: 100677049295746860016399
  • Decimals: 18
  • Human-readable amount: 100677.049295746860016399 USDT

The victim is the Carson/USDT pair and, economically, its liquidity providers. The pair's USDT balance fell by exactly the amount that the adversary EOA gained. The sender also paid exactly 56095692000000000 wei-BNB in gas, derived from receipt gasUsed = 18698564 and effectiveGasPrice = 3000000000.

7. References

  • Seed exploit transaction: 0x37d921a6bb0ecdd8f1ec918d795f9c354727a3ff6b0dba98a512fceb9662a3ac
  • Carson token: 0x0acd5019edc8ff765517e2e691c5eef6f9c08830
  • Carson/USDT pair: 0xe0a3e0f7e4c789747eb29a212ea915a7a1e1ac69
  • Helper/factory: 0xD092A69Dd806bd0f49E69739770784CDa896c07b
  • Public trace evidence: seed trace and balance diff under the collector seed artifacts
  • Bytecode evidence: pair disassembly, helper disassembly, selector extraction, helper fee-configuration note, and receipt under the auditor iter_1 artifacts
  • External explorer reference: BscScan transaction page for the exploit transaction