Tessera - Versus contest - cccz's results

The easiest way to collectively buy, own, and govern the NFTs you already know and love.

General Information

Platform: Code4rena

Start Date: 16/12/2022

Pot Size: $32,070 USDC

Total HM: 18

Participants: 5

Period: 3 days

Judge: hickuphh3

Total Solo HM: 5

Id: 195

League: ETH

Tessera

Findings Distribution

Researcher Performance

Rank: 3/5

Findings: 3

Award: $0.00

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: gzeon

Also found by: Trust, cccz

Labels

bug
3 (High Risk)
satisfactory
duplicate-25

Awards

Data not available

External Links

Lines of code

https://github.com/code-423n4/2022-12-tessera/blob/f37a11407da2af844bbfe868e1422e3665a5f8e4/src/seaport/modules/OptimisticListingSeaport.sol#L156-L160

Vulnerability details

Impact

In the OptimisticListingSeaport contract, any user with the Rae token corresponding to this Vault can call the propose function to create a listing proposal, which can be used for listing when the proposal exists over PROPOSAL_PERIOD.

    function list(address _vault, bytes32[] calldata _listProof) public {
        // Reverts if vault is not registered
        (address token, uint256 id) = _verifyVault(_vault);
        // Reverts if vault is not current owner of the assets
        if (_verifySale(_vault)) revert NotOwner();
        // Reverts if collateral of proposed listing has been rejected
        Listing storage proposedListing = proposedListings[_vault];
        if (proposedListing.collateral == 0) revert Rejected();
        // Reverts if proposal period has not elapsed
        if (proposedListing.proposalDate + PROPOSAL_PERIOD > block.timestamp)
            revert TimeNotElapsed();

Alternatively, any user can call the rejectProposal function to buy out the proposal, at which point the proposal will be reset

        if (proposedListing.collateral == 0) {
            // Resets proposed listing to default
            _setListing(proposedListing, address(this), 0, type(uint256).max, 0);
        }

This allows a malicious user to call the propose function to create a new proposal before the listing proposal is passed, and then immediately call rejectProposal to cancel the proposal, thereby indefinitely delaying the passing of the listing proposal

Consider the following scenario where alice has created a listing proposal and needs to wait 4 days for the proposal to pass On the third day, bob creates a proposal with a lower price and immediately calls rejectProposal to cancel the proposal, at which point alice's proposal is reset, alice needs to create a new proposal and still wait 4 days, and bob can continue to repeat the previous actions to indefinitely delay the listing of the proposal.

Proof of Concept

https://github.com/code-423n4/2022-12-tessera/blob/f37a11407da2af844bbfe868e1422e3665a5f8e4/src/seaport/modules/OptimisticListingSeaport.sol#L123-L124 https://github.com/code-423n4/2022-12-tessera/blob/f37a11407da2af844bbfe868e1422e3665a5f8e4/src/seaport/modules/OptimisticListingSeaport.sol#L156-L160 https://github.com/code-423n4/2022-12-tessera/blob/f37a11407da2af844bbfe868e1422e3665a5f8e4/src/seaport/modules/OptimisticListingSeaport.sol#L218-L227

Tools Used

None

Consider trying to resume the previous proposal in the rejectProposal function when the proposal is cancelled, and increasing the submission time of the previous proposal appropriately so as to avoid having the proposal already passed when it is resumed

#0 - HickupHH3

2022-12-20T02:50:25Z

similar to #41 in DoSing a listing proposal, but with a diff method.

#1 - trust1995

2022-12-21T06:20:56Z

Yeah, the exact method is dup of #42

#2 - c4-judge

2022-12-23T01:14:00Z

HickupHH3 marked the issue as duplicate of #25

#3 - c4-judge

2022-12-23T01:14:04Z

HickupHH3 marked the issue as satisfactory

Findings Information

🌟 Selected for report: IllIllI

Also found by: Lambda, cccz

Labels

bug
2 (Med Risk)
partial-25
duplicate-31

Awards

Data not available

External Links

Lines of code

https://github.com/code-423n4/2022-12-tessera/blob/f37a11407da2af844bbfe868e1422e3665a5f8e4/src/modules/GroupBuy.sol#L99-L116

Vulnerability details

Impact

In the contribute() function of the GroupBuy contract, even if the filledQuantity is less than _quantity, all the ETH provided by the user is locked in the contract, and the user can only call claim() to get it back after purchasing NFT or Pool expires, which leads to the contract locking too much of the user's ETH.

    function contribute(
        uint256 _poolId,
        uint256 _quantity,
        uint256 _price
    ) public payable {
        // Reverts if pool ID is not valid
        _verifyPool(_poolId);
        // Reverts if NFT has already been purchased OR termination period has passed
        (, uint48 totalSupply, , , ) = _verifyUnsuccessfulState(_poolId);
        // Reverts if ether contribution amount per Rae is less than minimum bid price per Rae
        if (msg.value < _quantity * minBidPrices[_poolId] || _quantity == 0)
            revert InvalidContribution();
        // Reverts if ether payment amount is not equal to total amount being contributed
        if (msg.value != _quantity * _price) revert InvalidPayment();

        // Updates user and pool contribution amounts
        userContributions[_poolId][msg.sender] += msg.value;
        totalContributions[_poolId] += msg.value;

Consider the following scenario Pool's totalSupply is 100, minBidPrices is 1 ETH, and _duration is 30 days. alice is ready to call the contribute function to contribute 50 ETH to the Pool But because another user called the contribute function first, when alice calls contribute, the remainingSupply in the Pool is 1. This causes alice's remaining 49 ETH to be locked in the contract, and since there is no corresponding NFT for sale in the market, alice's remaining 49 ETH will be locked for 30 days

Proof of Concept

https://github.com/code-423n4/2022-12-tessera/blob/f37a11407da2af844bbfe868e1422e3665a5f8e4/src/modules/GroupBuy.sol#L99-L116

Tools Used

None

Consider that in contribute(), when filledQuantity is less than _quantity, refund the excess ETH to the user

#0 - c4-judge

2022-12-20T04:36:19Z

HickupHH3 marked the issue as satisfactory

#1 - c4-judge

2022-12-20T04:36:23Z

HickupHH3 marked the issue as primary issue

#2 - c4-judge

2022-12-21T17:58:30Z

HickupHH3 marked the issue as selected for report

#3 - IllIllI000

2022-12-21T18:05:08Z

@HickupHH3 shouldn't this be invalid since funds aren't locked and can be withdrawn as expected?

#4 - HickupHH3

2022-12-21T18:19:07Z

Note that this issue makes no assumption about price, only about partial filling. In the case of a partial fill, we can break this down into 2 cases:

  1. _price >= lowestBid
  2. _price < lowestBid

(1) is invalid; funds aren't locked because the unfilled quantities will displace the lowest bids, so they're rightfully marked as contributions, even potentially displacing some bids from itself which will increment pendingBalances. (2) is the scenario described in #31

Therefore a very weak dup of #31 because the functional vuln is correctly identified (contributions lock up ETH), but didn't correctly pinpoint the conditions for it to occur.

#5 - c4-judge

2022-12-21T18:19:30Z

HickupHH3 marked the issue as duplicate of #31

#6 - c4-judge

2022-12-21T18:19:47Z

HickupHH3 marked the issue as partial-25

#7 - c4-judge

2022-12-21T18:19:53Z

HickupHH3 marked the issue as not selected for report

#8 - c4-judge

2023-01-11T02:27:20Z

HickupHH3 marked the issue as satisfactory

#9 - c4-judge

2023-01-13T15:25:45Z

HickupHH3 marked the issue as partial-25

Findings Information

🌟 Selected for report: Trust

Also found by: cccz

Labels

2 (Med Risk)
satisfactory
duplicate-45

Awards

Data not available

External Links

Judge has assessed an item in Issue #27 as M risk. The relevant finding follows:

Dup of #45. The effect is severe enough IMO to warrant a med severity although I initially intepreted it as a spec mismatch.

#0 - c4-judge

2022-12-20T07:44:55Z

HickupHH3 marked the issue as duplicate of #45

#1 - c4-judge

2022-12-20T07:45:26Z

HickupHH3 marked the issue as satisfactory

Findings Information

🌟 Selected for report: IllIllI

Also found by: Lambda, cccz, gzeon

Labels

bug
downgraded by judge
grade-b
QA (Quality Assurance)
sponsor acknowledged
Q-02

Awards

Data not available

External Links

Lines of code

https://github.com/code-423n4/2022-12-tessera/blob/f37a11407da2af844bbfe868e1422e3665a5f8e4/src/seaport/modules/OptimisticListingSeaport.sol#L422-L425

Vulnerability details

Impact

In the _constructOrder function, the feeReceiver is added to the seaport order, and when the user fulfills the order, tesseraFees amount of ETH is sent to the feeReceiver, which means that a malicious feeReceiver can prevent the listing from being purchased by disabling the receipt of ETH

        orderParams.consideration.push(
            ConsiderationItem(ItemType.NATIVE, address(0), 0, tesseraFees, tesseraFees, feeReceiver)
        );

        uint256 counter = ISeaport(seaport).getCounter(_vault);
        vaultOrderHash[_vault] = _getOrderHash(orderParams, counter);

Proof of Concept

https://github.com/code-423n4/2022-12-tessera/blob/f37a11407da2af844bbfe868e1422e3665a5f8e4/src/seaport/modules/OptimisticListingSeaport.sol#L422-L425 https://github.com/code-423n4/2022-12-tessera/blob/f37a11407da2af844bbfe868e1422e3665a5f8e4/src/seaport/modules/OptimisticListingSeaport.sol#L336-L339

Tools Used

None

consider using address(this) in the _constructOrder function as the receiving address for tesseraFees, after which the feeReceiver can collect tesseraFees from the contract

#0 - HickupHH3

2022-12-20T02:57:14Z

Disagree, feeReceiver is set by the contract deployer (project), would be a centralisation / trust issue. Downgrading to QA.

#1 - c4-judge

2022-12-20T02:57:20Z

HickupHH3 changed the severity to QA (Quality Assurance)

#2 - c4-judge

2022-12-20T09:39:51Z

HickupHH3 marked the issue as grade-b

#3 - c4-sponsor

2023-01-05T18:35:14Z

mehtaculous marked the issue as sponsor acknowledged

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