Vulnerable smart contracts have technical weaknesses of various types that can be exploited by attackers to steal funds or data.
Blockchains such as Ethereum rely on Turing-complete languages, such as Solidity, that can be used to program virtually any conceivable application. However, when you can program any outcome, it can be impossible to predict all the possible unintended consequences that may occur. For instance, a smart contract may be programmed to dispense tokens under certain conditions. If a malicious user manages to find a way to make the contract dispense tokens under conditions not foreseen by the developers, this is classed as a vulnerability.
Furthermore, the public nature of blockchains introduces certain factors that attackers can exploit. For instance, before a transaction is confirmed and entered into the blockchain ledger, it is broadcast to the network as part of a larger pool of unprocessed transactions known as a mempool. How transactions are selected from the mempool for inclusion in the next block can also be subject to manipulation and gamification since there is often no requirement for a miner or validator to choose transactions based on chronological order.
Therefore, while vulnerable code is often the root cause of an attack, attackers may use various factors to launch a successful attack. The objective of such an attack is typically to steal funds or data, although occasionally, the attacker may be a white-hat hacker demonstrating the vulnerability and will return the funds in exchange for a bug bounty. However, such attacks may still erode trust among users.
As a matter of best practice, developers often carry out code audits to identify vulnerabilities and bugs. Often, a program can still run effectively even with some bugs; therefore, code audits will classify bugs and vulnerabilities according to their severity and risk.
Types of smart contract vulnerabilities
While there are potentially many ways for attackers to exploit smart contracts, there are several known vulnerabilities and attack methods. These are some of the most common ones.
Reentrancy attacks
Smart contracts may communicate with other smart contracts as part of their normal operation. This capability underlies the composability of blockchain applications, allowing two or more Ethereum dApps to interact with one another even within the same Ethereum transaction.
Reentrancy attacks exploit vulnerabilities in smart contracts by requiring the code to make an external call to an untrusted second smart contract, usually to transfer funds. Before the transaction has been completed and entered into the ledger, the reentrancy attack will make the same call. Since the fund update has not yet happened, the reentrancy attack can keep making withdrawals to the point of draining the contract of all its funds.
One of the most famous reentrancy attacks is The DAO incident, which was an investment smart contract that was drained of $60 million worth of ETH in 2016.
Integer overflow/underflow
Programming languages generally don’t support floating point arithmetic, meaning that it’s not possible to represent fractions of numbers using a decimal point. Therefore, developers must use integers (whole numbers) when coding applications. Most cryptocurrencies operate fractional denominations of a cryptocurrency, such as gwei, instead of ether, which facilitates coding token values as integers.
The value of an integer cannot go to infinity; it is restricted by the maximum allowed by the Solidity word size, which is 256 bits. In Ethereum, this translates to 4.3 billion ETH.
When a smart contract is calculating a value, if the value of the unsigned integer ends up as zero, it will return the maximum possible value of 4.3 billion ETH.
Attackers attempt to exploit this limitation by using a malicious address that can be read by the smart contract as having a zero balance and sending one unit (any denomination) of ETH. This forces the smart contract to update to the maximum value permitted, 4.3 billion ETH.
Furthermore, the strict arithmetic logic involved in generating calculations entirely in integers can catch out developers. For example, percentage calculations can be done in one of two ways. If asked to calculate 50% of 90, it could be calculated as:
90*50 = 450
450/100 = 4
This method only uses integers. However, an alternative way to reach the same result could be:
90/100 = 0.9
0.9 x 50 = 45
This method generates an invalid entry (0.9) which will create an integer error that can be exploited.
The latter problem has generally been solved by Solidity compilers which scan for such errors so they can be corrected. However, BeautyChain, an Ethereum project that was subjected to an integer overflow attack in 2018, was too early for such an easy fix.
Gas fee vulnerabilities
In Ethereum, a gas limit applies to each block, which serves to manage the rate at which new data is added to the blockchain. However, this introduces a challenge for data-heavy applications. Apps may store data in arrays, so that it’s accessed by looping through these arrays. Such activity may ultimately mean the transaction breaches the block gas limit and fails to complete. This can also result in the entire contract becoming void since no further transaction can complete the loop.
GovernMental was a Ponzi crypto project that required users to send ETH to a smart contract. The list of participants became so long that the block gas fee limit now prevents anyone from being able to complete a transaction due to the need to scan the list.
Lack of parameters or precondition controls
Smart contracts often have basic preconditions in place, such as that an address is valid or has a sufficient balance to complete a transaction, for example. However, developers can often overlook the fine detail required to code preconditions, particularly if they are complex, resulting in unintended errors.
One example is Skyward Finance, an IDO platform developed on NEAR, which was attacked in 2022. The attacker exploited the fact that the smart contract did not check that the same wallet address should not be used more than once, so the attacker simply made repeated withdrawals to the tune of $3.2 million.
Frontrunning
Frontrunning is another type of attack that leverages the fact that transactions are visible in the mempool. Frontrunning bots monitor the mempool for profitable transactions. When they find one, they simply copy the transaction and set a higher gas fee for it. A miner or validator will be incentivized to choose the transaction with the higher gas fee for inclusion in the block, and ultimately, the original transaction will become redundant.
Frontrunning is not necessarily a vulnerability of smart contract coding so much as a vulnerability in the overall design of the blockchain. As such, frontrunning bots and attacks are relatively commonplace. However, programmers and app developers can implement ways that help to avoid frontrunning attacks, such as transaction counters that may reveal if the smart contract state has been modified or off-chain ordering systems to ensure sequential transactions.
How do developers avoid vulnerable smart contracts?
There is no 100% foolproof way for developers to avoid all vulnerabilities. However, there are some best practices, such as writing code using the latest syntax, regularly updating tools such as compilers, and carrying out extensive and robust stress testing under different scenarios.
Many projects will run incentivized testing phases for this purpose, and users can often earn rewards for engaging with early testing programs, which help developers to spot bugs and glitches.
How to spot and avoid vulnerable smart contracts
Anyone who can understand programming languages like Solidity can view the smart contract code using a block explorer such as Etherscan. Even without understanding the code, examining block explorer data such as transaction history may reveal anomalies that require further investigation. For instance, a lack of sell transactions could indicate that buyers are prevented from selling.
Other ways to avoid vulnerable smart contracts include checking projects for the presence of an independent code audit, always using reputable protocols with large numbers of users, and making sure that applications and wallets are using the latest version of the software available.
Vulnerable smart contracts essentials
- Vulnerabilities in smart contracts refer to the opportunity for attackers to create unintended outcomes, such as diverting funds to the attacker's wallet.
- Smart contract vulnerabilities include reentrancy attacks, integer overflows, and frontrunning.
- Users can mitigate the risks by taking security precautions such as checking for independent code audits and only using reputable protocols.