Open c4-bot-9 opened 4 months ago
Report is of sufficient quality
3docSec marked the issue as satisfactory
3docSec marked the issue as selected for report
There are 2 ways to take approval from user
approve
function => this function also discard previous approval. So we can not keep other's approval.setApprovalForAll
fuction => this function seems too risky for user to useERC721 implementation allows only one spender on a token at a time, therefore taking user's approval for our contract will also clear other approval on the same token
mapping(uint256 tokenId => address) private _tokenApprovals;
Another approach is to use setApprovalForAll
function in ERC721, which poses another concern of allowing our contract to use all user's positions.
With that in mind, we think that using onERC721Received
hook is still the safest option for our users and Krystal is able to provide convenient user experience.
Lines of code
https://github.com/code-423n4/2024-06-krystal-defi/blob/f65b381b258290653fa638019a5a134c4ef90ba8/src/Common.sol#L392 https://github.com/code-423n4/2024-06-krystal-defi/blob/f65b381b258290653fa638019a5a134c4ef90ba8/src/V3Utils.sol#L76-L85 https://github.com/code-423n4/2024-06-krystal-defi/blob/f65b381b258290653fa638019a5a134c4ef90ba8/src/V3Utils.sol#L171 https://github.com/code-423n4/2024-06-krystal-defi/blob/f65b381b258290653fa638019a5a134c4ef90ba8/src/V3Automation.sol#L92
Vulnerability details
Impact
The user loses their approved entities
Proof of Concept
The protocol transfers the Position NFT from the position owner to the protocol and back to the position owner to execute the actions.
List of occurences
```solidity Contract: Common.sol 402: params.nfpm.transferFrom(address(this), params.recipient, result.tokenId); ``` ```solidity Contract: V3Utils.sol 171: nfpm.transferFrom(address(this), from, tokenId); ``` ```solidity Contract: V3Utils.sol 76: function execute(INonfungiblePositionManager _nfpm, uint256 tokenId, Instructions calldata instructions) whenNotPaused() external 77: { 78: // must be approved beforehand 79: _nfpm.safeTransferFrom( 80: msg.sender, 81: address(this), 82: tokenId, 83: abi.encode(instructions) 84: ); 85: } ``` ```solidity Contract: V3Automation.sol 92: params.nfpm.transferFrom(positionOwner, address(this), params.tokenId); ``` ```solidity Contract: V3Automation.sol 167: params.nfpm.transferFrom(address(this), positionOwner, params.tokenId); ```This breaks the allowance mechanism of ERC721 for every action taken on the platform and it omits the allowances given from the token owners to the approved entities. For every NFT transfer, the allowances are zeroed out:
E.g.:
Tools Used
Manual Review
Recommended Mitigation Steps
NFPM uses
isAuthorizedForToken
modifier as below:Take approval of token owners rather than using
onERC721Received
hook.Assessed type
Other