IOP _ ThunderNFT 34957 - [Smart Contract - Critical] executionResults always returns an amount of le

Submitted on Sun Sep 01 2024 20:02:08 GMT-0400 (Atlantic Standard Time) by @SimaoAmaro for IOP | ThunderNFT

Report ID: #34957

Report type: Smart Contract

Report severity: Critical

Target: https://github.com/ThunderFuel/smart-contracts/tree/main/contracts-v1/libraries

Impacts:

  • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

Description

Brief/Intro

Executed orders always transfer 1 nft to the taker, when the maker order may have deposited more than 1 amount of the given collection and token id nft to the ThunderExchange.

Vulnerability Details

When executing orders in ThunderExchange::executeOrder(), it gets the result of the execution from the picked strategy, strategy_fixed_price_sale in this case, more precisely from ExecutionResult::s1(), which always returns an amount of 1.

However, orders may be place with an amount bigger than 1 as the ThunderExchange supports erc1155 style nfts. Thus, The following scenario will happen:

  1. User places an order of a certain erc1155 style nft with an amount bigger than 1.

  2. Another user executes the order, but only receives 1 nft, instead of the whole amount in the order. The remaining nfts get stuck in the ThunderExchange.

Impact Details

Stuck nfts in the ThunderExchange.

References

https://github.com/ThunderFuel/smart-contracts/blob/main/contracts-v1/libraries/src/execution_result.sw#L31

Proof of concept

Proof of Concept

To run a proof of concept, the exchange was modified to allow a maker of type Contract, as Sway tests do not support pranking an EOA.

Additionally, 3 new contracts were created, one user contract simulating a user placing an order, another user contract simulating a user executing the order and a erc1155 contract, simulating an erc1155 token.

The full changes were pushed to a github repository which can be shared with the team if requested.

The main test file is the following:

The user contract placing the order is:

The user contract executing the order is:

The erc1155 is the same as the erc721, but the mint function had the checks removed to allow more than 1 coin per token id:

Last updated

Was this helpful?