Art Gobblers contest - hansfriese's results

Experimental Decentralized Art Factory By Justin Roiland and Paradigm.

General Information

Platform: Code4rena

Start Date: 20/09/2022

Pot Size: $100,000 USDC

Total HM: 4

Participants: 109

Period: 7 days

Judge: GalloDaSballo

Id: 163

League: ETH

Art Gobblers

Findings Distribution

Researcher Performance

Rank: 13/109

Findings: 3

Award: $2,383.77

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
duplicate
3 (High Risk)

Awards

1858.2053 USDC - $1,858.21

External Links

Lines of code

https://github.com/code-423n4/2022-09-artgobblers/blob/2ae644ace932271fb34399c1c0771aabae2e423c/src/ArtGobblers.sol#L441

Vulnerability details

Impact

Users can recover already burned gobblers after minting a legendary gobbler.

The main flaw is that it doesn't reset getApproved[id] here.

As a result, users can have more emissionMultiple than they should by recovering the burned gobblers.

Proof of Concept

The below attack scenario is possible.

  • Alice has certain number of gobblers like 50 and she sets getApproved[id] = Alice for her gobblers here.
  • After that, she mints a legendary gobbler by burning her gobblers.
  • But getApproved[id] will be remaining here.
  • Alice transfers already burned NFTs to herself using transferFrom() with from = address(0), to = Alice.
  • Then this one will be passed properly because the owner of the burned NFT is address(0).
  • Also, these calculations will work because they are inside the unchecked block.
  • Finally, she can receive emissionMultiple again from the burned tokens here and she will get more GOO tokens.

Tools Used

Solidity Visual Developer of VSCode

We should delete getApproved[id] here.

#0 - csanuragjain

2022-09-27T17:27:21Z

Findings Information

Labels

bug
duplicate
2 (Med Risk)

Awards

470.3582 USDC - $470.36

External Links

Lines of code

https://github.com/code-423n4/2022-09-artgobblers/blob/2ae644ace932271fb34399c1c0771aabae2e423c/src/ArtGobblers.sol#L546

Vulnerability details

Impact

ArtGobblers contract wouldn't receive a random seed forever by a malicious user.

Currently it can't execute revealGobblers() and upgradeRandProvider() when gobblerRevealsData.waitingForSeed == true.

So if the contract fails to receive the random seed after requesting it, waitingForSeed will be true forever and the whole logic will be messy because the contract can't reveal new gobblers.

Proof of Concept

  • As we can see from this document, VRF V1 always waited for 10 blocks on Ethereum before delivering on-chain randomness.
More configuration capability: VRF V1 always waited for 10 blocks on Ethereum before delivering on-chain randomness.
If your fulfillRandomness implementation reverts, the VRF service will not attempt to call it a second time. Make sure your contract logic does not revert. Consider simply storing the randomness and taking more complex follow-on actions in separate contract calls made by you or your users.
  • This article shows it's possible to prevent other transactions for the small number of blocks with a high gas price.
  • So if a malicious user prevents other txs at most 10 blocks after the ArtGobblers contract requested a new random seed, gobblerRevealsData.waitingForSeed will be true permanently and the contract can't reveal NFTs.

Tools Used

Solidity Visual Developer of VSCode

I think we should reset gobblerRevealsData.waitingForSeed = false in upgradeRandProvider() so that the admin can upgrade a RandProvider when there is a problem with the previous one even though gobblerRevealsData.waitingForSeed == true.

function upgradeRandProvider(RandProvider newRandProvider) external onlyOwner { gobblerRevealsData.waitingForSeed = false; randProvider = newRandProvider; // Update the randomness provider. emit RandProviderUpgraded(msg.sender, newRandProvider); }

Otherwise, we can add a new function like ResetWaitingForSeed() to reset gobblerRevealsData.waitingForSeed = false.

function ResetWaitingForSeed() external onlyOwner { gobblerRevealsData.waitingForSeed = false; }

#0 - Shungy

2022-09-27T17:25:47Z

<del>requestRandomSeed() can be called again after a day of wait, regardless if the waitingForSeed is true or not. I don't think this is a death sentence. I guess DOS can be sustained for a while by a group with sufficient funds, but it would be too costly. Not sure if it warrants a High risk, but there is definitely some Med risk issue trusting the randomness provider that it will eventually call acceptRandomSeed(). I saw an issue with this design as well, and described it in a similar if not duplicate finding: https://github.com/code-423n4/2022-09-artgobblers-findings/issues/279</del>

Edit: Just realized requestRandomSeed cannot be re-requested until toBeRevealed is consumed: https://github.com/code-423n4/2022-09-artgobblers/blob/2ae644ace932271fb34399c1c0771aabae2e423c/src/ArtGobblers.sol#L517 This makes my previous paragraph mostly wrong. And it actually makes this submission stand out from the other submissions about reveals getting halted. Because this submission shows a very probably route on how this can happen. A well-funded malicious party could do it.

#2 - hansfriese

2022-09-30T05:19:38Z

@GalloDaSballo, can I ask one thing with this issue if you don't mind? I think #153 shows it wouldn't work properly when the randProvider stops working. But this issue shows it won't work by a malicious user even if the randProvider is normal. I am wondering if these issues should be considered as duplicates.

#3 - GalloDaSballo

2022-09-30T22:07:10Z

@GalloDaSballo, can I ask one thing with this issue if you don't mind? I think #153 shows it wouldn't work properly when the randProvider stops working. But this issue shows it won't work by a malicious user even if the randProvider is normal. I am wondering if these issues should be considered as duplicates.

Have added a note to check again

After thinking about it twice am not convinced that it is reasonable to expect that an attacker could deny a tx for 10 blocks in a row, also I'll need to look into why the VRF would drop the TX vs it just being stuck in the mempool.

I will re-check it though to give it the benefit of the the doubt

[N-01] Use two-phase ownership transfers

ArtGobblers and GobblerReserve contracts inherite the Owned contract and use setOwner() function without any two-phase ownership transfers.

[N-02] Immutable addresses should be 0-checked

[N-03] Event is missing indexed fields

Each event should use three indexed fields if there are three or more fields

#0 - GalloDaSballo

2022-10-06T19:19:40Z

[N-01] Use two-phase ownership transfers

NC

[N-02] Immutable addresses should be 0-checked

L

I disagree with the event indexed without an explanation

1L 1NC

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