APC Proxy Spot-Price Exploit
Exploit Transactions
Victim Addresses
0x5a88114f02bffb04a9a13a776f592547b3080237BSC0xc6fc79db585bf2cc613913f3c24b999a676944acBSCLoss Breakdown
Similar Incidents
DKP Exchange Flash-Price Exploit
37%Nimbus Spot-Oracle Reward Drain
35%Local Traders Price Takeover
34%MicDao Mixed-Price Sale Exploit
33%BBOX Pair Burn Price Manipulation
33%HEALTH Zero-Transfer Price Manipulation
32%Root Cause Analysis
APC Proxy Spot-Price Exploit
1. Incident Overview TL;DR
On BNB Chain, an unprivileged adversary exploited the APC/MUSD swap proxy at 0x5a88114f02bffb04a9a13a776f592547b3080237 by manipulating the APC/USDT Pancake market price inside the same transaction. The proxy trusted the live Pancake spot quote for APC and therefore bought APC too expensively and sold APC back too cheaply during the attacker-controlled round trip.
The root cause was the implementation at 0xc6fc79db585bf2cc613913f3c24b999a676944ac using Pancake pair reserves plus router getAmountOut(1e18, reserveAPC, reserveUSDT) as an oracle in its swap path. Because those reserves were read synchronously from a public AMM, the attacker could move them with flash liquidity and force the proxy to settle at attacker-chosen prices.
2. Key Background
The affected contract was a proxy at 0x5a88114f02bffb04a9a13a776f592547b3080237. The collector's EIP-1967 slot reads show that it delegated to implementation 0xc6Fc79db585BF2cC613913F3c24B999a676944Ac at both the exploit block and the later confirmation block.
{
"block": 23527084,
"parsed_address": "0xc6Fc79db585BF2cC613913F3c24B999a676944Ac"
}
{
"block": 23527907,
"parsed_address": "0xc6Fc79db585BF2cC613913F3c24B999a676944Ac"
}
The APC reference market was Pancake pair 0x3de032d5d11c94d2d79dba0c34d7851ffaa05dd8, quoted against BNB Chain USDT 0x55d398326f99059ff775485246999027b3197955. The proxy inventory tokens were APC 0x2aa504586d6cab3c59fa629f74c586d78b93a025 and MUSD 0x473c33c55be10bb53d81fe45173fcc444143a13e. The attacker used public flash liquidity from pair 0x7efaef62fddcca950418312c6c91aef321375a00.
3. Vulnerability Analysis & Root Cause Summary
The vulnerability class is an attacker-manipulable spot-price oracle used directly in settlement logic. The proxy implementation did not anchor APC pricing to a TWAP, external oracle, or delayed observation. Instead, it read the current APC/USDT reserves from the Pancake pair and immediately converted those reserves into a price using the Pancake router. That made the proxy price a pure function of current AMM reserves, which an attacker could move intra-transaction with flash liquidity. Once the attacker pumped APC, the proxy overpaid MUSD for APC. After the attacker dumped APC and pushed the spot price down, the same proxy underpriced APC and returned more APC than economically justified for the MUSD received.
4. Detailed Root Cause Analysis
The implementation artifact shows the exact oracle path. At runtime, it loads a pair address from storage, performs a staticcall with selector 0x0902f1ac (getReserves), then prepares a call to the Pancake router with selector 0x054d50d4 (getAmountOut) using a fixed 1e18 input amount.
0000020d: PUSH1 0xcc
00000212: SLOAD
...
0000022c: PUSH4 0x0902f1ac
...
0000024e: STATICCALL
...
00000292: PUSH8 0x0de0b6b3a7640000
...
000002bc: PUSH20 0x10ed43c718714eb63d5aa57b78b54704e256024e
000002d2: PUSH4 0x054d50d4
000002e6: STATICCALL
The exploit transaction 0xbcaecea2044101c80f186ce5327bec796cd9e054f0c240ddce93e2aead337370 demonstrates the full mechanism. First, the attacker helper borrowed 1200000000000000000000000 USDT from the public flashswap pair. The trace then shows the helper buying APC on Pancake, which increased the APC price before interacting with the proxy.
The first victimized proxy swap occurred immediately after the pump. The implementation read reserves 1892088320943861701221496 APC and 3477898610159282653915500 USDT, called getAmountOut(1e18, reserveAPC, reserveUSDT), received 1833530705879737115, and then paid 181519539882093974385000 MUSD for 100000000000000000000000 APC:
0xc6Fc79...::swap(APC, MUSD, 100000000000000000000000) [delegatecall]
PancakePair::getReserves() -> 1892088320943861701221496, 3477898610159282653915500
PancakeRouter::getAmountOut(1000000000000000000, ...) -> 1833530705879737115
MUSD::transfer(attacker_helper, 181519539882093974385000)
After collecting that MUSD, the attacker dumped APC back into Pancake, which drove APC cheaper. The second proxy swap then re-read the now-depressed reserves 2754005423681298958861615 APC and 2397639284103570275527213 USDT, called the same pricing function again, got a lower APC quote 868423968622209319, and transferred 206931580629195942203962 APC out for the 181519539882093974385000 MUSD previously acquired:
0xc6Fc79...::swap(MUSD, APC, 181519539882093974385000) [delegatecall]
PancakePair::getReserves() -> 2754005423681298958861615, 2397639284103570275527213
PancakeRouter::getAmountOut(1000000000000000000, ...) -> 868423968622209319
APC::transfer(attacker_helper, 206931580629195942203962)
This is the code-level breakpoint: the proxy settled a bilateral APC/MUSD exchange using a same-transaction spot quote that the attacker had just manipulated. The invariant violation is explicit: a public user should not be able to choose the protocol's APC/MUSD exchange rate by transiently moving APC/USDT AMM reserves.
5. Adversary Flow Analysis
The first exploit transaction was sent by EOA 0xc578d755cd56255d3ff6e92e1b6371ba945e3984 and executed through helper contract 0x02a1d194914bfda8d5d46333cf6dda485923c4c4. The flow was:
- Borrow
1,200,000e18USDT from flash pair0x7efaef62fddcca950418312c6c91aef321375a00. - Buy APC on the APC/USDT Pancake pair to raise the spot APC price observed by the proxy.
- Call
proxy.swap(APC, MUSD, 100000e18)and receive181519539882093974385000MUSD at the manipulated expensive APC price. - Sell APC back into Pancake to push APC down.
- Call
proxy.swap(MUSD, APC, 181519539882093974385000)and receive206931580629195942203962APC at the manipulated cheap APC price. - Sell the returned APC back to USDT and repay
1203600000000000000000000USDT to the flash pair. - Keep the residual USDT profit.
The collector balance diff for the same transaction shows the outcome directly:
{
"token": "0x55d398326f99059ff775485246999027b3197955",
"holder": "0xc578d755cd56255d3ff6e92e1b6371ba945e3984",
"delta": "40477142019493049063536"
}
The proxy's APC inventory also fell by 106931580629195942203962 units in that transaction, which matches the proxy inventory drain claimed in the analysis. A second transaction, 0xf2d4559aeb945fb8e4304da5320ce6a2a96415aa70286715c9fcaf5dbd9d7ed2, repeated the same public strategy and realized an additional 6126987133757734534775 USDT profit, confirming reproducibility rather than one-off coincidence.
6. Impact & Losses
The exploit extracted value from the APC/MUSD proxy by forcing two bad exchange rates in opposite directions within one transaction. In the first seed transaction, the attacker realized 40477142019493049063536 raw USDT units after flashswap repayment. In the repeated seed transaction, the attacker realized 6126987133757734534775 additional raw USDT units. Combined observed loss was 46604129153250783698311 raw USDT units, with decimal=18.
The economic harm was borne by the proxy inventory: it overpaid MUSD when APC was briefly overpriced and underpriced APC when APC was immediately depressed. This is a direct protocol-value extraction event rather than generic market slippage.
7. References
- Exploit tx
0xbcaecea2044101c80f186ce5327bec796cd9e054f0c240ddce93e2aead337370 - Repeated exploit tx
0xf2d4559aeb945fb8e4304da5320ce6a2a96415aa70286715c9fcaf5dbd9d7ed2 - Proxy
0x5a88114f02bffb04a9a13a776f592547b3080237 - Implementation
0xc6fc79db585bf2cc613913f3c24b999a676944ac - APC token
0x2aa504586d6cab3c59fa629f74c586d78b93a025 - MUSD token
0x473c33c55be10bb53d81fe45173fcc444143a13e - APC/USDT Pancake pair
0x3de032d5d11c94d2d79dba0c34d7851ffaa05dd8 - Flashswap pair
0x7efaef62fddcca950418312c6c91aef321375a00 - Proxy implementation-slot proof from collector artifacts
- Implementation runtime disassembly from collector artifacts
- Seed traces and balance-diff artifacts for both exploit transactions