Integer overflow and underflow vulnerabilities
Integer overflow and underflow vulnerabilities are considered the eighth most dangerous vulnerability in the Common Weakness Enumeration (CWE) Top 25 List of 2019. These types of vulnerabilities are created by misuse of variable types and can be exploited to bypass protections against other types of vulnerabilities as buffer overflows.
Variable types and sizes
Every programming language has the concept of different types of variables. A character, which can contain text data, is very different from an integer or a float. However, beyond these general types are additional distinctions between variables, and these distinctions are what make integer overflow and underflow vulnerabilities possible.
Most programming languages have the concept of signed and unsigned integers. A signed integer (as the name suggests) has a sign, allowing it to store both positive and negative values. An unsigned variable, on the other hand, can only store positive numbers.
As shown in the image above, signed and unsigned variables of a given size are written identically in a computer’s memory. The only difference is how the value is interpreted. In a signed value, a leading one means a negative number, while in an unsigned value, a one in the first spot just means a large number.
The other main distinction between different “integer” variable types is the size of the value that they can contain. Programming languages have the concepts of short, integer, long and beyond, each of which defines the size of the memory space allocated for that variable and the range of values that it can contain.
Integer overflows and underflows
Integer overflow and underflow vulnerabilities boil down to unsafe conversion between signed and unsigned variables and integer variable types of different sizes. It is generally permitted to convert between these different types and, in many cases, the results actually make sense. However, in other cases, the result of an unsafe typecast is nonsense.
Integer overflow vulnerabilities are caused when a value is moved into a variable type too small to hold it. One example is downcasting from a long (which has eight bytes allocated to it) to an int (which uses two or four bytes). This is accomplished by cutting the value down to a small enough size that it fits in the smaller value. If any of the bits that are dropped are non-zero, then the value suddenly becomes a lot smaller.
Integer overflows can also occur when typecasting from an unsigned to a signed variable type. Both a signed and an unsigned short are stored in two bytes, but the most significant bit of a signed short indicates whether it is positive or negative. An unsafe cast from unsigned to signed of a value with a one in the most significant bit changes it from a large positive number to a negative one.
Integer overflows occur when a value exceeds the maximum value that a variable can contain, and integer underflows happen when a value becomes too small to fit. When this occurs, the value wraps around from the minimum value that can be stored to the maximum.
For example, consider an unsigned variable with a current value of zero. Subtracting one from this value should result in a value of negative one; however, unsigned variables can’t store negative numbers. The result is an unsigned variable that contains the maximum value that it can hold.
Exploitation of integer overflows and underflows
Integer overflow and underflow vulnerabilities are useful to hackers in a number of different ways. The main reason for this is that these vulnerabilities can invalidate checks made to protect against other classes of vulnerabilities.
For example, a buffer overflow vulnerability is created when a developer fails to check the length of user-controlled input before placing it in a preallocated memory buffer. The solution to this vulnerability is to ensure that the length of the user input is shorter than the size of the available buffer. However, if an attacker can cause this check to fail due to an integer overflow vulnerability, the vulnerability becomes exploitable again.
Another application of integer overflow and underflows is defeating checks to determine if a value meets certain criteria, like ensuring that an account has a certain minimum balance before initiating a withdrawal. If this check is implemented using unsigned variables and checks to see if the difference between the account balance and withdrawal amount is greater than zero, the check will never return false, making it possible to perform unauthorized withdrawals.
Conclusion: Protecting against integer overflow and underflow vulnerabilities
Integer overflow and underflow vulnerabilities are caused by misuse of variable type conversions. Protecting against these vulnerabilities is fairly simple: use a language with dynamic variable typing (like Python) or make sure that variable types are explicitly specified and the correct type for the job.