29288 - [SC - Critical] all NFTs can be stolen by calling VestedZeroNFT...

Submitted on Mar 13th 2024 at 11:27:54 UTC by @EricTee for Boost | ZeroLend

Report ID: #29288

Report type: Smart Contract

Report severity: Critical

Target: https://github.com/zerolend/governance

Impacts:

  • Direct theft of any user NFTs, whether at-rest or in-motion, other than unclaimed royalties

  • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

Description

Brief/Intro

Wrong implementation in VestedZeroNFT::split() causes users' NFT to be stolen by anyone. Specifically, the problem arises from the ERC721Upgradeable.sol::_requireOwned check in VestedZeroNFT::split() which fails to check the caller is the owner of the NFT, allow anyone to split the pendingAmount and upfrontAmount of any tokenId up to 99%.

Vulnerability Details

In VestedZeroNFT::split():

 function split(
        uint256 tokenId,
        uint256 fraction
    ) external whenNotPaused nonReentrant {
        _requireOwned(tokenId);

// REDACTED by erictee

the _requireOwned check is not implemented correctly. Let's take a look at ERC721Upgradeable.sol::_requireOwned in Openzeppelin:

This function only return the address of the NFT owner but never revert if the caller is not the NFT owner. Therefore, anyone can call VestedZeroNFT::split() with any tokenId and to steal pendingAmount and upfrontAmount up to 99%.

Impact Details

Direct theft of any user rewards.

Recommendation

Consider making the following changes in VestedZeroNFT::split():

References

https://github.com/zerolend/governance/blob/main/contracts/vesting/VestedZeroNFT.sol#L234

Proof of Concept

  • Install foundry.

  • Rename the original test folder to hardhat-test and create a new folder name test.

  • Add forge-std module to lib with command: git submodule add https://github.com/foundry-rs/forge-std lib/forge-std

  • add remappings.txt file in contracts folder with the following content:

  • Add VestedZeroNFT.t.sol file within test folder with the following content:

  • Finally, run the foundry test with : forge test --match-test test_StealAmountBySplitting -vv

Foundry Result:

Last updated

Was this helpful?