Astaria contest - 0xcm'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: 53/103

Findings: 3

Award: $158.13

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
3 (High Risk)
satisfactory
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#L114-L167

Vulnerability details

Impact

ClearingHouses are deployed for each new loan and settle payments between Seaport auctions and Astaria Vaults if a liquidation occurs.

However, due to the lack of proper data validation in the current implementation, anyone can fake a token and transfer it to the ClearingHouse to settle the auction, which would undermine the clearing process and harm the entire system.

Proof of Concept

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

function safeTransferFrom(
    address from, // the from is the offerer
    address to,
    uint256 identifier,
    uint256 amount,
    bytes calldata data //empty from seaport
  ) public {
    //data is empty and useless
    _execute(from, to, identifier, amount);
  }

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

function _execute(
    address tokenContract, // collateral token sending the fake nft
    address to, // buyer
    uint256 encodedMetaData, //retrieve token address from the encoded data
    uint256 // space to encode whatever is needed,
  ) internal {
    IAstariaRouter ASTARIA_ROUTER = IAstariaRouter(_getArgAddress(0)); // get the router from the immutable arg

    ClearingHouseStorage storage s = _getStorage();
    address paymentToken = bytes32(encodedMetaData).fromLast20Bytes();

    uint256 currentOfferPrice = _locateCurrentAmount({
      startAmount: s.auctionStack.startAmount,
      endAmount: s.auctionStack.endAmount,
      startTime: s.auctionStack.startTime,
      endTime: s.auctionStack.endTime,
      roundUp: true //we are a consideration we round up
    });
    uint256 payment = ERC20(paymentToken).balanceOf(address(this));

    require(payment >= currentOfferPrice, "not enough funds received");

    uint256 collateralId = _getArgUint256(21);
    // pay liquidator fees here

    ILienToken.AuctionStack[] storage stack = s.auctionStack.stack;

    uint256 liquidatorPayment = ASTARIA_ROUTER.getLiquidatorFee(payment);

    ERC20(paymentToken).safeTransfer(
      s.auctionStack.liquidator,
      liquidatorPayment
    );

    ERC20(paymentToken).safeApprove(
      address(ASTARIA_ROUTER.TRANSFER_PROXY()),
      payment - liquidatorPayment
    );

    ASTARIA_ROUTER.LIEN_TOKEN().payDebtViaClearingHouse(
      paymentToken,
      collateralId,
      payment - liquidatorPayment,
      s.auctionStack.stack
    );

    if (ERC20(paymentToken).balanceOf(address(this)) > 0) {
      ERC20(paymentToken).safeTransfer(
        ASTARIA_ROUTER.COLLATERAL_TOKEN().ownerOf(collateralId),
        ERC20(paymentToken).balanceOf(address(this))
      );
    }
    ASTARIA_ROUTER.COLLATERAL_TOKEN().settleAuction(collateralId);
  }

safeTransferFrom() is a public function without msg.sender access check, encodedMetaData is user data, there no check make sure address paymentToken = bytes32(encodedMetaData).fromLast20Bytes() is the settlementToken.

so the paymentToken can be a fake token worth nothing.

when an attacker call safeTransferFrom before the real auction got settled, the real auction can not settle.

add check make sure paymentToken = settlementToken

#0 - c4-judge

2023-01-24T07:45:29Z

Picodes marked the issue as duplicate of #564

#1 - c4-judge

2023-02-15T07:28:21Z

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:25Z

Picodes marked the issue as not a duplicate

#5 - c4-judge

2023-02-24T10:40:16Z

Picodes marked the issue as duplicate of #521

Findings Information

🌟 Selected for report: rbserver

Also found by: 0Kage, 0xcm, bin2chen, caventa, csanuragjain, synackrst, unforgiven

Labels

bug
2 (Med Risk)
satisfactory
duplicate-486

Awards

39.0944 USDC - $39.09

External Links

Lines of code

https://github.com/AstariaXYZ/astaria-gpl/blob/4b49fe993d9b807fe68b3421ee7f2fe91267c9ef/src/ERC4626-Cloned.sol#L19-L36

Vulnerability details

Impact

In the current implementation, PublicVault.sol#minDepositAmount() is intended to prevent token deposits that are too small. However, in ERC4626-Cloned.sol#deposit(), the minDepositAmount is incorrectly used to compare with the number of shares obtained from this deposit to confirm compliance with the minimum deposit requirement, which does not match the return unit of PublicVault.sol#minDepositAmount().

Due to the features of PublicVault, as profits increase, share prices will continue to rise. Under high share prices, the current implementation will continue to increase the minimum deposit amount, preventing more and more small investors

Proof of Concept

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L96-L108

function minDepositAmount()
    public
    view
    virtual
    override(ERC4626Cloned)
    returns (uint256)
  {
    if (ERC20(asset()).decimals() == uint8(18)) {
      return 100 gwei;
    } else {
      return 10**(ERC20(asset()).decimals() - 1);
    }
  }

https://github.com/AstariaXYZ/astaria-gpl/blob/4b49fe993d9b807fe68b3421ee7f2fe91267c9ef/src/ERC4626-Cloned.sol#L19-L36

function deposit(uint256 assets, address receiver)
    public
    virtual
    returns (uint256 shares)
  {
    // Check for rounding error since we round down in previewDeposit.
    require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

    require(shares > minDepositAmount(), "VALUE_TOO_SMALL");
    // Need to transfer before minting or ERC777s could reenter.
    ERC20(asset()).safeTransferFrom(msg.sender, address(this), assets);

    _mint(receiver, shares);

    emit Deposit(msg.sender, receiver, assets, shares);

    afterDeposit(assets, shares);
  }

Given:

  • asset = USDC
  • minDepositAmount = 10**5 (0.1 USDC)
  • share price is now 10
  1. Alice call minDepositAmount() got 100,000

  2. Alice call deposit() try deposit 200,000, tx revert with VALUE_TOO_SMALL

Consider Change to:

function deposit(uint256 assets, address receiver)
    public
    virtual
    returns (uint256 shares)
  {
    // Check for rounding error since we round down in previewDeposit.
    require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

    require(assets > minDepositAmount(), "VALUE_TOO_SMALL");
    // Need to transfer before minting or ERC777s could reenter.
    ERC20(asset()).safeTransferFrom(msg.sender, address(this), assets);

    _mint(receiver, shares);

    emit Deposit(msg.sender, receiver, assets, shares);

    afterDeposit(assets, shares);
  }

#0 - c4-judge

2023-01-25T14:35:13Z

Picodes marked the issue as duplicate of #588

#1 - c4-judge

2023-01-25T15:28:49Z

Picodes marked the issue as not a duplicate

#2 - c4-judge

2023-01-25T15:29:59Z

Picodes marked the issue as duplicate of #486

#3 - c4-judge

2023-02-21T22:13:52Z

Picodes marked the issue as satisfactory

Findings Information

🌟 Selected for report: obront

Also found by: 0xcm, Tointer, bin2chen, zaskoh

Labels

bug
2 (Med Risk)
satisfactory
duplicate-367

Awards

85.8039 USDC - $85.80

External Links

Lines of code

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L96-L108

Vulnerability details

Impact

If the asset is WBTC, under the current design, the minDepositAmount will be 0.1 WBTC, valued more than 2000 US dollars, which is a excessively high minimum single investment amount for most investors, seriously affecting the efficiency of fundraising investment.

Proof of Concept

https://github.com/code-423n4/2023-01-astaria/blob/1bfc58b42109b839528ab1c21dc9803d663df898/src/PublicVault.sol#L96-L108

function minDepositAmount()
    public
    view
    virtual
    override(ERC4626Cloned)
    returns (uint256)
  {
    if (ERC20(asset()).decimals() == uint8(18)) {
      return 100 gwei;
    } else {
      return 10**(ERC20(asset()).decimals() - 1);
    }
  }

WBTC decimals is 8, minDepositAmount() for WBTC will be 10**7 wei worth more than 2000 USD

consider add a storage minDepositAmount in VIData, and let owner set it

#0 - c4-judge

2023-01-25T15:02:41Z

Picodes marked the issue as duplicate of #367

#1 - c4-judge

2023-02-23T07:32:32Z

Picodes marked the issue as satisfactory

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