Foundation contest - cccz's results

Building the new creative economy

General Information

Platform: Code4rena

Start Date: 24/02/2022

Pot Size: $75,000 USDC

Total HM: 21

Participants: 28

Period: 7 days

Judge: alcueca

Total Solo HM: 15

Id: 94

League: ETH

Foundation

Findings Distribution

Researcher Performance

Rank: 6/28

Findings: 2

Award: $3,530.39

🌟 Selected for report: 1

πŸš€ Solo Findings: 1

Findings Information

🌟 Selected for report: cccz

Labels

bug
2 (Med Risk)
disagree with severity
sponsor confirmed

Awards

2434.7549 USDC - $2,434.75

External Links

Lines of code

https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/SendValueWithFallbackWithdraw.sol#L37-L77

Vulnerability details

Impact

The NFTMarketFees contract and the NFTMarketReserveAuction contract use the _sendValueWithFallbackWithdraw function to send ether to FoundationTreasury, CreatorRecipients, Seller, Bidder. When the receiver fails to receive due to some reasons (exceeding the gas limit or the receiver contract cannot receive ether), it will record the ether to be sent in the pendingWithdrawals variable.

function _sendValueWithFallbackWithdraw( address payable user, uint256 amount, uint256 gasLimit ) internal { if (amount == 0) { return; } // Cap the gas to prevent consuming all available gas to block a tx from completing successfully // solhint-disable-next-line avoid-low-level-calls (bool success, ) = user.call{ value: amount, gas: gasLimit }(""); if (!success) { // Record failed sends for a withdrawal later // Transfers could fail if sent to a multisig with non-trivial receiver logic unchecked { pendingWithdrawals[user] += amount; } emit WithdrawPending(user, amount); } }

The user can then withdraw ether via the withdraw or withdrawFor functions.

function withdraw() external { withdrawFor(payable(msg.sender)); } function withdrawFor(address payable user) public nonReentrant { uint256 amount = pendingWithdrawals[user]; if (amount == 0) { revert SendValueWithFallbackWithdraw_No_Funds_Available(); } pendingWithdrawals[user] = 0; user.sendValue(amount); emit Withdrawal(user, amount); }

However, the withdrawFor function can only send ether to the address recorded in pendingWithdrawals. When the recipient is a contract that cannot receive ether, these ethers will be locked in the contract and cannot be withdrawn.

Proof of Concept

https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/SendValueWithFallbackWithdraw.sol#L37-L77

Tools Used

None

Add the withdrawTo function as follows:

function withdrawTo(address payable to) public nonReentrant { uint256 amount = pendingWithdrawals[msg.sneder]; if (amount == 0) { revert SendValueWithFallbackWithdraw_No_Funds_Available(); } pendingWithdrawals[msg.sneder] = 0; to.sendValue(amount); emit Withdrawal(msg.sneder, amount); }

#0 - HardlyDifficult

2022-02-25T19:58:54Z

We believe this is better classified as a 2 (Med Risk).

  • Worst case is funds for a user are trapped in escrow, however they are correctly attributed to that user and cannot be accessed by any other account. If we did not address this by launch, the issue could be corrected post launch via a contract upgrade and that user would then be made whole. So the funds are just temporarily unavailable to them.

This is a great report and we will be making a change to address the problem!

When reviewing the recommended mitigation we identified a new potential risk that could introduce. So we are going a slightly different way...

The _sendValueWithFallbackWithdraw function will send FETH tokens when it fails to transfer ETH. For any account that currently has a non-zero pendingWithdrawals we will include a migration function allowing us to move the escrowed ETH out of the market contract and into that user's FETH account.

Once that migration has been completed, we will delete the migration function and withdraw* functions -- simplifying the market contract and freeing up much needed contract size to make room for new features in the future.

#1 - alcueca

2022-03-18T10:59:34Z

The combination of upgradability with the limited scope of the vulnerability makes it reasonable to downgrade this to a medium severity.

Findings Information

🌟 Selected for report: leastwood

Also found by: cccz

Labels

bug
duplicate
2 (Med Risk)

Awards

1095.6397 USDC - $1,095.64

External Links

Lines of code

https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L263-L292

Vulnerability details

Impact

The adminAccountMigration function will update auction.seller to newAddress. Consider the following situation:

  1. User A calls the setBuyPrice function and the createReserveAuction function to set the buy price and start the auction for his NFT.

  2. The adminAccountMigration function is called to update auction.seller to newAddress.

  3. User B wants to call the buy function to buy user A's NFT. Since buyPrice.seller != auction.seller at this point, the purchase will fail.

function _transferFromEscrow( address nftContract, uint256 tokenId, address recipient, address seller ) internal virtual override { uint256 auctionId = nftContractToTokenIdToAuctionId[nftContract][tokenId]; if (auctionId != 0) { ReserveAuction storage auction = auctionIdToAuction[auctionId]; if (auction.endTime == 0) { // The auction has not received any bids yet so it may be invalided. if (auction.seller != seller) { // The account trying to transfer the NFT is not the current owner. revert NFTMarketReserveAuction_Not_Matching_Seller(auction.seller); }

Proof of Concept

https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L263-L292

https://github.com/code-423n4/2022-02-foundation/blob/main/contracts/mixins/NFTMarketReserveAuction.sol#L539-L542

Tools Used

None

Consider updating buyPrice.seller in the adminAccountMigration function

#0 - HardlyDifficult

2022-03-03T13:32:57Z

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter