Hunting the exploits in the Cardano NFT ecosystem
Hello, we are JAM ON BREAD, the fastest decentralized NFT marketplace built on Cardano, powered by Vacuumlabs. We want to contribute to this amazing ecosystem as much as possible, so we decided to bring you this article.
To spread awareness and to help developers, we’ll describe the most common critical exploit we have found in multiple NFT marketplaces on Cardano. We’ll go through the whole story of how the form of this exploit evolved as it appeared in newer and newer marketplaces. We first found this problem back in 2021 in three CNFT marketplaces. From there, similar exploits appeared in different forms in two others, the most recent one being epoch.art marketplace in April 2022. We contacted all affected marketplaces.
All of them except epoch.art reacted promptly to protect their users, which we greatly appreciate. Sadly, we have to do the exploit announcement instead of epoch.art themselves, as it is taking them too long and we want to protect their users. We describe their exploit at the end of the article. Let’s dive into the story!
As smart contract capabilities arrived to Cardano, developers started working on the first decentralized applications on our beloved blockchain. Cardano’s eUTxO model is novel, and a different type of thinking needs to be put into the design of smart contracts. A few attack vectors are well known to developers with a deep understanding of the eUTxO model but are not known to newcomers and are easy to miss for them. As we have years of experience building on Cardano, we were able to identify three similar exploits that appeared across multiple NFT marketplaces.
After discovering the problem in jpg.store, genesishouse and adapix NFT marketplaces back in 2021, we immediately let them know. We also agreed on not explaining how the attack works to the general public for some time. We did it to give people an opportunity to delist their NFTs from vulnerable smart contracts without bad actors trying to take advantage of the situation.
How exploitable NFT marketplaces work
What flow should we expect while interacting with a smart contract that lets users list and buy NFTs for a fixed price set by sellers?
A seller chooses an NFT from their wallet and specifies a price for which they want to sell this NFT. Then, they create a transaction with an output that is locked under the smart contract. This output stores the NFT and information about the seller and the price.
When someone wants to buy this NFT, they need to create a transaction that gets multiple inputs and outputs:
The first input is from the smart contract and holds the NFT (with seller and price information). The second input is from the buyer and contains enough ADA to pay for the NFT.
The first output is an NFT going to the buyer and the second output is ADA going to the seller.
For the transaction to be valid, both inputs have to have the right to be spent in it. The ADA input can only be spent if the buyer signs the transaction. What about the smart contract input? The smart contract needs to approve this transaction. It has to check that there exists an output that goes to the seller and contains enough ADA to pay for the NFT. It does not necessarily have to check whether the NFT goes to the buyer because we know that the buyer signed the transaction and therefore approves it as it is.
Seems secure enough, right? The buyer has to pay enough to the seller. What could possibly go wrong?
The problem is not in ordinary transactions assembled by marketplaces themselves. Instead, the problem is that anyone can assemble a transaction with arbitrary inputs and outputs and submit it. If such a transaction validates, inputs get spent, and outputs are created.
Imagine a situation where a seller, let us call them A, sells multiple NFTs on the same marketplace. Each of those NFTs, together with information about seller A and a price, is locked under the same smart contract. As a result, a bad actor could create the following transaction. As inputs, they put:
- All of the NFTs that A sells
- Enough ADA to pay for the most expensive of A’s NFTs
As outputs, they put:
- All of the NFTs that A sells going the bad actor
- Enough ADA to pay for the most expensive of A’s NFTs going to A
For each of the smart contract inputs, a validation is performed. Each of these NFT inputs checks whether an output with enough ADA exists to pay for this particular NFT going to seller A. As the single output that goes to seller A contains enough ADA to pay for the most expensive NFT, all of the NFT inputs are satisfied with this single output and approve the transaction.
This way, a bad actor could only pay for one NFT and get other, cheaper NFTs from the same seller for free. This attack has been known for years. It is described in the ErgoScript whitepaper from 2019. It was also mentioned in a blogpost from our beloved pioneers in the space: Spacebudz. Yet, we found this exploit in all Cardano NFT marketplaces with open source code in 2021. Likely, this problem was also present in marketplaces that did not have their smart contracts published, but we could not verify that.
All platforms affected during 2021 reacted promptly and fixed their contracts after we approached them and helped some of them with the fix. Yet, this was not the end of the story…
The exploit returns
After some time passed, we decided to review all the contracts we could find again. As most of the marketplaces unpublished their code after the first exploit, we were only able to find one — Martify.
Martify’s contract tried to fix the problem by limiting the number of script inputs that are locked in this marketplace’s contract to 1. This way, the transaction constructed by a bad actor in the previous example fails to validate because it has more than one input from this marketplace’s contract. Do you see a problem there?
The added check is too weak, and it only partially fixes the problem. Let’s call this marketplace X. Imagine that there is another marketplace called Y. This Y uses a similar smart contract with the same fix. There is a seller B that sells one NFT on marketplace X and one NFT on marketplace Y. A bad actor could assemble a transaction similar to the previous one. This time, the inputs will be A’s NFT from marketplace X, A’s NFT from marketplace Y, and enough ADA to pay for the more expensive of those two. The outputs will be both NFTs going to the bad actor and ADA going to the seller. Again, both NFT inputs are satisfied because an output containing enough ADA exists. The fix does not help here because the NFT inputs are from different smart contracts.
The working fix to this problem is to limit the number of smart contract inputs to 1, regardless of the smart contract that the input comes from. This mitigates the risk to a lower level, as the contract only allows the most restrictive input configurations. This is also a reason why buying multiple NFTs in one transaction is not supported by most marketplaces right now. There could also be some limitations of outputs added for further security.
The exploit returns once again
Some time passed, and we thought there was no general NFT marketplace with an open-source contract left to check. Then we noticed that epoch.art marketplace published their contract.
Of course, we decided to check it out. We immediately spotted a problem. It was less severe than the original one, but it allowed an attacker to steal NFTs worth thousands to tens of thousands of ADA.
This smart contract did not check for a single script input. It looks like its authors were not even aware of the original exploit. Luckily, they managed to make the possible damage smaller. Their check was whether there is an exact price of the NFT present amongst outputs going to the NFT seller. Therefore, each NFT needed to be bought by paying the exact price. This is different from previous contracts, as they were checking whether the seller receives **at least** the price.
This allows the attacker to buy multiple NFTs and only pay for one if purchased NFTs are from the same seller and are listed for the same price. We found multiple “exploitable groups” of NFTs listed for the same price by the same seller worth thousands of ADA. We contacted epoch.art and let them know about this exploit. After some talking, the contract’s author understood how the exploit works. We advised them to hide the contract from their GitHub until the fix is done and users have time to relist their assets to a new contract. The fix is trivial and it should take just 1 day to prepare it. Epoch.art paid us a bounty of 500 ADA and 2 Mutant NFTs. When we asked epoch.art when they will announce the exploit, they said 2–3 days. This time limit passed and no announcement has been made. Instead, they told us that they decided to add new features into the contract alongside the fix and that it will take them 2 weeks. We found it irresponsible of them, but we did let them do it. We were waiting for them to finish the new contract for some time. After 3 weeks, it still wasn’t ready. When we asked them what was happening they said that they are working on it. We told them that we give them 5 days to push the trivial fix and then we will push the announcement if they don’t do that. So, this is our announcement. As the Cardano community is one of the most transparent ones out there, we believe that this is the right thing to do. All users that have multiple NFTs listed for the same price on epoch.art are affected and should delist their assets as soon as possible.
We hope this spreads awareness about this common issue, but this is a mistake that eUTxO newcomers will probably keep on making. We need to avoid this as much as possible to repeat this again and again from time to time.
The Cardano smart contract space is still evolving and needs time so these simple exploits will be well known among developers. We found an exploit in most live dApps with public smart contracts. However, the biggest exploit, the Minswap one, has been found by our partners from WingRiders DEX. Our partners from WingRiders DEX helped us review our smart contracts too, to make sure they are safe. As we want to help the community grow and make the life of new developers easier and the lives of users calmer, we are open-sourcing our instant-buy smart contract and thus becoming a truly decentralized marketplace, unlike the ones with closed-source contracts.
We are JAM ON BREAD, a new NFT marketplace and aggregator built on Cardano, offering sales of NFTs for a fixed price and placing offers on all NFTs or even on entire collections. We are trying to be the fastest NFT marketplace on Cardano and have many more features coming. You can support us by following our Twitter account or using our marketplace. Thanks for reading, and have a great day!
Fun fact: If you list an NFT on jpg.store, it is usually available on JAM ON BREAD a few minutes sooner than on jpg.store at the time of publishing this article.