Although a smart contract technology has been increasingly utilized and recognized as the safest option of exchanging digital assets between several parties, the number of general errors are still occurring from time to time. When auditing smart contracts of our clients we face the number of identical bugs over and over again. In order to prevent the same errors from happening, we decided to address the most common smart contract vulnerabilities and maybe protect future developers from making headlines throughout the world. So, without further ado, let’s take a look at those bugs which can potentially cost companies millions of dollars.
Arithmetic Over / Under Flows
The fixed-size data types for integers are specified by the Ethereum Virtual Machine (EVM). What it means is that it can represent only a certain range of numbers. Without taking the proper measures the variables can be utilized in case if user input is unchecked which is the reason why numbers can be outside the range of data type they are stored at.
It usually happens during an operation requiring a fixed size variable to store a piece of data or a number surpassing the variable’s data type range. Such smart contract vulnerabilities are utilized by cybercriminals in order to misuse the code and benefit from the process.
Example: Adding numbers that exceed the data type range is called Overflow. As soon as the uint (unsigned integer) reaches its maximum size, the next element added will overflow. For example, for uint8, the maximum number is 255, and if you add 1 more to it, then the variable will be overflowed and will equal 0 (if added 2, then the variable would be 1).
Solidity’s functions have visibility types which determine how those functions can be called. The visibility also determines whether a function can be called by users or any contracts and whether it can be done only internally or only externally. Solidity implies using four visibility types and ‘public’ is set for functions by the default which allows users to call them externally. Improper use of those specifiers can result in a serious smart contract vulnerability leading to huge consequences.
‘Public’ is set for functions by the default which makes it possible for the external users to call them. There are cases when developers confuse private and public visibility qualifiers by mistake which can lead to exposing the data to malicious users.
Every transaction on the Ethereum blockchain has a global impact on the entire Ethereum ecosystem in a calculable way. Basically, it means that any randomness or entropy is impossible inside the blockchain ecosystem. Therefore, ‘rand()’ function is absent in Solidity and achieving decentralized entropy is a problem many experts address.
Some programmers are trying to write their own “random” functions, but as they are not well familiar with the ecosystem of the ETH – they mess up; as a result, vulnerabilities appear.
Race Conditions / Front Running
The Ethereum blockchain nature implies the combination of external calls to other contracts and a large number of users which makes it possible for cybercriminals to determine Solidity vulnerabilities by racing code execution for their benefit.
Due to the blockchain technology, Ethereum nodes form transactions into blocks which are considered valid as soon as a miner solves a consensus mechanism.
Before a transaction is added to the block, it goes to the mempool where everyone knows what will occur. Such circumstances can be troublesome for decentralized markets as a transaction to buy some tokens is seen, and a market order implemented before the other transaction included. It’s almost impossible to be protected against it, as front running is a specific feature of a contract itself. However, it would be better to implement batch auctions (also to stay protected against high-frequency trading issues) or to use a pre-commit scheme (“I’m going to submit the details later”).
Denial of Service (DOS)
Being a very wide category, DOS attack implies leaving contracts dysfunctional for some time or even permanently. This attack can freeze ether contained in those contracts for an indefinite period or even forever. Moreover, DOS attacks can violate the logic of a smart contract.
Ethereum smart contracts allow calling and employing a code of other contracts which send ETH to different external user addresses. The process of calling external contracts or sending ETH to an address is impossible without submitting an external call by the contract. This is where cybercriminals steal those external calls and force contract to execute and call back to itself (using a fallback function). This way, the execution of the code “re-enters” the contract.
A malicious user creates a contract with malicious code in the fallback function at an external address. This way, the malicious code runs when the contract sends ETH to the address. Therefore, it controls the contract and performs illegal actions the developer is not aware of. “Re-entrancy” means that the external malicious contract calls back on the vulnerable contract and repeats code execution on the vulnerable contract in such a way withdrawing all ether from the contract.
Constructors with Care
Being specific functions, constructors carry out the most important and special tasks during the contract initialization. In the earlier Solidity versions, constructors were named the same as the contract that has them. This way, when the contract name has been changed during the development, and the constructor name hasn’t, it turns out as a regular callable function. Therefore, the vulnerability became the reason for numerous cybercrimes. This type of vulnerability is rarely met nowadays, as the majority resorts to constructor keyword.
Ethereum blockchain has a global variable – tx.origin. It runs through the entire calling process and returns the address of the account that was sending the transaction. Utilizing the variable for the smart contract authentication creates a serious vulnerability for a phishing cyber-attack.
We rarely come across this vulnerability. Developers who have little experience in solidity cannot distinguish it from the variable msg.sender and therefore write contracts with vulnerabilities, using it where it is not advisable. So, never use tx.origin for authorization.
Regardless of how innovative blockchain is, even the best developer in the world can make an unintentional mistake which can cause serious problems. This is exactly why we decided to provide a list of the most common vulnerabilities we have found when auditing our clients’ smart contracts (there is also a separate class of logical mistakes which may have critical nature but we’ll describe them in further articles). Maybe, it will help someone to avoid facing devastating consequences and losing the company’s reputation. As they say, forewarned is forearmed.