Astaria contest - HE1M's results

On a mission is to build a highly liquid NFT lending market.

General Information

Platform: Code4rena

Start Date: 05/01/2023

Pot Size: $90,500 USDC

Total HM: 55

Participants: 103

Period: 14 days

Judge: Picodes

Total Solo HM: 18

Id: 202

League: ETH

Astaria

Findings Distribution

Researcher Performance

Rank: 21/103

Findings: 3

Award: $673.06

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
3 (High Risk)
satisfactory
edited-by-warden
duplicate-521

Awards

33.2422 USDC - $33.24

External Links

Lines of code

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

Vulnerability details

Impact

The function safeTransferFrom is used when an NFT is auctioned, and it is supposed to handle the payments done for the NFT in seaport. The payment is done through a token which is not validated. So, an attacker can use a fake token for the payment.

Proof of Concept

The function safeTransferFrom can be called by anyone, so:

In summary, the attacker could use a fake token for buying the NFT and paying the debts.

Tools Used

The identifier parameter should be validated.

#0 - c4-judge

2023-01-24T07:57:49Z

Picodes marked the issue as duplicate of #564

#1 - c4-judge

2023-02-15T07:33:58Z

Picodes marked the issue as satisfactory

#2 - c4-judge

2023-02-23T21:03:28Z

Picodes changed the severity to QA (Quality Assurance)

#3 - c4-judge

2023-02-24T10:37:08Z

This previously downgraded issue has been upgraded by Picodes

#4 - c4-judge

2023-02-24T10:39:42Z

Picodes marked the issue as not a duplicate

#5 - c4-judge

2023-02-24T10:40:53Z

Picodes marked the issue as duplicate of #521

Findings Information

🌟 Selected for report: Jeiwan

Also found by: HE1M, obront

Labels

bug
3 (High Risk)
judge review requested
satisfactory
sponsor confirmed
edited-by-warden
duplicate-480

Awards

588.5044 USDC - $588.50

External Links

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

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 ); }

#0 - c4-sponsor

2023-01-27T03:32:09Z

SantiagoGregory marked the issue as sponsor confirmed

#1 - androolloyd

2023-02-03T18:12:10Z

#2 - c4-sponsor

2023-02-03T18:12:15Z

androolloyd requested judge review

#3 - c4-judge

2023-02-15T08:11:53Z

Picodes marked the issue as duplicate of #480

#4 - c4-judge

2023-02-15T08:12:57Z

Picodes marked the issue as selected for report

#5 - c4-judge

2023-02-15T08:13:01Z

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

#6 - c4-judge

2023-02-15T08:13:35Z

Picodes marked the issue as satisfactory

#0 - c4-judge

2023-01-26T14:56:14Z

Picodes marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter