Closed code423n4 closed 2 years ago
The title makes this sound worse than it is, as it's only under very specific and unusual circumstances that this would be possible (for instance, any tokenId between 0 and 100k would be immune to this), but it is a valid finding and we have a PR implementing a fix prepared
Dupe of #168
Lines of code
https://github.com/ProjectOpenSea/seaport/blob/878121af65be408462f3eae04ab81018b4e199da/contracts/lib/CriteriaResolution.sol#L157-L161
Vulnerability details
Impact
Orders in Seaport can involve "criteria-based items", in which case it isn't one specific item that can fulfill the order but any one of a set of items. This set of items is the "criteria", and it is specified in the order as the merkle root of a merkle tree that encodes the set (
identifierOrCriteria
). At the time of fulfillment, a particular item is specified along with a merkle proof to validate that it is a member of the set (CriteriaResolver
).The soundness of this mechanism relies on the inability to produce a merkle proof for an item that is not in that set. However, this assumption fails in two ways.
First, consider that the prefixes of valid merkle proofs are themselves valid merkle proofs for the inner nodes of the tree. As a result, the set of items that satisfy the criteria are not only the leaves but also the inner nodes, which may be valid item identifiers. This on its own is enough to cause unintended effects for the offerer, as the set of items offered may be larger than intended by them. But the second way in which the assumption fails is compounded by the potential for information asymmetry.
Consider that the identifier of one of the items in the set could itself be the root of a second merkle tree. There will be a valid proof for that item, but additionally the concatenation of that proof with any proof for the second tree will itself be a valid proof for the merkle root of the criteria. In that case, the set of items that satisfy the criteria is again larger than intended and includes all the items in the second merkle tree. The information asymmetry lies in the fact that an identifier is not known to be a merkle root until the leaves of the tree are disclosed.
This can be exploited by an attacker who publishes a collection of items some of whose identifiers are merkle roots for other items. For example, imagine a collection with a high-valued item
A
and a low-valued itemB
. An offerer that owns both items publishes an order to offer to exchangeB
for a relatively small consideration. The attacker knows thatB
is in fact the merkle root for the set{A, X}
(whereX
may be a random salt) and can thus produce a proof thatA
satisfies criteriaB
. The attacker can then fulfill the order and take itemA
, against the intent of the offerer, and presumably for a much lower consideration than what would be the market value of the item.Proof of Concept
Adapted from one of the tests in the Seaport test suite (Criteria-based offer item ERC1155 (standard)).
Recommended Mitigation Steps
In order to prevent this issue, there should be no overlap between leaves and inner nodes of the merkle tree used for criteria-based items.
A simple way to achieve this is to make the leaves of the tree not the item identifiers themselves (which are arbitrary values and may be merkle roots), but the keccak256 hashes of the identifiers. This ensures there is no overlap (assuming collision resistance), since inner nodes are known to be hashes of two words (the two children) and leaves will be known to be the hash of a single word.
The process of fulfilling an order would be the same: an item identifier is specified along with a merkle proof, but the merkle proof will not prove membership of the identifier in the tree, instead it will prove membership of the hash of the identifier in the tree.