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

Releasing an NFT by the liquidator does not update the mapping idToUnderlying and does not burn the collateralId #196

Closed
code423n4 opened this issue Jan 13, 2023 · 7 comments
Labels
3 (High Risk) Assets can be stolen/lost/compromised directly bug Something isn't working duplicate-480 edited-by-warden judge review requested Judge should review this issue satisfactory satisfies C4 submission criteria; eligible for awards sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")

Comments

@code423n4
Copy link
Contributor

code423n4 commented Jan 13, 2023

Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L331

Vulnerability details

Impact

The ClearingHouse is supposed to be the owner of the collateralized NFT always. The only time that it is not the owner, is during flashAction in which the NFT will be transferred back to the ClearingHouse at the end of the transaction. The ownership is tracked by ownerOf(collateralId), and the NFT data is tracked by s.idToUnderlying[collateralId]. In a normal case, if an NFT is going to be released by calling the function releaseToAddress, these data will be burned and removed, ownerOf(collateralId) = address(0) and delete s.idToUnderlying[collateralId].
https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L331

But, this is not the case if the NFT is going to be released by a liquidator during an auction.

Proof of Concept

  • Suppose Alice owns an NFT and transfers it to CollateralToken.sol to be used as collateral. This transfer triggers the onERC721Received:

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L553

  • This contract will assign a collateralId to this NFT by appending the token contract address and token id.

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L560

  • This contract will deploy a ClearingHouse contract and transfers this NFT to that contract.

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L578

  • This contract will mint a collateral id for the owner of this NFT.

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L588

  • So, we will have:

    • ERC721(tokenContract).ownerOf(tokenId) = the address of the deployed contract ClearingHouse
    • s.clearingHouse[collateralId] = the address of the deployed contract ClearingHouse
    • ownerOf(collateralId) = Alice
    • s.idToUnderlying[collateralId].tokenContract = address of the token contract
  • Suppose later, Alice is going to be liquidated, and her collateral (her NFT which is locked in the ClearingHouse) is auctioned.

  • After the liquidation, the liquidator should claim this NFT by calling liquidatorNFTClaim.

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L109

  • This function calls the internal function _releaseToAddress.

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/CollateralToken.sol#L352

  • In this function, the NFT will be transferred to the liquidator. So, we will have:

    • ERC721(tokenContract).ownerOf(tokenId) = the address of liquidator
    • s.clearingHouse[collateralId] = the address of the deployed contract ClearingHouse
    • ownerOf(collateralId) = Alice
    • s.idToUnderlying[collateralId].tokenContract = address of the token contract
  • This is the vulnerability, because that NFT is transferred to the liquidator, but the mapping idToUnderlying is not deleted and the minted collaterId is not burned. In other words, it is correct to have the following states:

    • ERC721(tokenContract).ownerOf(tokenId) = the address of liquidator
    • s.clearingHouse[collateralId] = the address of the deployed contract ClearingHouse
    • ownerOf(collateralId) = address(0)
    • s.idToUnderlying[collateralId].tokenContract = address(0)
  • This can lead to some issues. For example, if the liquidator sells this NFT to another innocent user (Bob):

  • The situation can be even worse:

In summary, the issue is that the mapping and burning are done only in the function releaseToAddress, while the internal function _releaseToAddress is called during claiming the NFT by the liquidator in the function liquidatorNFTClaim.

Tools Used

Recommended Mitigation Steps

The following modifications should be applied:

  // Before
  
  function releaseToAddress(uint256 collateralId, address releaseTo)
    public
    releaseCheck(collateralId)
    onlyOwner(collateralId)
  {
    CollateralStorage storage s = _loadCollateralSlot();

    if (msg.sender != ownerOf(collateralId)) {
      revert InvalidSender();
    }
    Asset memory underlying = s.idToUnderlying[collateralId];
    address tokenContract = underlying.tokenContract;
    _burn(collateralId);
    delete s.idToUnderlying[collateralId];
    _releaseToAddress(s, underlying, collateralId, releaseTo);
  }

  function _releaseToAddress(
    CollateralStorage storage s,
    Asset memory underlyingAsset,
    uint256 collateralId,
    address releaseTo
  ) internal {
    ClearingHouse(s.clearingHouse[collateralId]).transferUnderlying(
      underlyingAsset.tokenContract,
      underlyingAsset.tokenId,
      releaseTo
    );
    emit ReleaseTo(
      underlyingAsset.tokenContract,
      underlyingAsset.tokenId,
      releaseTo
    );
  }
//After  
  function releaseToAddress(uint256 collateralId, address releaseTo)
    public
    releaseCheck(collateralId)
    onlyOwner(collateralId)
  {
    CollateralStorage storage s = _loadCollateralSlot();

    if (msg.sender != ownerOf(collateralId)) {
      revert InvalidSender();
    }
    Asset memory underlying = s.idToUnderlying[collateralId];
    _releaseToAddress(s, underlying, collateralId, releaseTo);
  }

  function _releaseToAddress(
    CollateralStorage storage s,
    Asset memory underlyingAsset,
    uint256 collateralId,
    address releaseTo
  ) internal {
    _burn(collateralId);
    delete s.idToUnderlying[collateralId];  
    ClearingHouse(s.clearingHouse[collateralId]).transferUnderlying(
      underlyingAsset.tokenContract,
      underlyingAsset.tokenId,
      releaseTo
    );
    emit ReleaseTo(
      underlyingAsset.tokenContract,
      underlyingAsset.tokenId,
      releaseTo
    );
  }
@code423n4 code423n4 added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working labels Jan 13, 2023
code423n4 added a commit that referenced this issue Jan 13, 2023
@code423n4 code423n4 added 3 (High Risk) Assets can be stolen/lost/compromised directly edited-by-warden and removed 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value labels Jan 14, 2023
@c4-sponsor
Copy link

SantiagoGregory marked the issue as sponsor confirmed

@c4-sponsor c4-sponsor added the sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") label Jan 27, 2023
@androolloyd
Copy link

same underlying issue as this, #480

@c4-sponsor
Copy link

androolloyd requested judge review

@c4-sponsor c4-sponsor added the judge review requested Judge should review this issue label Feb 3, 2023
@c4-judge
Copy link
Contributor

Picodes marked the issue as duplicate of #480

@c4-judge
Copy link
Contributor

Picodes marked the issue as selected for report

@c4-judge c4-judge added primary issue Highest quality submission among a set of duplicates selected for report This submission will be included/highlighted in the audit report labels Feb 15, 2023
@c4-judge c4-judge added duplicate-480 and removed primary issue Highest quality submission among a set of duplicates selected for report This submission will be included/highlighted in the audit report labels Feb 15, 2023
@c4-judge
Copy link
Contributor

Picodes marked issue #480 as primary and marked this issue as a duplicate of 480

@c4-judge
Copy link
Contributor

Picodes marked the issue as satisfactory

@c4-judge c4-judge added the satisfactory satisfies C4 submission criteria; eligible for awards label Feb 15, 2023
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 duplicate-480 edited-by-warden judge review requested Judge should review this issue satisfactory satisfies C4 submission criteria; eligible for awards sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
Projects
None yet
Development

No branches or pull requests

4 participants