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
Rank: 67/72
Findings: 1
Award: $36.34
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: RaymondFam
Also found by: 0x1f8b, 0x52, 0xSmartContract, 0xc0ffEE, 0xhacksmithh, 8olidity, Awesome, BClabs, Bnke0x0, Chom, Deivitto, Hashlock, IllIllI, Josiah, KingNFT, Nyx, R2, ReyAdmirado, Rolezn, SamGMK, Sathish9098, SinceJuly, V_B, Vadis, Waze, a12jmx, adriro, ajtra, aphak5010, bearonbike, bin, brgltd, carlitox477, carrotsmuggler, cccz, ch0bu, chaduke, datapunk, delfin454000, erictee, fatherOfBlocks, fs0c, horsefacts, jayphbee, ktg, ladboy233, pashov, perseverancesuccess, rbserver, ret2basic, tnevler, zaskoh
36.3434 USDC - $36.34
Currently the function to buy NFT with ERC20 tokens in file ERC20EnabledLooksRareAggregator.sol
is implemented like this:
function execute( TokenTransfer[] calldata tokenTransfers, ILooksRareAggregator.TradeData[] calldata tradeData, address recipient, bool isAtomic ) external payable { if (tokenTransfers.length == 0) revert UseLooksRareAggregatorDirectly(); _pullERC20Tokens(tokenTransfers, msg.sender); aggregator.execute{value: msg.value}(tokenTransfers, tradeData, msg.sender, recipient, isAtomic); }
This is TokenTransfer struct, which contains ERC20 currency and amount the buyer deposit for buying NFT.
struct TokenTransfer { uint256 amount; // ERC20 transfer amount address currency; // ERC20 transfer currency }
Function execute
will call _pullERC20Tokens
and transfer ERC20 token from the caller to the aggregator.
However, the tradeData.order.currency
is the one used for purchase NFT, not tokenTransfer.currency
, and there is no checking if these 2 currencies actually match.
struct TradeData { ... BasicOrder[] orders; // Orders to be executed by the marketplace .... } struct BasicOrder { .... address currency; // The order's currency, address(0) for ETH ... }
This can lead to user can buy NFT for free or withdraw all stuck tokens:
Suppose there are some token A stuck in aggregator (and enough to buy some NFT)
The buyer create tradeData.order
with token A but input tokenTransfer.currency
as token B
He then call execute
and receive NFT without paying (since the amount of token A stuck in aggregator pay for him), and receive full
token B refund.
Manual source code review.
I recommend checking inputs between transferData
and tokenTransfer
to make sure that tokenTransfer
's tokens must contain all tokens from transferData.orders
.
#0 - Picodes
2022-11-21T14:03:37Z
I guess this is the current implementation to allow for maximal flexibility when integration exchanges.
Also:
originator
at the end of execute
so it's not any issue to send incorrect tokens#1 - c4-judge
2022-11-21T14:04:27Z
Picodes changed the severity to QA (Quality Assurance)
#2 - 0xhiroshi
2022-11-24T12:26:41Z
If any ERC20 tokens are sent to the contract by accident that are not for trades, we have decided it's free for all.
#3 - c4-sponsor
2022-11-24T12:27:02Z
0xhiroshi marked the issue as sponsor acknowledged
#4 - c4-judge
2022-12-11T13:17:30Z
Picodes marked the issue as grade-b