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: 23/72
Findings: 2
Award: $233.06
🌟 Selected for report: 1
🚀 Solo Findings: 0
196.7234 USDC - $196.72
https://github.com/code-423n4/2022-11-looksrare/blob/main/contracts/lowLevelCallers/LowLevelETH.sol#L35 https://github.com/code-423n4/2022-11-looksrare/blob/main/contracts/lowLevelCallers/LowLevelETH.sol#L46 https://github.com/code-423n4/2022-11-looksrare/blob/main/contracts/lowLevelCallers/LowLevelETH.sol#L57
The call
opcode's return value not checked, which could leads to the originator
lose funds.
The caller of LooksRareAggregator.sol::execute
could be a contract who may not implement the fallback
or receive
function, when a call to it with value sent, it will revert, thus failed to receive the ETH.
Let's imagine the contract call the execute
function to buy multiple NFTs with ETH as the payout currency and make the isAtomic
parameter being false. Since the batch buy of NFTs is not atomic, the failed transactions in LooksRare or Seaport marketplace will return the passed ETH. The contract doesn't implement the fallback/receive
function and the call opcode's return value not checked, thus the ETH value will be trapped in the LooksRareAggregator
contract until the next user call the execute
function and the trapped ETH is returned to him. The originator
lose funds.
function _returnETHIfAny(address recipient) internal { assembly { if gt(selfbalance(), 0) { let status := call(gas(), recipient, selfbalance(), 0, 0, 0, 0) // @audit-issue status not checked. } } }
Manual review
check the return value the call
opcode.
function _returnETHIfAny() internal { bool status; assembly { if gt(selfbalance(), 0) { status := call(gas(), caller(), selfbalance(), 0, 0, 0, 0) // @audit-issue [MED] status not checked } } if (!status) revert ETHTransferFail(); } function _returnETHIfAny(address recipient) internal { bool status; assembly { if gt(selfbalance(), 0) { status := call(gas(), recipient, selfbalance(), 0, 0, 0, 0) // @audit-issue status not checked. } } if (!status) revert ETHTransferFail(); } function _returnETHIfAnyWithOneWeiLeft() internal { bool status; assembly { if gt(selfbalance(), 1) { status := call(gas(), caller(), sub(selfbalance(), 1), 0, 0, 0, 0) } } if (!status) revert ETHTransferFail(); }
#0 - c4-judge
2022-11-19T10:07:32Z
Picodes marked the issue as primary issue
#1 - c4-judge
2022-11-21T16:17:23Z
Picodes marked the issue as selected for report
#2 - c4-sponsor
2022-11-24T17:27:57Z
0xhiroshi marked the issue as sponsor confirmed
#3 - c4-judge
2022-12-11T12:15:24Z
Picodes changed the severity to 2 (Med Risk)
#4 - Picodes
2022-12-11T12:15:58Z
Medium severity as only the dust is impacted.
#5 - c4-judge
2022-12-11T12:19:53Z
Picodes marked the issue as satisfactory
🌟 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
https://github.com/code-423n4/2022-11-looksrare/blob/main/contracts/TokenTransferrer.sol#L22
The transferFrom() method is used instead of safeTransferFrom(), presumably to save gas. I however argue that this isn’t recommended because:
OpenZeppelin’s documentation discourages the use of transferFrom(), use safeTransferFrom() whenever possible Given that any NFT can be used for the call option, there are a few NFTs (here’s an example) that have logic in the onERC721Received() function, which is only triggered in the safeTransferFrom() function and not in transferFrom()
In the TokenTransferer.sol::TokenTransferer::_transferTokenToReceipient
function the transferFrom() method is used instead of safeTransferFrom(), presumably to save gas. There are reasons not use transferFrom function:
Manual review
Call the safeTransferFrom() method instead of transferFrom() for ERC721 transfers.
if (collectionType == CollectionType.ERC721) { IERC721(collection).safeTransferFrom(address(this), recipient, tokenId); } else if (collectionType == CollectionType.ERC1155) { IERC1155(collection).safeTransferFrom(address(this), recipient, tokenId, amount, ""); }
#0 - c4-judge
2022-11-21T08:37:06Z
Picodes marked the issue as duplicate of #254
#1 - c4-judge
2022-11-21T08:42:39Z
Picodes marked the issue as duplicate of #174
#2 - c4-judge
2022-12-11T16:32:18Z
Picodes marked the issue as not a duplicate
#3 - c4-judge
2022-12-11T16:32:39Z
Picodes changed the severity to QA (Quality Assurance)
#4 - c4-judge
2022-12-11T16:33:24Z
Picodes marked the issue as grade-b