In-depth Analysis of NBA NFT Contract Vulnerabilities: No Whitelisting, How Did Hackers Easily Exploit Them?

share
In-depth Analysis of NBA NFT Contract Vulnerabilities: No Whitelisting, How Did Hackers Easily Exploit Them?

Original Title: "In-depth Analysis: How did scientists exploit the hexadecimal contract loophole of the NBA?" published on 4/21, this article is authorized for reprint by the author. "Plucking the skin bald" refers to pulling until the skin is damaged.

Table of Contents

Original Title: "In-depth Analysis: How Did Scientists Exploit the Hexadecimal Contract Vulnerability in the NBA?", published on 4/21, with author's authorization for reprinting. "Exploit the Hexadecimal Contract Vulnerability" refers to exploiting vulnerabilities until the skin is completely peeled off.

The author of this article is Jason Chen, from the WeChat public account "Have You Learned a Bit More about the World Today?" You can follow the public account to read past articles and engage in in-depth discussions with the author, or follow the author on Twitter jason_chen998

This is currently the most exciting, longest, and most arduous article I have written, meticulously unraveling how scientists exploited loopholes to profit. Please be sure to read the entire article, as it will surely be very rewarding for you!

This morning, I woke up to see everyone in the group discussing the excitement NBA brought to scientists last night. It is said that some directly free minted 100, which, at the current floor price of 0.4 ETH, amounts to one million RMB. The reason was another loophole in the contract, so let's take a closer look at what happened.

NBA released their NFT series, The Association NFT, yesterday, creating 75 NFTs for each of the 240 players, totaling 18,000 supply. Whitelisted holders could free mint one, a key point to note. Previously mentioned in the article about Gh0stlyGh0sts, which was also free minted, only requiring payment of gas fees to mint and hold for appreciation. Given the visible profit potential, if loopholes such as bypassing the whitelist or minting restrictions are discovered, it's akin to shearing sheep's wool to the skin. Thus, whenever these NFT projects emerge, numerous scientists are vigilant in finding loopholes. This time, NBA played a trump card, breaching both the whitelist restriction and the mint one restriction.

This vulnerability also dealt a significant blow to users who painstakingly obtained whitelists. Those without whitelists could mint limitlessly, snatching up all the spots reserved for whitelisted users. Many users had initially spent thousands of dollars to acquire whitelists off-market. Additionally, a surge of scientists entering during minting led to skyrocketing gas fees, burdening whitelisted users with additional gas fees (as one of the whitelist's purposes is to avoid gas wars). Consequently, numerous users voiced complaints and sought justice, leading to a wail of sorrow among whitelisted users in the Discord community.

The issue primarily stems from two scenarios:

  1. Users bypassing the official website, directly interacting with the contract through MetaMask using pre-generated hexadecimal Input Data.
  2. A vulnerability in the whitelist verification of the contract during minting.

Let's start with the first scenario. Let me ask you a question: what are the various ways to interact with a smart contract for minting? You would likely mention clicking a "mint" button on the website to call the smart contract for minting, right? This is the most common and expected operation that all project teams hope users will perform. You must have experienced frantically clicking the mint button to participate in public sales.

Experienced users might also mention directly opening a blockchain explorer like Etherscan, finding the project's contract address, and interacting with the contract through the read and write contract functions. Many users have successfully participated in public sales using this method, as it bypasses the first step of triggering a request on the front end to interact with the contract, making the process faster. For those unfamiliar with this method, you can refer to my previous article on understanding NFT project situations through Etherscan.

However, this method may not work in two scenarios: when the project team has not open-sourced their contract or when the contract interface requires additional parameters like signatures that can only be generated through the project team's centralized servers, forcing users to operate through the project's official website.

There is actually a third method: directly interacting with the contract through the MetaMask wallet. You may not be familiar with this method, but you have been using it extensively without realizing it. The process of transferring funds is essentially interacting directly with the contract through your wallet.

When I transfer funds to an address, I first click "send."

Then I enter the recipient address and the amount I want to transfer.

You are very familiar with this process, as it is essentially your interaction with the contract. Since transferring funds involves sending money to an address where the recipient only needs to receive the funds without additional information, you may wonder how minting differs. Minting requires entering mint quantities and possibly whitelisting verification parameters, so how do you input these parameters?

We can open the MetaMask wallet settings, go to Advanced, and find a switch called "Display Hex Data" and turn it on.

What is this switch for? To explain what hexadecimal data is, you need to understand that whenever you interact with a contract, you input specific data that the contract processes. This data defines which interface function you are interacting with in the contract, what parameters to pass to this interface function, etc., all compressed in hexadecimal format.

If you open Etherscan, find any of your past transaction records, and scroll down, you will see the input data section where there is a long string of characters. This is the data you input when interacting with that contract function in hexadecimal format.

The key is that this long, irregular string of numbers may seem incomprehensible at first glance, but there is a pattern to it. Although the NBA's vulnerability doesn't require you to understand hexadecimal to exploit it, for the sake of thoroughness, we should understand the meaning behind it.

First, you will notice that after the "0x" at the beginning, there are several characters followed by all zeros.

These characters represent the encoding of the contract function you are calling, with each function having its unique code.

When you check a contract's transaction record, you will see some method calls with understandable text like mint, transfer, while others start with "0x" codes. Etherscan automatically translates standard function codes like mint and transfer into text because they are common operations. However, if a function is custom-developed by the project, only the original code is displayed.

Let's test interacting directly with a contract on the test network using this method. I found a deployed contract I had set up before and clicked into the completed mint transaction details.

Then, I copied the input data from that mint transaction.

After entering the contract address, pasting the copied input data, and clicking next.

Next is the step to pay gas fees.

By checking the data, we can see that the function type here is mint, confirming that we successfully called the contract with the hexadecimal input data.

Then, by confirming the gas fee payment, we can check on Etherscan to see that it was successful!

So far, we now understand that using existing transaction hexadecimal data can directly execute a contract function.

As mentioned earlier, the second reason for this issue was a loophole in the whitelist verification of the contract. This vulnerability allowed scientists to exploit using hexadecimal methods.

Next, let's examine the contract minting issues to understand what went wrong. Readers familiar with my previous articles should know that during minting, several verifications are usually performed, primarily including whether mint verification is enabled, quantity verification, whitelist verification, etc. Here's a breakdown of the mint contract used by NBA, which has three layers of verification:

The batchNumber is used to verify the mint batch, which is not the focus today and can be skipped for now.

The focus lies on the second layer, whitelist verification, and the third layer, quantity verification.

Here's the crux: the second layer verification uses a function called "verify," passing an "info" parameter to verify if the current user is on the whitelist. The issue lies here.

Before explaining the whitelist verification issue, it's essential to understand two common whitelist verification methods. In the early days of the NFT industry, many projects manually entered whitelists and verified whether the user's address matched the whitelist during minting. Each entry required a gas fee, resulting in high costs. Some spent tens of thousands of dollars just to enter the whitelist.

Gradually, people realized this method was costly and inefficient, leading to the adoption of a more technically challenging but cost-effective method using cryptographic signature verification like Merkle trees. This method involves storing the whitelist off-chain, managed by the project team, and generating a signature based on the user's wallet address using an algorithm during minting on the official website. NBA used this encrypted signature verification approach.

The specific encryption principles and code implementation are too complex to delve into now, but those interested can explore elliptic curve cryptography, Merkle trees, and related topics. For now, understanding the principle suffices. Just know that this verification method requires entering a wallet address to verify if it's on the whitelist, returning a signature if found.

You might start to sense a problem. If the verification involves entering an address and receiving a result, what if I use the hexadecimal interaction method mentioned earlier? Can't I simply input the hexadecimal data of a whitelisted user's executed transaction to achieve "whitelisted minting"? It's like using a fingerprint to unlock a door; you can use anyone's fingerprint to gain entry (a bit brutal).

Here's the catch: if the project adds another layer of verification, such as requiring the minting address to match the whitelist address, ensuring the unlocking fingerprint and entering person are the same, then the loophole is closed. But unfortunately, NBA did not address this.

Let's revisit the code. Initially, the contract gets the user's address currently interacting with the contract using msg.sender. During minting, this address is passed as part of the "info" parameter for verification.

Prior to minting, the contract verifies the "info" parameter, which includes the address obtained by the official website when the user interacts. The issue lies in this process.

Do you see where the problem lies? A user like Zhang San, with whitelist access, clicks mint on the official website. The website passes Zhang San's address into the "info" parameter for contract verification, which passes successfully. Then, during minting, the contract verifies Zhang San's address again and transfers the NFT to him. However, the contract fails to check if this Zhang San is the same as the interacting Zhang San! It doesn't verify if these two addresses are identical.

So, I can easily find Zhang San's transaction record, copy his hexadecimal data, paste it into the wallet, and execute the transaction. During whitelist verification, it uses Zhang San's address, passing the verification. But during minting, it uses my address because I'm the one interacting with the contract.

Case cracked! Isn't it fascinating? I chopped off Zhang San's finger to unlock the vault!

Let's see how the hexadecimal of users who minted through the whitelist looks. You can see this address successfully minted one NFT.

Now, let's look at the hexadecimal. There are two key points: the number 1 signifies that they minted one, and the address below is their own, matching up.

Now, let's examine the hexadecimal of the user who minted 69 NFTs, which caused a stir among scientists. Below are their address and the minting of 100 NFTs.

Let's see what their hexadecimal looks like. You'll notice the number 45. What does this mean? Doesn't it represent the quantity? But it's not 1 or 64; hexadecimal 45 converts to 69 in decimal, so this user changed the 1 to 45 to mint 64 instead.

You may wonder, wasn't there a limit of one NFT per person? Here's another loophole: while the code does impose restrictions, stating that your holdings cannot exceed 1 during minting, I minted 0, so this restriction doesn't apply to me. I could mint ten thousand; this restriction wouldn't stop me… I wonder what the contract engineer was thinking.

Next, let's look back at the hexadecimal data. If we check the contract address, it doesn't match the user who minted 69, indicating they used their hexadecimal to unlock. Let's see who the unlucky one is.

Entering their address, we see this user also has 30 ETH in their account, so being exploited didn't necessarily result in a loss.

Checking their transaction records, we see they indeed minted an NBA NFT last night, so it seems the user who minted 69 used their hexadecimal to unlock.

This should be one of the most exciting articles I've written, like solving a case layer by layer to explain how scientists fully exploited loopholes. It's also the most exhausting piece I've written, taking a whole 5 hours; writing isn't easy. If you found this useful, please share and spread the word.

I've emphasized in my previous articles that the open-source nature of the web3 world creates ample room for mischief. While the ethics of scientists' actions may be debatable, project teams have a responsibility to safeguard their users' interests and must approach technology with respect and a sense of awe, ensuring rigorous code reviews.