Cardano Vulnerabilities #1 — Double Satisfaction

Vacuumlabs Auditing
6 min readJul 18, 2023

--

Welcome to our first blog! At Vacuumlabs, we are committed to securing the Cardano ecosystem. This is the first of a series of blogs, where we show the most common Cardano smart contract vulnerabilities.

We start the series with the most commonly found issue during our audits — the Double Satisfaction. This vulnerability is very simple in theory, but comes in different shades, is tricky to prevent, and often has a critical impact on security. In the first few blogs of this series, we will explore why double satisfaction is so common, how it can be exploited, and what to do to prevent such attacks. We will do this without any code so that it is as easy to understand as possible.

The focus of this first blog in our series is to lay down some foundation for the readers of how Cardano smart contracts behave and what are some potential pitfalls. In the next blogs, we will go much deeper into the vulnerability itself and potential mitigations.

Smart Contracts on Cardano

The Cardano smart contracts are very different from the ones on Ethereum. To represent its blockchain, Cardano uses the Unspent Transaction Output (UTxO) model. A simplification is that the blockchain is a list of unspent transactions, and the only thing we can do is spend them (though since the Vasil fork, we can also reference inputs without spending them).

A transaction can be seen as a change of some already existing UTxOs into some new UTxOs. Each UTxO has some rules attached to it that detail what transaction can spend it. Cardano blockchain only validates a transaction if it follows rules for every single of its inputs. There are some additional rules imposed by the Cardano blockchain — for example, the sum of Ada on inputs and outputs must match (except for the fees which we will omit for simplicity).

This model is pretty simple to understand if we disregard smart contracts. For usual UTxOs, there are explicit rules on how to spend them. The most common rule is the public-key hash UTxO — the transaction that spends it needs to be signed by a corresponding private key. This can be seen in the next image:

Alice pays Bob 20 ADA so she uses 2 of her UTxOs and signs the transaction.

Cardano brings an extension to this UTxO model in the form of smart contracts. While UTxOs on a public-key hash address can be spent by signing the transaction with the owner’s private key, UTxOs at smart contract addresses can be spent only by transactions that follow rules written in the smart contracts. These rules are called validators and can be written in programming languages such as Plutus, Plutarch, or Aiken. The validator is a pure function — its result depends only on the inputs to the function. The inputs to validators are:

  1. Validated transaction — the validator can check anything about the transaction being validated including inputs, outputs, or minted tokens.
  2. Datum of the validated UTxO — this is some data attached to the UTxO that validators can use. The datum can represent the address of the owner, the price of the sold NFT, or more complex structures.
  3. UTxO redeemer — this is just any data that the spender can attach to the transaction. For example, if the smart contract can perform multiple actions, it’s common to insert the name of the action we want to perform inside the redeemer.

Nothing else comes into the validation process. If we want multiple smart contracts to interact, they all need to be included as inputs for the transaction in which they interact.

Let us introduce a simple smart contract that will be used in all of the following examples — smart contract BuyNFT. The purpose of the contract is to sell an NFT to any buyer who is willing to pay the price to the seller. From this purpose, we see that the datum of the smart contract can be viewed as a pair of:

  • The seller of the NFT — an address, the contract needs to know who the buyer should pay.
  • The price to pay — an integer, the contract needs to know how much the buyer should pay.

Having the datum defined as pair of price and seller, we can write a simple validation rule for our smart contract:

  • At least price ADA went to the address seller.

More formally, the validator of BuyNFT checks that the transaction spending it contains an output UTxO to the address seller with the value of at least price ADA. We will say that a transaction that follows this rule satisfies the contract.

The BuyNFT UtxO can only be spent if 100ADA goes to Alice.

A small detail to notice is that the contract doesn’t force anything on the NFT or what should be done with it. It doesn’t need to, as the only thing that Alice is interested in is getting the money when Eve spends the BuyNFT UTxO. What Eve does with the NFT doesn’t need to be specified in the contract. On the other hand, if Alice creates the BuyNFT contract and doesn’t put any NFT into it, Eve can see that the contract is empty and does not spend the BuyNFT UTxO.

The Double Satisfaction

The classic double satisfaction vulnerability stems from using multiple contracts in the same transaction when they do not expect it. Each contract’s validator validates the transaction independently and they all must be satisfied for the transaction to be validated by the blockchain.

Imagine that Alice wants to sell 2 different NFTs, so she creates 2 different BuyNFT contracts. If someone wants to buy both NFTs, Alice would expect them to use the contract this way:

Alice would expect Eve to use multiple BuyNFT UTxOs only if Eve pays for each of them.

However, because each validator validates the transaction independently of the second validator, if Eve pays just 120 ADA, both validators still pass:

A diagram displaying double satisfaction.
Both validators see the same output UTxO with 120 ADA going to Alice so they are satisfied.

So by paying just 120 ADA, Eve could buy both NFTs. We call this vulnerability double satisfaction because Eve satisfies two conditions where she should have satisfied only one.

See you next time

Even if the classic double satisfaction attack looks simple, the whole topic is broad and complex. In this blog, we have shown the necessary foundations of Cardano smart contracts and one simple attack vector. In the next blog, we will present more complex attacks and of course some potential mitigation and prevention tools.

Bonus: Real-world cases

Double satisfaction was present in many of our audits. Multiple examples of this vulnerability can be found in our older AADA report. As AADA is an open-source project, you can also check their GitHub repository and try to find it yourself.

Do not miss any of our blogs by subscribing to our Medium. If you have any requests, please feel free to contact us at audit@vacuumlabs.com.

--

--

Vacuumlabs Auditing

Expert team of smart contract auditors ensuring safety and efficiency in the blockchain world. Join for insightful crypto knowledge.