Platform: Code4rena
Start Date: 07/04/2023
Pot Size: $47,000 USDC
Total HM: 20
Participants: 120
Period: 6 days
Judge: GalloDaSballo
Total Solo HM: 4
Id: 230
League: ETH
Rank: 103/120
Findings: 1
Award: $10.86
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: AkshaySrivastav
Also found by: 0xTheC0der, Dug, GT_Blockchain, Haipls, adriro, bin2chen, carlitox477, dingo2077, fs0c, hasmama, hihen, holyhansss_kr, juancito, ladboy233, philogy, saian, said, sashik_eth, yixxas, zion
10.8554 USDC - $10.86
https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c /src/Factory.sol#L71
for example an attacker can call the create() function Adam NFT as input and these NFT will be transferred to the private pool without the Adam knowledge, the attacker can potentially steal NFT from Adam and deposit them into the private pool, result in Adam total loss of assets.
the function does not check factory to transfer the NFT that will be deposited to the pool here is the link where the vulnerability occure. https://github.com/code-423n4/2023-04-caviar/blob/cd8a92667bcb6657f70657183769c244d04c015c /src/Factory.sol#L71
for example let say jhon is an attacker and Adam is a victim, jhon create a new contract that inherits from Factory and implements a attack() function that takes the address of the Adam NFT and the uint256 salt as input.
contract jhon is Factory { function stillingadamnft(address adamNFT, uint256 salt) public { } }
In the stillingadamnft() function, calculate the salt that will be used to put the minimal proxy clone of the PrivatePool contract. This salt needs to be unique to make ensure that the new private pool is a new contract.
bytes32 salt = keccak256(abi.encodePacked(msg.sender, adamNFT, salt));
Call the create() function of the Factory contract, passing in the Adam NFT as one of the tokenIds and the calculated salt as the _salt parameter. Make sure to approve the Factory contract to transfer the Adam NFT before calling the create() function.
function stillingadamnft(address adamNFT, uint256 salt) public { bytes32 poolSalt = keccak256(abi.encodePacked(msg.sender, adammNFT, salt)); ERC721(adamNFT).approve(address(this), 1); create( adamNFT, poolSalt, [1], 0 ); }
After the create() function returns, the new private pool contract will have been put with the Adam NFT as one of the deposited NFT, and the jhon will have control over the private pool. The Adam NFT will have been transferred to the private pool without the Adam knowledge.
Here is the modified create() function that includes the vulnerability:
function create( address _baseToken, address _nft, uint128 _virtualBaseTokenReserves, uint128 _virtualNftReserves, uint56 _changeFee, uint16 _feeRate, bytes32 _merkleRoot, bool _useStolenNftOracle, bool _payRoyalties, bytes32 _salt, uint256[] memory tokenIds, uint256 baseTokenAmount ) public payable returns (PrivatePool privatePool) { if ((_baseToken == address(0) && msg.value != baseTokenAmount) || (_baseToken
VSCODE
To mitigate this vulnerability, the Factory contract should check whether the caller has approved the factory to transfer the NFTs that will be deposited to the pool. This can be achieved by calling the ERC721.approve() function on the NFT contract with the Factory contract address and the token ID as inputs. The Factory contract should then check the approved status of the NFT before transferring it to the private pool. This will make ensure that only NFTs that have been approved by their owners are transferred to the private pool
#0 - c4-pre-sort
2023-04-20T17:18:54Z
0xSorryNotSorry marked the issue as duplicate of #419
#1 - c4-judge
2023-04-30T08:58:08Z
GalloDaSballo changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-05-01T07:22:57Z
GalloDaSballo marked the issue as satisfactory