Astaria contest - eierina'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: 65/103

Findings: 1

Award: $69.09

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: adriro

Also found by: Breeje, JC, JTs, Josiah, ast3ros, bin2chen, eierina, obront, rbserver, yongskiws

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
edited-by-warden
duplicate-588

Awards

69.0905 USDC - $69.09

External Links

Lines of code

https://github.com/AstariaXYZ/astaria-gpl/blob/f430f5b8fe868d4883a8249555df76ee7752587a/src/ERC4626-Cloned.sol#L129-L133

Vulnerability details

Impact

The first transfer in a vault that is based on ERC4626Cloned produces different results depending on whether it is done via deposit or via mint. While first deposit produces a number of shares that is 1:1 with the asset, in the case of mint, the results may vary:

  • if the user mints 1 share, he will transfer 10 ethers of the asset setting a ratio of share:asset of 1 : 10 ethers
  • if the user mints 10 ethers of shares, he will transfer 10 ethers of the asset setting a ration of share:asset of 1:1
  • if the user mints 20 ethers of share, he will transfer 10 ethers of the asset setting a ratio of share:asset of 2:1

This means that if the first mint is for a small number of shares (say N), the minimum deposit from there on will be given by 10 ethers / N or in other words the minimum transfer to produce at least 1 share will be very large.

Proof of Concept

    function testLargeTransferZeroShares() public {
        TestNFT nft = new TestNFT(1);
        address tokenContract = address(nft);
        uint256 tokenId = uint256(0);

        uint256 initialBalance = WETH9.balanceOf(address(this));

        // create a PublicVault with a 14-day epoch
        address publicVault =
            _createPublicVault({strategist: strategistOne, delegate: strategistTwo, epochLength: 14 days});

        PublicVault pv = PublicVault(publicVault);
        address joe = address(0x70a57ed);
        vm.deal(joe, 20 ether);
        vm.startPrank(joe);
        WETH9.deposit{value: 20 ether}();
        WETH9.approve(publicVault, 20 ether);
        uint256 assets = pv.mint(1, joe);

        vm.expectRevert(bytes("ZERO_SHARES"));
        pv.deposit(9.99 ether, joe);

        vm.stopPrank();
    }

Test results:

Running 1 test for src/test/AstariaTest.t.sol:AstariaTest [PASS] testLargeTransferZeroShares() (gas: 1351952) Test result: ok. 1 passed; 0 failed; finished in 13.69ms

Tools Used

n/a

Mind and deposit previews are calculated using the following formulas.

previewMint(uint256 shares)    => totalSupply() == 0 ? 10e18 : shares * totalAssets() / totalSupply();
previewDeposit(uint256 assets) => totalSupply() == 0 ? assets : assets * totalSupply() / totalAssets();

Either substitute `10e18` with the original `shares` in previewMint or ensure that if totalSupply is zero, then the number of shares requested (or issued) is also a large, matching value in order to produce a 1:1 initial ratio.

#0 - c4-judge

2023-01-23T16:27:03Z

Picodes marked the issue as duplicate of #588

#1 - Picodes

2023-01-23T16:27:25Z

The finding could be clearer as to how this could lead to a loss of fund

#2 - c4-judge

2023-01-25T15:27:31Z

Picodes marked the issue as not a duplicate

#3 - c4-judge

2023-01-25T15:27:43Z

Picodes marked the issue as duplicate of #588

#4 - c4-judge

2023-02-19T16:43:11Z

Picodes marked the issue as partial-50

#5 - Picodes

2023-02-19T16:47:55Z

No explanations of how this could lead to a loss of funds or a break in functionalities.

#6 - c4-judge

2023-02-19T16:48:03Z

Picodes marked the issue as not a duplicate

#7 - c4-judge

2023-02-19T16:48:11Z

Picodes marked the issue as duplicate of #588

#8 - c4-judge

2023-02-19T16:48:16Z

Picodes marked the issue as full credit

#9 - c4-judge

2023-02-19T16:58:07Z

Picodes changed the severity to 3 (High Risk)

#10 - c4-judge

2023-02-24T10:11:11Z

Picodes marked the issue as satisfactory

#11 - c4-judge

2023-02-24T10:11:17Z

Picodes marked the issue as partial-50

#12 - c4-judge

2023-02-24T10:22:42Z

Picodes marked the issue as satisfactory

Findings Information

🌟 Selected for report: adriro

Also found by: Breeje, JC, JTs, Josiah, ast3ros, bin2chen, eierina, obront, rbserver, yongskiws

Labels

bug
3 (High Risk)
satisfactory
duplicate-588

Awards

69.0905 USDC - $69.09

External Links

Lines of code

https://github.com/AstariaXYZ/astaria-gpl/blob/f430f5b8fe868d4883a8249555df76ee7752587a/src/ERC4626-Cloned.sol#L38-L52

Vulnerability details

Impact

If a user or a contract that has a large allowance (10 ethers or max) on an ERC4626Cloned based Vault that has not yet received any deposits, calls mint with 0 share argument, will have a 10 ethers of the asset transferred to the Vault with no way to reclaim it because he/it will receive no shares.

NOTE: Raising this issue as high risk conforming to judging criteria '3 — High: Assets can be stolen/*lost*/compromised directly'. No complains if judge feels that the necessary pre-conditions makes it a '2 — Med'.

Proof of Concept

    function testAssetsLostToVault() public {
        TestNFT nft = new TestNFT(1);
        address tokenContract = address(nft);
        uint256 tokenId = uint256(0);

        uint256 initialBalance = WETH9.balanceOf(address(this));

        // create a PublicVault with a 14-day epoch
        address publicVault =
            _createPublicVault({strategist: strategistOne, delegate: strategistTwo, epochLength: 14 days});

        PublicVault pv = PublicVault(publicVault);
        address joe = address(0x70a57ed);
        vm.deal(joe, 10 ether);
        vm.startPrank(joe);
        WETH9.deposit{value: 10 ether}();
        WETH9.approve(publicVault, 10 ether);
        uint256 assets = pv.mint(0, joe);

        emit log_string(string.concat("Joe's balance     : ", Strings.toString(WETH9.balanceOf(address(joe)))));
        emit log_string(string.concat("Joe's shares      : ", Strings.toString(pv.balanceOf(address(joe)))));
        emit log_string(string.concat("pv.totalAssets()  : ", Strings.toString(pv.totalAssets())));
        emit log_string(string.concat("pv.totalSupply()  : ", Strings.toString(pv.totalSupply())));
        emit log_string(string.concat("pv.previewRedeem(): ", Strings.toString(pv.previewRedeem(pv.balanceOf(address(joe))))));

        vm.stopPrank();
    }

Test results:

Running 1 test for src/test/AstariaTest.t.sol:AstariaTest [PASS] testAssetsLostToVault() (gas: 1307633) Logs: Joe's balance : 0 Joe's shares : 0 pv.totalAssets() : 10000000000000000000 pv.totalSupply() : 0 pv.previewRedeem(): 0 Test result: ok. 1 passed; 0 failed; finished in 29.48ms

Tools Used

n/a

Since the previewMint does not use the original supply == 0 ? shares : ..., at least add a require precondition to the mint function to prevent minting of 0 shares.

#0 - c4-judge

2023-01-24T08:58:38Z

Picodes marked the issue as duplicate of #588

#1 - Picodes

2023-01-24T08:59:09Z

Giving duplicate with partial credit as this report misses the main impact of the finding which is the possibility for the first user to trick the next ones

#2 - c4-judge

2023-01-24T08:59:14Z

Picodes marked the issue as partial-50

#3 - eierina

2023-01-24T12:53:01Z

Hi @Picodes

I trust this to be an oversight, please note that this is not the same issue you are mentioning (and indeed I have another issue open for the same you're mentioning) and it would still be present after the fix to https://github.com/code-423n4/2023-01-astaria-findings/issues/588.

This one is about the risk of calling mint passing 0 for shares argument. In this case, the message sender will lose 10 ethers and receive nothing in exchange therefore having no way to redeem or withdraw.

The original ERC4626 code emits "x" shares if supply is 0, where "x" is the same argument passed to the mint function, so in the original code there is no risk of passing 0 because it has no effects, but in this case it does have an effect and therefore the mint should be protected, which is not. Please check carefully the included test and the test outputs.

#4 - Picodes

2023-02-19T16:48:57Z

Hi @eierina, indeed I missed that you had another issue opened. In this case, I'll treat both reports separately.

This report successfully notices that there is an issue with previewMint but the scenario described here is strictly speaking of Low severity at best. Indeed, calling the function with 0 as an argument would be an error on the user side as it doesn't make any sense. Therefore, protecting users from this falls within the Safety checks category, which is QA.

#5 - Picodes

2023-02-19T16:51:21Z

Also, please note that you shouldn't comment on your own findings until post-judging QAs and check the rules here: https://code4rena.notion.site/Backstage-Warden-Guidelines-06d1073540994cc08937f721c2951b0f

#6 - c4-judge

2023-02-24T10:22:37Z

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