Solidity Integer Overflow & Underflow

5 min read

Understanding the smart contract vulnerability caused due to integer overflow and underflow.

The Ethereum Virtual Machine specifies integers with fixed-size data types. This means that an integer variable can only represent numbers in a certain range. Solidity variables can be exploited if user input is left unchecked and calculations are conducted that result in numbers that are outside of the range of the data type that contains them. In versions of Solidity before 0.8.x, an integer variable would automatically change to a lower or higher number when it reaches the floor or ceiling of its range.

When we use a value that is larger than what a uint value can accommodate, it wraps back to a number it can accept. This wrapping is usually toward the absolute lowest or highest value that the uint can store. For instance, consider a two-digit unsigned decimal number. When the number 99 is reached and then increased by one, the result becomes 00. In contrast, if we take the lowest two-digit unsigned decimal number, 00, and try to subtract it by 1, we would end up with the highest value of this two-digit range, which is 99.

The "uint8" declaration represents an unsigned integer having a lower range value of 0 and an upper range value of 255 (2^8 - 1). 

Vulnerabilities#

Integer Overflow#

An integer overflow is a type of vulnerability that has been around for a long time. Most computer languages use integers that can only go up to a certain value. For instance, it is possible for the sum of two extremely large integers to result in a very small number because of the underlying data type’s capacity or storage limitations.

In simple terms, overflow occurs when an unsigned integer (uint) reaches its maximum value. After that, any value added to it will then rotate the value from the beginning of its range.

Let's say we have an uint256 variable, which can only hold values in the range [0, 2^256 - 1].

The result would be the lowest value in this uint256 range, which is 0, if you increase the highest number in this range by 1. This is an overflow.

  0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ 0x000000000000000000000000000000000001
-----------------------------------------
= 0x000000000000000000000000000000000000

Integer Underflow#

Since unsigned integers cannot support signed (negative) numbers, subtracting 1 from its lowest value 0 would result in the maximum value in its range, rather than -1. This is an underflow for uint256.

   0x000000000000000000000000000000000000
- 0x000000000000000000000000000000000001
----------------------------------------------------
= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Attack Explained#

Consider the below contract IntegerOverflow.sol,

Line 6 - 10:

In line 6 of the contract, we declare a uint256 variable named number. In line 8 of the contract, we created a function named setNumber, with a parameter called `num`.

When this setNumber function is called by passing a uint256 value, the `num` variable will be assigned the value of this parameter that has been passed.

Line 12-14:

Let us assume that the parameter passed to the setNumber function was the lowest value of the uint256 data type, which is 0.

After this, a call invoked to the decrease function of the contract, which intuitively takes the number variable and subtracts it by 1, would return to the highest value in the uint256 range, which is 2**256-1. This creates an underflow.

Line 16-18:

Similarly, let us assume that the parameter passed to the setNumber function is the highest value of the uint256 variable, which is 2**256-1.

After this, a call invoked to the increase function of the contract, which intuitively takes the earlier set number variable and adds it by 1, would return to the lowest value in the uint256 range, which is 0. This creates an overflow. 

Exploit Impact#

PoWH Coin Ponzi Scheme

In 2018, the 4chan group launched Proof of Weak Hands Coin, a cryptocurrency Ponzi scheme disguised as a pyramid scheme. Its value soared to more than $1,000,000; however, soon after, 866 ETH disappeared from the contract due to a vulnerability in their code. An ERC-20 implementation flaw in PoWH's code allowed users to authorize other users to transfer tokens on their behalf.

Thus, it was possible for a malicious actor to set up a second account to sell tokens from the first account. The sold tokens from the first account would, however, be taken off the second account's balance. The resulting unsigned integer underflow would leave the second account with an extremely large balance of PoWH Coins, which were ultimately sold for over 866 ETH, and were worth approximately $ 952,600 during the time of this incident.

Prevention#

A conventional approach to guard against underflow or overflow errors is to use or build mathematical libraries that replace the standard math operators: addition, subtraction, and multiplication.

The SafeMath library by Open Zeppelin provides the fundamental arithmetic operations as well as the ability to throw errors based on the preconditions and postconditions to determine whether an overflow or underflow has occurred.

Solidity compilers above version 0.8.0 can automatically check for overflows and underflows and produce an error if the values go out of bounds. Moreover, it is also important to properly design the smart contract in order to prevent potential Denial of Service attacks that are based on integer overflow and underflow issues.

Conclusion#

Thus, when the value of an integer type exceeds its maximum value, an integer overflow occurs, and the value wraps around to the minimum value of that type.

Conversely, when the value of an integer type goes below its minimum value, an integer underflow occurs, and the value wraps around to the maximum value of that type.

While both integer overflow and underflow can lead to unexpected and potentially harmful behavior in a smart contract, underflow is generally considered more dangerous than overflow. This is because an underflow can cause the program logic to fail in such a way that the attackers can be hugely profited by the exploit.

For example, suppose a smart contract uses an uint256 variable to track the balances of accounts. If an attacker is able to trick the smart contract to incorrectly subtract a value that is larger than their balance, an integer underflow will occur, which will lead to the computed value  going close to uint256’s maximum value. This enables an attacker to withdraw more funds than they should be able to, sometimes even being able to completely drain entire assets from the smart contract.

By

Tags