code4rena-2022-10-blur-exchange-h01

[H-01] StandardPolicyERC1155.sol returns amount 1 instead of amount order.amount

보고서

Summary

ERC1155 거래시 NFT를 여러개 구매해도 1개만 받게 된다.

Keyword

ERC1155, NFT marketplace, logic flaw, bug

Vulnerability

BlurExchange.execute 함수에서, _canMatchOrders 함수가 리턴한 데이터를 기반으로 거래 정산을 한다. 거래금을 판매자에게 전달하고 구매자에게 NFT를 보낸다.

    function execute(Input calldata sell, Input calldata buy)
        external
        payable
        reentrancyGuard
        whenOpen
    {
        ...
        (uint256 price, uint256 tokenId, uint256 amount, AssetType assetType) = _canMatchOrders(sell.order, buy.order);
 
        _executeFundsTransfer(
            sell.order.trader,
            buy.order.trader,
            sell.order.paymentToken,
            sell.order.fees,
            price
        );
        _executeTokenTransfer(
            sell.order.collection,
            sell.order.trader,
            buy.order.trader,
            tokenId,
            amount,
            assetType
        );
        ...
    }

_canMatchOrders 에서는 canMatchMakerAsk 또는 canMatchMakerBid 함수의 리턴값을 그대로 리턴한다. StandardPolicyERC1155.canMatchMakerAsk 함수와 StandardPolicyERC1155.canMatchMakerBid 함수에서, 파라미터로 들어온 amount를 확인하지 않고 무조건 1을 리턴한다.

    function canMatchMakerAsk(Order calldata makerAsk, Order calldata takerBid)
        external
        pure
        override
        returns (
            bool,
            uint256,
            uint256,
            uint256,
            AssetType
        )
    {
        return (
            (makerAsk.side != takerBid.side) &&
            (makerAsk.paymentToken == takerBid.paymentToken) &&
            (makerAsk.collection == takerBid.collection) &&
            (makerAsk.tokenId == takerBid.tokenId) &&
            (makerAsk.matchingPolicy == takerBid.matchingPolicy) &&
            (makerAsk.price == takerBid.price),
            makerAsk.price,
            makerAsk.tokenId,
            1, //@audit makerAsk.amount와 takerBid.amount를 확인하지 않고 1로 하드코딩하여 리턴
            AssetType.ERC1155
        );
    }
 
    function canMatchMakerBid(Order calldata makerBid, Order calldata takerAsk)
        external
        pure
        override
        returns (
            bool,
            uint256,
            uint256,
            uint256,
            AssetType
        )
    {
        return (
            (makerBid.side != takerAsk.side) &&
            (makerBid.paymentToken == takerAsk.paymentToken) &&
            (makerBid.collection == takerAsk.collection) &&
            (makerBid.tokenId == takerAsk.tokenId) &&
            (makerBid.matchingPolicy == takerAsk.matchingPolicy) &&
            (makerBid.price == takerAsk.price),
            makerBid.price,
            makerBid.tokenId,
            1, //@audit makerBid.amount와 takerAsk.amount를 확인하지 않고 1로 하드코딩하여 리턴
            AssetType.ERC1155
        );
    }

이 버그로 인해 구매자는 구매 비용을 다 지불하고도 1개의 ERC1155 토큰을 얻게 된다.

다음은 PoC 코드이다. alice 가 ERC1155 10개짜리 판매를 올리고 bob이 이를 구매했지만, 실제로 bob이 얻은 ERC1155 토큰은 1개이다.

    it('Only 1 ERC1155 received for order with amount > 1', async () => {
      await mockERC1155.mint(alice.address, tokenId, 10);
      sell = generateOrder(alice, {
        side: Side.Sell,
        tokenId,
        amount: 10,
        collection: mockERC1155.address,
        matchingPolicy: matchingPolicies.standardPolicyERC1155.address,
      });
      buy = generateOrder(bob, {
        side: Side.Buy,
        tokenId,
        amount: 10,
        collection: mockERC1155.address,
        matchingPolicy: matchingPolicies.standardPolicyERC1155.address,
      });
      sellInput = await sell.pack();
      buyInput = await buy.pack();
 
      await waitForTx(exchange.execute(sellInput, buyInput));
 
      // Buyer only receives 1 token
      expect(await mockERC1155.balanceOf(bob.address, tokenId)).to.be.equal(1);
      await checkBalances(
        aliceBalance,
        aliceBalanceWeth.add(priceMinusFee),
        bobBalance,
        bobBalanceWeth.sub(price),
        feeRecipientBalance,
        feeRecipientBalanceWeth.add(fee),
      );
    });

출처: https://github.com/code-423n4/2022-10-blur-findings/issues/666

Impact

유저가 구매한 ERC1155 토큰을 전부 받지 못한다.

Mitigation

실제로 주문 요청한 amount를 이용해야 한다.


tags: bughunting, blur exchange, smart contract, solidity, erc1155, nft marketplace, logic flaw, severity high