Introduction
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.
Basics
This section provides the reader with a basic introduction to blockchain technology and smart contracts.
Blockchain
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.
Smart Contract
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.
Solidity
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.
Gas
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.
Smart Contract Vulnerabilities
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
- Front-running
- Logic error
- Block gas limit vulnerability
- Oracle manipulation
- Timestamp dependence
- Arithmetic underflow/overflow
Reentrancy attack
Overview
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.
Real-life example
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.
Mitigations
There are a variety of techniques to protect smart contracts against reentrancy attacks. The most common ones are listed below.
- Mutexes: Reentrancy attacks rely on repeatedly invoking a smart contract function. A mutex or guard can be used to prevent the attacker from invoking the function multiple times before it has finished. This countermeasure renders some types of reentrancy attacks, such as Single Function Reentrancy and Cross Function Reentrancy, ineffective, provided that all functions that interact with external contracts are protected with a mutex.
- Checks-Effects-Interactions pattern: This is best explained using an example: Suppose a smart contract implements an ATM. Before a withdrawal, the smart contract first verifies that the user’s balance exceeds the requested amount. Only then the requested amount is transferred. This pattern suggests to first perform checks (e.g. validity of arguments, access control, etc.) before updating any state. If all the checks passed, then effects to the state variables of the contract should be made. Only then should the contract interact with other smart contracts.
- Restrict interaction: Scrutinize external contract calls. Contracts interacting with external untrusted contracts enable an attacker to exploit external contract calls – analyzing and limiting which external contract it can interact with can help mitigate reentrancy attacks.
- Industry standard frameworks: Use industry standard frameworks to develop secure blockchain applications, such as OpenZeppelin.
As a best practice, it is recommended to use industry standard frameworks and combine multiple mitigation techniques for the strongest level of protection.
Front-running (transaction order dependence attacks)
Overview
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.
Real-life example
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.
Mitigations
- Orderless/Batching: Observe that frontrunning attacks rely on the (re)ordering/timing of the transactions within a block/over multiple blocks. Designing smart contracts that can be executed independent of order within the block/timing would mitigate this problem. Executing them in batches would also make the contract less vulnerable to such attacks.
- Reduced visibility: Front-running attacks also rely on the visibility of the transaction while they are in the buffer. As such, using a “commit and reveal” scheme, in which the data of the transaction is hidden (e.g. with a cryptographic hash function, etc.) and revealed and verified in subsequent transactions when needed, is another method to reduce the visibility and render the contract less vulnerable against tailored front-running attacks. Using a private buffer also reduces visibility.
Logic error
Overview
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.
Real-life examples
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.
Mitigations
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:
- Documentation: Document the assumptions made about input/output data; comment the code to describe its intended behavior and compare it with the actual implementation.
- Tests: Rigorous and extensive testing, for instance unit tests or integration tests, covering every possible edge case can also contribute to mitigation.
- Audit: It is industry standard to consult a professional independent third party to audit the smart contract before deployment.
Block gas limit vulnerability
Overview
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.
Real-life example
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.
Mitigations
The central issue in this type of vulnerability is code that requires more gas to run than permitted. It is recommended to:
- Rewrite the smart contract code to be more gas-efficient. If necessary, split the computation into multiple transactions.
- Avoid looping over dynamically sized objects.
- Test and benchmark the smart contract code in advance for gas usage.
Oracle manipulation
Overview
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.
Real-life example
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.
Mitigations
The reliance on a single oracle for off-chain data introduces a single point of failure.
- Multiple trusted oracles/data sources with verifiable data should be used.
- In case of a manipulation of the exchange rate, TWAPs (Time-Weighted Average Prices) and VWAPs (Volume-Weighted Average Prices) can be employed to dampen the effect of rapid changes.
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.
Timestamp dependence
Overview
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).
Real-life example
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.
Mitigations
- After the proof of stake merge, this vulnerability no longer exists on the Ethereum main fork. For smart contract deployment it is advised to select the main Ethereum blockchain.
- If this is not possible, it is recommended to rely on multiple trusted oracles for timestamping, instead of using
block.timestamp
orblock.number
(see also Timestamp Dependence – Ethereum Smart Contract Best Practices).
Arithmetic under/overflow attack
Overview
Arithmetic operations can cause integer under/overflow. This can potentially subvert the contract’s logic; a vulnerability an attacker can exploit.
Real-life example
An example is the BatchOverflow bug.
Mitigations
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.
- It is recommended to use at least Solidity 0.8.
- For Solidity 0.7 or lower, use checked arithmetic whenever possible. Alternatively, The use of the SafeMath.col library can resolve typical under/overflow mistakes.
Conclusion
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.
Acknowledgments
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.
Leave a Reply