A blockchain is a distributed append-only database (aka ledger) that removes the concept of a centralized trusted authority. All information stored on a blockchain is publicly accessible. Blockchains have been successfully used to implement cryptocurrencies, such as Bitcoin and Ethereum. Some implementations of blockchain technology, such as Ethereum, offer a means to develop software called smart contracts that are used to automate transactions and contracts in general, without the need for a mutually trusted intermediary.
This blog post focuses on the Ethereum blockchain, as it is the most popular chain for smart contracts. Recent events have demonstrated that security considerations are crucial when designing and implementing smart contracts.
To identify and understand threats and weaknesses of smart contracts, it is important to be at least familiar with common smart contract bugs and vulnerabilities, how they can be leveraged by a malicious attacker, and how these issues can be mitigated.
This blog article aims to raise awareness about common smart contract vulnerabilities and their corresponding mitigation strategies.
This section provides the reader with a basic introduction to blockchain technology and smart contracts.
A blockchain is a decentralized and distributed append-only ledger that securely records and verifies transactions across multiple participants. It is designed to achieve consensus among the participants to ensure transparency, immutability, and trust without the need for a central authority. A blockchain is essentially a chain of blocks, where each block contains a list of transactions (see image below). Each block also contains a unique identifier called a hash value, which is generated based on the content of the block and on the hash value of the previous block. Once a block is formed it is then added to the blockchain sequentially and permanently in a way that achieves consensus on the blockchain across all nodes.
One of the key features of a blockchain is its decentralized nature. Instead of relying on a central authority, such as a bank or a government, blockchains rely on a network of nodes to validate and verify transactions. This distributed network ensures that no single entity has control over the entire blockchain, making it resistant to tampering and fraud.
Another important aspect of a blockchain is its immutability. Once a block has been added to the blockchain, it becomes extremely difficult to remove or modify it, as this requires altering all subsequent blocks and the consensus of the (majority of the) nodes, which is not only detectable but also an infeasible task to accomplish.
A sequence of blocks, chained together by their hash value, forming a blockchain. Image from GeeksForGeeks.
Blockchain technology has gained significant attention due to its potential applications beyond cryptocurrencies. It can be used in various industries, such as finance, supply chain management, healthcare, and more, as it has the potential to streamline processes, reduce costs, and enhance trust.
In a nutshell, a smart contract is a program (i.e., code) that is stored and run on the blockchain. They are self-executing contracts with the terms of the agreement directly embedded in the code; all parties involved must adhere to these terms. They execute predefined agreed-upon actions when certain conditions specified in the contract are met. As smart contracts run on the blockchain, they eliminate the need for intermediaries, such as lawyers or brokers, as the code itself enforces the terms of the agreement. Once a smart contract is deployed on the blockchain, it becomes immutable and tamper-proof, ensuring the integrity of the agreement.
As an illustrative example, in a real estate smart contract, the contract could automatically transfer ownership of a property to the buyer once the seller receives the full payment. This eliminates the need for manual verification and reduces the risk of fraud or disputes.
Another example of a smart contract is a decentralized crowdfunding platform. Suppose there is a project seeking funding, and they create a smart contract on a blockchain platform. The contract specifies the funding goal, the duration of the campaign, and the terms for contributors. When someone wants to contribute to the project, they send their funds to the smart contract wallet. The smart contract automatically verifies the contribution and updates the total amount raised. If the funding goal is reached within the specified time-frame, the smart contract triggers the release of the funds to the project. If the goal is not met, the smart contract automatically refunds the contributed funds back to the contributors. Note that there is no need for an intermediate entity.
Various programming languages can be used to design and develop smart contracts. One of them is Solidity, a popular choice for writing smart contracts for the Ethereum blockchains and variants thereof.
In the context of smart contracts, gas is a measurement of the computational effort required to execute operations or transactions on the Ethereum blockchain. Gas is used to allocate resources and determine the cost of executing smart contracts.
It acts as an economic incentive for participants to use resources efficiently and discourages malicious or computationally expensive activities that could potentially disrupt the network.
Gas prices are typically denominated in a cryptocurrency, such as Ether (ETH), and can fluctuate based on supply and demand dynamics. In order to deploy a smart contract and run transactions the user supplies gas. There is a global gas limit that prevents users to supply arbitrarily high amounts of gas. If during execution the provided gas is depleted, the execution will fail and any changes made up to that point will be reverted. This mechanism ensures that the network remains stable and prevents infinite loops or resource-intensive operations from consuming an excessive amount of resources.
As smart contracts are essentially executable code, vulnerabilities might arise if they are not cautiously designed and written. Therefore, careful consideration and auditing of the smart contract code is crucial to ensure its accuracy and security.
The subsequent sections cover the following vulnerabilities:
Reentrancy attacks typically occur when functions within a (vulnerable) smart contract can be entered multiple times before the state of the smart contract is updated. Such an vulnerability can allow an attacker to steal a smart contract’s financial funds. There are multiple reentrancy types: Single-Function Reentrancy, Cross-Function Reentrancy, Cross-Contract Reentrancy, and more.
On June 17th, 2016, the DAO hack, a reentrancy attack, resulted in the theft of 3.6 million ETH (valued at the time at around $50 million). In the years that followed, more reentrancy attacks were successfully performed on smart contracts, resulting in considerable financial damages.
There are a variety of techniques to protect smart contracts against reentrancy attacks. The most common ones are listed below.
As a best practice, it is recommended to use industry standard frameworks and combine multiple mitigation techniques for the strongest level of protection.
In public blockchains, nodes buffer transactions before they are potentially included in a new block that is to be added to the blockchain. The transactions are usually ordered according to the gas price provided, with the highest first, and lowest last. Since this buffer is publicly visible, attackers can see the buffered transactions and make tailored trans-actions with higher or lower gas price, thus influencing the transaction ordering. This allows the attacker to influence the order of the transactions within the same block where the targeted transactions are located. This can possibly change the outcome of the targeted transaction, which in turn can cause financial damage.
For instance, in a decentralized exchange (DEX) trade, suppose a legitimate user wants to buy some cryptocurrency for a certain price. An attacker could also purchase the same cryptocurrency. By increasing the gas price of their transaction, it is more likely that the attacker’s transactions, within the same block, is processed before the legitimate user’s transaction. This increases the price for the cryptocurrency for the legitimate user; the user can purchase less cryptocurrency for the same amount. This is an example of front-running. The attacker can subsequently sell the acquired cryptocurrency (for a higher price), thus making a profit. In the DEX scenario this is called a sandwich attack and is usually caused by users setting relaxed limits on their trade parameters. The most famous sandwich bot is jaredfromsubway. The front-running attack reduced the purchasing power of the legitimate user and the attacker, by selling, profited from it.
The DODO smart contract fell victim to two attackers: An attacker that took advantage of a flaw in the contract and cryptocurrency bots. These bots set a high transaction fee for their transactions, which allowed them to frontrun the first attacker’s transactions. The bots were able to steal around $3.1 million in cryptocurrency.
Logic errors occur when the implemented logic does not match the developer’s intended logic. This can cause unexpected behavior and potentially lead to an undesired catastrophic outcome.
A prominent real-life incident caused by a logic error was a typo in the Hegic trading protocol that prevented clients from accessing their assets as soon as their contract expired, resulting in about $48000 of financial damages.
The parity multi-sig wallet smart contract contained a logic error, that allowed the attacker to obtain exclusive ownership of the wallet, resulting in the loss of around $30 million.
Overly complex logic with involved cryptography makes it harder to argue and guarantee correctness of the smart contract code. Logic errors are difficult to prevent using technical means. The following non-comprehensive list helps to identify logic errors:
To run a smart contract the caller pays gas. Code that requires more gas to run than the maximal amount of gas permitted will at some point fail and, as a result, the contract state is reverted. Consequently, a smart contract that is vulnerable to a block gas limit vulnerability cannot be executed to completion, potentially rendering it unusable.
GovernMental was a project that suffered from the block gas limit vulnerability. Participants could join the project by sending Ether to the smart contract. The list of participants was kept in a data structure that dynamically grows with the number of participants. At a certain point, it required more gas to delete the list of participants than the maximum of gas permitted for a transaction, preventing it from being cleared.
The central issue in this type of vulnerability is code that requires more gas to run than permitted. It is recommended to:
Oracles, which are sometimes smart contracts themselves, are used to access off-chain information (e.g., current time, stock price information, weather information, etc.). The vulnerability arises when the targeted contract relies on the oracle-supplied data to (automatically) perform operations, even though the data in question might be outdated, wrong at the time of execution, or maliciously influenced beforehand. A manipulated oracle can result in undesired outcomes, e.g., manipulation of exchange rates.
On October 26, 2020, the Harvest Finance Attack manipulated the exchange rate oracles for cryptocurrencies by shot selling one currency against the other temporarily. This eventually resulted in a loss of roughly $33.8 million. This incident was not a singular event, as the Synthetix MKR Price Manipulation, and some others, demonstrate.
The reliance on a single oracle for off-chain data introduces a single point of failure.
However, note that mitigations and solutions should be tailored to the smart contract in question since the variety of use cases makes it difficult to provide generic mitigations for oracle manipulation.
The timestamp of a block is generated by the node that adds it to the chain. As the name suggests, a smart contract that relies on block timestamps to perform operations is susceptible to block timestamp manipulation by a malicious node, which can result in theft of resources and reputational damage (see also here).
EtherLotto was a smart contract acting as an on-chain lottery game. It suffered from a timestamp dependence vulnerability that allowed an attacker to win the game by simply manipulating the timestamp.
block.timestamp
or block.number
(see also Timestamp Dependence – Ethereum Smart Contract Best Practices).Arithmetic operations can cause integer under/overflow. This can potentially subvert the contract’s logic; a vulnerability an attacker can exploit.
An example is the BatchOverflow bug.
Since Solidity 0.8, arithmetic operations are checked at runtime. Integer under/overflow will cause all arithmetic operations to revert, thus rendering this attack impossible.
Designing reliable and secure smart contracts is a non-trivial task. Developers need to be aware of various smart contract vulnerabilities and their mitigations. Technical means to analyze smart contracts can be used to perform code reviews and auditing, such as various static/dynamic code analysis, fuzzing, and formal verification. A non-comprehensive list of tools can be found at rareskills.io.
A more comprehensive list of smart contract vulnerabilities can be found at soliditylang.org, OWASP, swcregistry.io and scfg.io.
I would like to thank my colleagues Urs and Lukasz, and my friend Sven Gnap, who aided me with writing and reviewing many iterations of this blog article.