NFTX contest - cmichel's results

A community-owned protocol for NFT index funds

General Information

Platform: Code4rena

Start Date: 06/05/2021

Pot Size: $66,000 USDC

Total HM: 16

Participants: 11

Period: 6 days

Judge: cemozer

Total Solo HM: 9

Id: 8

League: ETH

NFTX

Findings Distribution

Researcher Performance

Rank: 1/11

Findings: 9

Award: $14,938.39

🌟 Selected for report: 11

🚀 Solo Findings: 3

Findings Information

🌟 Selected for report: cmichel

Also found by: a_delamo, janbro, pauliax

Labels

bug
duplicate
3 (High Risk)
Confirmed

Awards

1388.8806 USDC - $1,388.88

External Links

Handle

@cmichelio

Vulnerability details

Vulnerability Details

ERC20FlashMintUpgradeable.flashLoan does not check for an overflow when adding the fees to the flashloan amount. The functionality might have been copied from https://eips.ethereum.org/EIPS/eip-3156 but this one already has overflow checks as it uses solidity 0.8.0.

Impact

This leads to an issue where the attacker does not need to pay back the flashloan as they will burn 0 tokens:

_burn(address(receiver), amount + fee);

They end up with a huge profit.

Luckily, this is currently not exploitable as the fee is set to 0 so there's no possibility to overflow. However, if governance decides to change the flashloan fee, flashloans can be taken without having to repay them.

Use SafeMath.

#0 - 0xKiwi

2021-05-21T00:24:09Z

Upgraded to 0.8.x.

Findings Information

🌟 Selected for report: cmichel

Labels

bug
3 (High Risk)
Confirmed

Awards

999.994 USDC - $999.99

External Links

Handle

@cmichelio

Vulnerability details

Vulnerability Details

NFTXEligiblityManager._sendForReceiver should check returnData.length == 1 before decoding, otherwise if it returns no return data, the abi.decode call and with it the whole distribute function will revert.

Impact

A single badly implemented feeReceiver can break the whole distribute function and do a denial of service by reverting the transaction.

Change to: bool tokensReceived = returnData.length == 1 && abi.decode(returnData, (bool));.

#0 - cemozerr

2021-05-25T20:58:36Z

Marking this as high risk because one nefarious feeReceiver can in fact deny other users to receive their fees

Findings Information

🌟 Selected for report: cmichel

Also found by: gpersoon

Labels

bug
duplicate
3 (High Risk)
Acknowledged

Awards

3429.3348 USDC - $3,429.33

External Links

Handle

@cmichelio

Vulnerability details

Vulnerability Details

NFTXVaultUpgradeable.getRandomTokenIdFromFund does not work with ERC1155 as it does not take the deposited quantity1155 into account.

Impact

Assume tokenId0 has a count of 100, and tokenId1 has a count of 1. Then getRandomId would have a pseudo-random 1:1 chance for token 0 and 1 when in reality it should be 100:1.

This might make it easier for an attacker to redeem more valuable NFTs as the probabilities are off.

Take the quantities of each token into account (quantity1155) which probably requires a design change as it's currently hard to do without iterating over all tokens.

#0 - cemozerr

2021-05-25T21:10:45Z

Marking this as high risk as an attacker can weed out high-value NFTs from a vault putting other users funds at risk

#1 - 0xKiwi

2021-06-30T01:14:46Z

Hello. The rationale for assigning this as a high risk issue doesn't make much sense. If NFTs are in a pool its because they are all the same value. If anyone wants a rarer they can either just game it or perform a target redeem.

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: JMukesh, cmichel, gpersoon, maplesyrup, pauliax

Labels

bug
duplicate
2 (Med Risk)
Acknowledged

Awards

224.9987 USDC - $225.00

External Links

Handle

@cmichelio

Vulnerability details

Vulnerability Details

The NFTXVaultUpgradeable.getPseudoRand is not really random and can be predicted. It's also easy to make sure that one gets the correct token by having a smart contract simulate the randomness logic before the call to redeem / swap.

Impact

When redeeming one should only be able to specify the NFTs when enableDirectRedeem is true which allows specifying specificIds. With randomness that is resolved in the same transaction, one can circumvent this restriction and get the same behaviour as if enableDirectRedeem is true even though it's false. A smart contract can redeem and check the result of the redeem and if it's not the desired result, the transaction is reverted and retried.

This predictability can only be circumvented with a two-step process that doesn't resolve the randomness in the same step as the tokens are transferred.

Findings Information

🌟 Selected for report: cmichel

Labels

bug
2 (Med Risk)
Acknowledged

Awards

2286.2232 USDC - $2,286.22

External Links

Handle

@cmichelio

Vulnerability details

Vulnerability Details

NFTXEligiblityManager.distribute iterates over all _feeReceivers.

Impact

If the number of _feeReceivers gets too big, the transaction's gas cost could exceed the block gas limit and make it impossible to call distribute at all.

Keep the number of _feeReceivers small.

Findings Information

🌟 Selected for report: cmichel

Labels

bug
2 (Med Risk)

Awards

2286.2232 USDC - $2,286.22

External Links

Handle

@cmichelio

Vulnerability details

Vulnerability Details

The fees in NFTXVaultUpgradeable can be set arbitrarily high (no restriction in setFees).

Impact

The manager can frontrun mints and set a huge fee (for example fee = base) which transfers user's NFTs to the vault but doesn't mint any pool share tokens in return for the user.

Similar griefing attacks are also possible with other functions besides mint.

Check for a max fee as a percentage of base (like 10%) whenever setting fees.

Findings Information

🌟 Selected for report: cmichel

Also found by: pauliax

Labels

bug
2 (Med Risk)
Confirmed

Awards

2286.2232 USDC - $2,286.22

External Links

Handle

@cmichelio

Vulnerability details

Vulnerability Details

When dealing with ERC721 (instead of 1155) the amounts array is ignored, which leads to an issue.

User can call NFTXMintRequestEligibility.requestMint for an ERC721 with amounts[i] = 0. The ERC721.transferFrom is still executed but user cannot reclaimRequestedMint later and the NFT is stuck as it checks (amounts[i] > 0).

Impact

Tokens can get stuck. Also, subscribers to Request event could be tricked by specifying amounts[i] > 1 in the ERC721 case, as only one token was transferred but the amount multiple quantities get logged.

requestMint: Check amounts[i] == 1 in ERC721 case, amounts[i] > 0 in 1155 case.

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