LooksRare Aggregator contest - KingNFT's results

An NFT aggregator protocol.

General Information

Platform: Code4rena

Start Date: 08/11/2022

Pot Size: $60,500 USDC

Total HM: 6

Participants: 72

Period: 5 days

Judge: Picodes

Total Solo HM: 2

Id: 178

League: ETH

LooksRare

Findings Distribution

Researcher Performance

Rank: 2/72

Findings: 2

Award: $10,871.41

QA:
grade-b

🌟 Selected for report: 1

🚀 Solo Findings: 1

Findings Information

🌟 Selected for report: KingNFT

Labels

bug
2 (Med Risk)
satisfactory
selected for report
sponsor acknowledged
edited-by-warden
M-01

Awards

10835.0653 USDC - $10,835.07

External Links

Lines of code

https://github.com/code-423n4/2022-11-looksrare/blob/e3b2c053f722b0ca2dce3a3eb06f64859b8b7a6f/contracts/proxies/SeaportProxy.sol#L224

Vulnerability details

Impact

The '_executeNonAtomicOrders' function in SeaportProxy.sol tries to send fee by batch, this may break the 'NonAtomic' feature.

Proof of Concept

Let's say user want to buy 3 NFTs with following order parameters

NFT_1: price = 100 USDT, fee = 5% NFT_2: price = 200 USDT, fee = 5% NFT_3: price = 300 USDT, fee = 5%

Given user only sends 600 USDT, the expected result should be

NFT_1: success NFT_2: success NFT_3: failed

But as the fees are batched and sent at last, cause all 3 orders failed.

function _executeNonAtomicOrders( BasicOrder[] calldata orders, bytes[] calldata ordersExtraData, address recipient, uint256 feeBp, address feeRecipient ) private { uint256 fee; address lastOrderCurrency; for (uint256 i; i < orders.length; ) { OrderExtraData memory orderExtraData = abi.decode(ordersExtraData[i], (OrderExtraData)); AdvancedOrder memory advancedOrder; advancedOrder.parameters = _populateParameters(orders[i], orderExtraData); advancedOrder.numerator = orderExtraData.numerator; advancedOrder.denominator = orderExtraData.denominator; advancedOrder.signature = orders[i].signature; address currency = orders[i].currency; uint256 price = orders[i].price; try marketplace.fulfillAdvancedOrder{value: currency == address(0) ? price : 0}( advancedOrder, new CriteriaResolver[](0), bytes32(0), recipient ) { if (feeRecipient != address(0)) { uint256 orderFee = (price * feeBp) / 10000; if (currency == lastOrderCurrency) { fee += orderFee; } else { if (fee > 0) _transferFee(fee, lastOrderCurrency, feeRecipient); lastOrderCurrency = currency; fee = orderFee; } } } catch {} unchecked { ++i; } } if (fee > 0) _transferFee(fee, lastOrderCurrency, feeRecipient); }

Tools Used

VS Code

Don't batch up fees.

#0 - 0xhiroshi

2022-11-23T14:10:05Z

I would treat this as a malicious user trying to buy NFTs without paying for fees as he should be paying 600 x 1.05 = 630 instead of 600. So I think it's ok to just let it revert.

@0xJurassicPunk thoughts?

#1 - 0xhiroshi

2022-11-23T14:10:18Z

I would treat this as a malicious user trying to buy NFTs without paying for fees as he should be paying 600 x 1.05 = 630 instead of 600. So I think it's ok to just let it revert.

@0xJurassicPunk thoughts?

#2 - c4-sponsor

2022-11-23T14:10:38Z

0xhiroshi marked the issue as sponsor disputed

#3 - c4-sponsor

2022-11-23T14:10:48Z

0xhiroshi marked the issue as sponsor acknowledged

#4 - c4-judge

2022-12-11T12:48:12Z

Picodes marked the issue as satisfactory

#5 - ydspa

2022-12-19T01:40:04Z

Sorry for not indicated clearly, the reason why i think this may happen is that the NFT price may increase during submitting of tx, not due to a malicious user.

#6 - 0xhiroshi

2022-12-19T08:51:29Z

Sorry for not indicated clearly, the reason why i think this may happen is that the NFT price may increase during submitting of tx, not due to a malicious user.

Even if the seller submits a new listing with a higher price, the current transaction should still be valid. When there are two listings for the same NFT, they are both valid unless the seller explicitly cancels the original order on-chain, usually through the cancellation of an order nonce.

[L-01] Miss check for 'recipient' in 'setFee()' function

function setFee( address proxy, uint256 bp, address recipient // @audit miss check if it's address(0) ) external onlyOwner { if (bp > 10000) revert FeeTooHigh(); _proxyFeeData[proxy].bp = bp; _proxyFeeData[proxy].recipient = recipient; emit FeeUpdated(proxy, bp, recipient); }

https://github.com/code-423n4/2022-11-looksrare/blob/e3b2c053f722b0ca2dce3a3eb06f64859b8b7a6f/contracts/LooksRareAggregator.sol#L156

[L-02] Miss check for 'to' in 'rescueERC721()' function

function rescueERC721( address collection, address to, // @audit miss check 'to' uint256 tokenId ) external onlyOwner { _executeERC721TransferFrom(collection, address(this), to, tokenId); }

https://github.com/code-423n4/2022-11-looksrare/blob/e3b2c053f722b0ca2dce3a3eb06f64859b8b7a6f/contracts/LooksRareAggregator.sol#L197

[L-03] Miss check for 'to' in 'rescueERC1155()' function

function rescueERC1155( address collection, address to, // @audit miss check 'to' uint256[] calldata tokenIds, uint256[] calldata amounts ) external onlyOwner { _executeERC1155SafeBatchTransferFrom(collection, address(this), to, tokenIds, amounts); }

https://github.com/code-423n4/2022-11-looksrare/blob/e3b2c053f722b0ca2dce3a3eb06f64859b8b7a6f/contracts/LooksRareAggregator.sol#L213

[L-04] It's recommended to use 'safeTransferFrom' interface to transfer ERC721 token

function _executeERC721TransferFrom( address collection, address from, address to, uint256 tokenId ) internal { (bool status, ) = collection.call(abi.encodeWithSelector(IERC721.transferFrom.selector, from, to, tokenId)); // @audit not safe if (!status) revert ERC721TransferFromFail(); }

https://github.com/code-423n4/2022-11-looksrare/blob/e3b2c053f722b0ca2dce3a3eb06f64859b8b7a6f/contracts/lowLevelCallers/LowLevelERC721Transfer.sol#L27

#0 - c4-judge

2022-11-21T19:45:42Z

Picodes marked the issue as grade-b

#1 - 0xhiroshi

2022-11-23T14:46:52Z

L-01: Invalid L-02: Invalid L-03: Invalid L-04: Valid

#2 - c4-sponsor

2022-11-23T14:47:00Z

0xhiroshi requested judge review

#3 - 0xhiroshi

2022-12-12T23:35:34Z

@Picodes what is the edict of this report?

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