Today, I’m releasing a proof of concept exploit contract for the Moonbirds NFT collection.
The exploit code is here https://github.com/dhanjani/MoonbirdsSploit/blob/main/MoonbirdsSploit.sol.
Don't worry. The bad guys and girls don't need my proof of concept code. They have either already figured it out, or can figure it out on their own. The intention of the proof of concept is to promote the community understanding of what the bug is and how it can be abused.
The nature of smart contracts is that their code is on the blockchain and readable by everyone, so in time, more and more people will find any given bug. Keeping it secret and not informing the community is not an option. Mitigating strategizes need to be put in place and the community informed.
Moonbirds grossed $58 million the day it was launched and since has increased its market cap multiple folds. The highest price paid for one Moonbird NFT to date is about $1,026,778.
The Moonbirds collection is the second NFT drop from its parent PROOF collective project that also has an impressive market cap, the highest PROOF NFT sold for approximately $413,901 at one point.
[Disclosure: As of this writing, I own 4 PROOF NFTs and 5 Moonbirds NFTs.]
The Moonbirds eco-system relies on a staking mechanism called ‘nesting’. When an NFT owner ‘nests’ their bird, the contract does not allow for the NFT to be sold on the secondary market. This is a creative mechanism to incentivize loyalty and rewards are issued to NFT holders frequently as long the birds are ‘nested’.
However, the Moonbirds smart contract has a vulnerability that allows the nesting mechanism to be circumvented. The code for the Moonbirds contract can be found here: https://etherscan.io/address/0x23581767a106ae21c074b2276d25e5c3e136a68b#code.
Figure: Successful exploit transaction demonstrating sale of nested bird on the rinkeby test network.
Technical details:
- The safeTransferWhileNesting function sets the value of a private variable nestingTransfer to 2. This function allows for the transfer of nested birds. As long as the value of this is set to 2, the contract functions that perform the actual transfer will allow it.
- The ERC721 https://erc721.org standard specifies that transfers must check to see if the destination is a smart contract (and not a regular wallet), and if it is, it should be confirmed that the destination contract inherits IERC721Receiver and implements onERC721Received. This check is to make sure that the contract has mechanisms for the owner to be able to access and transfer the NFT, otherwise sending the NFT to an NFT-unaware smart contract may render the token unrecoverable.
- The Moonbirds contract implements this check and does a callback to the destination contract. If the destination contract is malicious (such as the proof of concept exploit code), it can in turn call back the Moonbirds contract to transfer nested birds because nestingTransfer will still be set to 2. This is the reentrancy condition.
- As shown in the exploit code, this vulnerability can be abused to purchase nested Moonbirds on the secondary market ex: LooksRare.
Thoughts on risk:
- There are no formal standards for staking of NFTs and its courageous of the PROOF team to be one of the first to implement such a creative mechanism. However, because there is no standard for this, you have to work with individual marketplaces to avoid glitches, i.e., if a nested bird is listed for sale, anyone attempting to buy it will be confused because the sale order will not go through.
- The team has done a good job of working with OpenSea to customize the handling of Moonbirds so that nested birds are hidden from the sale catalog. This lowers the risk tremendously because OpenSea is the most popular market platform.
- There is also a mechanism in the contract for the project team to ‘expel’ individual birds for cases where someone may intentionally list a bird lower than the floor price to cause panic. However, this requires constant monitoring and manual contract transactions. The project team has done a good job of warning users that have nested birds for sale and then following through with expelling them.
- It will be interesting to see if this bug contributes to nervousness from a floor price point because the staking mechanism can be circumvented. If anyone can sell nested birds, what’s the value of nesting? The counterpoint to this notion is that majority of people don’t want to get in the business of implementing smart contracts to do this. The counterpoint to the counterpoint is that someone could take the proof of concept exploit and extend it (or start from scratch on their own) to create a marketplace customized to Moonbirds where people can trade nested birds that are listed on LooksRare and other secondary marketplaces (and take additional royalties for themselves).
- My prediction is that the project team will announce additional mechanisms to mitigate the risk in a creative way so it ultimately preserves the confidence in the collection.
On responsible disclosure and my experience:
- I first reported the bug to the team on May 13 2022, and followed up on May 23 and May 24. On May 29, there was an announcement on the community Discord that a bug exists and that details will be made available on May 31.
- My intention since the day 1 has been to make sure the Moonbirds community is informed of the bug as soon as possible, and that is what I pushed for. The code is on the blockchain. Others will discover it if we don’t preemptively acknowledge it ourselves, otherwise unfriendly parties can and will leverage it for FUD.
- I am disappointed in the way communication was handled the entire time.
- I will chalk up this experience to be a result of the immense explosion of the PROOF collective and Moonbirds project, and that the team is understaffed. I have confidence this process will improve.
Here are my recommendations to PROOF and also other NFT projects:
- Make sure your devs have dropped their ego.
- Take the processing and handling of security bugs very seriously. A fundamental security bug in a smart contract has the tendency to take a $100+ million dollar market cap NFT collection to zero, instantly hurting all of the community.
- Publish a formal responsible disclosure policy.
- Have a crisp process outlined on the project websites that clearly lists contact information to report security bugs.
- Strongly consider participating in a bug bounty program. Many serious bugs are a result of the researcher performing a free assessment that would’ve costed hundreds of thousands of dollars. The the bug itself may be worth millions of dollars on the underground market.
- If no bug bounty program is in place, consider guidelines for at least acknowledging all researchers that report the issue (in cases where it’s a handful). This incentivizes people to report their findings, especially in case of researchers that are early in their career.
- Many of the folks coming into the web3 Universe are young (not me), eager, and so full of energy, including the security researchers that often submit bugs for free. They bring with them an immense amount of integrity, principle, and character. Let’s embrace this. Let’s not give them the bureaucratic run-around and lawyer-speak so rampant in the web2 space. No, let’s not do this in web3.
To wrap up, the ideas and creativity in the DeFI/NFT space are so exciting, and we are yet to see even more amazing products that are going to be delivered in short order, iteratively. Let’s make sure we deliver securely, together.
We’re still early.
LFG🚀