Open code423n4 opened 2 years ago
Agree with assessment. Fixed. https://github.com/infinitydotxyz/exchange-contracts-v2/commit/c3c0684ac02e0cf1c03cdbee7e68c5a37fa334a8 and removed support for ERC1155
This is an interesting scenario where the same NFT appears multiple times in a match and results in one order being under filled, leading to potential losses for the user. And the attack does not depend on the matching engine. Agree this is High risk.
Lines of code
https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityOrderBookComplication.sol#L271-L312 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityOrderBookComplication.sol#L59-L116 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L245-L294 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityOrderBookComplication.sol#L118-L143 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L330-L364 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L934-L951 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityOrderBookComplication.sol#L145-L164 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L171-L243
Vulnerability details
Impact
Function
matchOneToManyOrders()
andtakeOrders()
andmatchOrders()
suppose to matchsell order
tobuy order
and should perform some checks to ensure that user specified parameters in orders which are signed are not violated when order matching happens. but There is no check in their execution flow to check that anorder
has differentNFT token ids
in each one of it's collections, so even so number of tokens could be valid inorder
toorder
transfer but the number of real transferred tokens and their IDs can be different than what user specified and signed. and user funds would be lost. (because ofERC1155
there can be more than one token for atokenId
, so it would be possible to transfer it)Proof of Concept
This is
_takeOrders()
andand
code:As you can see it uses
canExecTakeOrder()
to check that it is valid to perform matching. This iscanExecTakeOrder()
andareTakerNumItemsValid()
anddoTokenIdsIntersect()
code which are used in execution flow to check orders and matching validity:As you can see there is no logic to check that
token IDs
in one collection of order are different and code only checks that total number of tokens in oneorder
matches the number of tokens specified and the ids in one order exists in other list defined. functiondoTokenIdsIntersect()
checks to see thattokens ids
in one collection can match list of specified tokens. because of this check lacking there are some scenarios that can cause fund lose forERC1155
tokens (normalERC721
requires more strange conditions). here is first example:ERC1155
user1
has signed this order: A:(user1 BUY 3 NFT IDs[(1,1),(2,1),(3,1)] at 15 ETH)
(buy1
token of eachid=1,2,3
)NFT ID[1]
fair price is1 ETH
,NFT ID[2]
fair price is2 ETH
,NFT ID[3]
fair price is12 ETH
attacker
who has 3 ofNFT ID[1]
create this list: B:(NFT IDs[(1,1), (1,1), (1,1)] )
(list to trade1
token ofid=1
for 3 times)takeOrders()
with this parameters: makerOrder: A , takerNfts: Battacker
would receive15 ETH
for his 3 token ofNFT ID[1]
and stealuser1
funds.user1
would receive 3 ofNFT ID[1]
and pays15 ETH
and even so his order A has been executed he doesn't receiveNFT IDs[(2,1),(3,1)]
and contract would violates his signed parameters.This examples shows that in verifying one to many order code should verify that one order's one collection's token ids are not duplicates. (the function
doTokenIdsIntersect()
doesn't check for this).This scenario is performable to
matchOneToManyOrders()
andmatchOrders()
and but exists in their code (related check logics) too. more important things about this scenario is that it doesn't require off-chain maching engine to make mistake or malicious act, anyone can calltakeOrders()
if NFT tokens areERC1155
. for otherNFT
tokens to perform this attack it requires thatseller==buyer
or some other strange cases (like auto selling when receiving in one contract).Tools Used
VIM
Recommended Mitigation Steps
add checks to ensure
order
's onecollection
's token ids are not duplicate indoTokenIdsIntersect()