Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

approve() shouldn't be called by the approved address for that tokenId #65

Closed
howlbot-integration bot opened this issue Sep 16, 2024 · 3 comments
Closed
Labels
3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working primary issue Highest quality submission among a set of duplicates 🤖_10_group AI based duplicate group recommendation sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards

Comments

@howlbot-integration
Copy link

Lines of code

https://github.com/code-423n4/2024-08-superposition/blob/4528c9d2dbe1550d2660dac903a8246076044905/pkg/sol/OwnershipNFTs.sol#L161

Vulnerability details

Impact

The current implementation of OwnershipNFTs::approve() function allows the approved address to call approve() on the same tokenId for which he is approved. This puts the owner at risk as any approved address can proceed to approve any other users to this token without his consent.

Proof of Concept

EIP721 states that approve():

    ///  Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner.

However, the current implementation breaks this access control provided by the standard.

    function approve(address _approved, uint256 _tokenId) external payable {
>>      _requireAuthorised(msg.sender, _tokenId);
        getApproved[_tokenId] = _approved;
    }

In the contract, approve() uses _requireAuthorised() which checks if:

  • The caller is owner
  • caller is approved for that tokenId
  • caller is an operator
    function _requireAuthorised(address _from, uint256 _tokenId) internal view {
        // revert if the sender is not authorised or the owner
        bool isAllowed =
>>          msg.sender == _from ||
>>          isApprovedForAll[_from][msg.sender] ||
>>          msg.sender == getApproved[_tokenId];

        require(isAllowed, "not allowed");
        require(ownerOf(_tokenId) == _from, "_from is not the owner!");
    }

This setup violates the set standard as it should only check the caller is current NFT owner, or an authorized operator.

Tools Used

Manual Review

Recommended Mitigation Steps

When approve()is called, check only if msg.sender is the owner, or isApprovedForAll.

    function approve(address _approved, uint256 _tokenId) external payable {
-       _requireAuthorised(msg.sender, _tokenId);
+       owner = ownerOf(_tokenId);
+       require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "Caller not authorized");
        getApproved[_tokenId] = _approved;
    }

Assessed type

Access Control

@howlbot-integration howlbot-integration bot added 3 (High Risk) Assets can be stolen/lost/compromised directly 🤖_10_group AI based duplicate group recommendation bug Something isn't working primary issue Highest quality submission among a set of duplicates sufficient quality report This report is of sufficient quality labels Sep 16, 2024
howlbot-integration bot added a commit that referenced this issue Sep 16, 2024
@af-afk
Copy link

af-afk commented Sep 17, 2024

We feel that this is perhaps better considered as medium, in that approving someone access to the token enables them to transfer it on the owner's behalf anyway, so the risk profile is the same.

@af-afk af-afk added the sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") label Sep 17, 2024
@alex-ppg
Copy link

The Warden's submission states that an approved user can set yet another approval which is incorrect.

The code presently permits only the owner to perform an approval as it mandates that the _from argument of the OwnershipNFTs::_requireAuthorised function is equal to the owner of the NFT. As such, an approved user may pass the isAllowed check but will fail at the one right after.

The code indeed contains a flaw in the sense that an operator is not able to approve an NFT, however, this submission did not outline this vulnerability and the vulnerability itself is of limited impact.

@c4-judge c4-judge added the unsatisfactory does not satisfy C4 submission criteria; not eligible for awards label Sep 23, 2024
@c4-judge
Copy link
Contributor

alex-ppg marked the issue as unsatisfactory:
Invalid

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working primary issue Highest quality submission among a set of duplicates 🤖_10_group AI based duplicate group recommendation sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards
Projects
None yet
Development

No branches or pull requests

3 participants