Secure coding

# What is is integer overflow and underflow?

November 16, 2020 by Srinivas

This article provides an overview of Integer Overflow and Overflow vulnerabilities and how they can be exploited. Integer Overflows and Underflows occur due to the input, whose size does not meet the boundaries of integer variables. While integer Overflows themselves are not dangerous, they can lead to other vulnerabilities when exploited. In this article, we will first discuss the basics of Integer Overflows and underflows and then we will discuss an example of how integer overflows can lead to other dangerous vulnerabilities such as Stack based Buffer Overflows.

## What is an Integer

Int, short for “integer,” is a variable type used to represent real numbers without fractional part.

An integer on a 32 bit system is 32 bits long and it is 64 bits long on 64 bit systems. For example, the decimal value 2 on a 64 bit system is represented as follows in binary.

 00000000000000000000000000000010

When computers deal with integers, this binary representation is used as computers cannot directly deal with decimal values.

It should be noted that the integer values can be either positive or negative. The integer variables that can be used to store only positive values are known as unsigned integers and the variables that can be used to store both positive and negative values are known as signed integers.

When computers need to store negative numbers, the most significant bit is used to determine the sign. If the most significant bit is set to 0, the variable is interpreted as positive and it is represented as negative if the most significant bit is set to 1. This is not applicable to variables such as unsigned integers.

Following are the data types and their ranges of some commonly used integer types on 32 bit CPUs.

Notice the minimum and maximum sizes for various variable types.

## What is integer overflow?

As we noticed in the table above, the maximum size for a variable of type Signed char is 127 and it is 255 for unsigned char. Storing a value greater than maximum supported value will lead to integer overflow. Integer overflows by themselves do not lead to code execution. However, it could lead to other vulnerabilities such as Buffer Overflows. For example, if we try to store the value 256 in a char variable, it becomes 100000000 in binary and on a 32 bit system the maximum it will be shown as 00000000 as it can hold a maximum of 32 bits only.

The following example demonstrates this issue.

 #include int main(int argc, char* argv[]) {         unsigned char a,b;            a = 255;   printf(“%d\n”, a);          b = a+1;        //Overflow occurs here   printf(“%d\n”,b);  }

As we can notice, there are two variables a and b of type unsigned char. This means, each of these variables can hold a maximum size of 255. However, the value 1 is added to 255 and stored in variable b resulting in an integer overflow. Let us observe what happens when we run this program.

 \$ ./test  255 0 \$

As we can notice, instead of 256, the variable b contains 0 due to the integer overflow.

## What is integer underflow?

Similarly, integer underflow occurs when storing a value lesser than the minimum supported value. For example, when we try to store -129 to signed char data type, it gets stored as 127 due to the underflow.

The following example demonstrates this issue.

 #include int main(int argc, char* argv[]) {         char a,b;          a = -128; printf(“%d\n”, a);         b = a-1; //Underflow occurs here printf(“%d\n”,b);  }

As we can notice, there are two variables a and b of type signed char. This means, each of these variables can support -128 as their minimum values. However, the value 1 is subtracted from -128 and stored in variable b resulting in an integer underflow. Let us observe what happens when we run this program.

 \$ ./test  -128 127 \$

As we can notice, instead of 256, the variable b contains 0 due to the integer overflow.

## How can integer overflows or underflows be exploited?

As mentioned earlier, integer overflow vulnerabilities won’t directly lead to arbitrary code execution vulnerabilities, but they can lead to successful exploitation of other vulnerabilities such as Buffer Overflows.

Let us consider the following example.

 #include #include #include void validate_data(char* user_data); int main(int argc, char* argv[]) {  if(argc!=2) {   printf(“The program requires an argument:   \n”);   fflush(stdout);   exit(-1);  }  validate_data(argv[1]);  return 0; } void validate_data(char* user_data) {  char buffer[112];  unsigned char length = strlen(user_data);  if(length >= 5 && length <= 9)  {   printf(“Strong Enough\n”);    printf(“%d\n”, length);   printf(“%u\n”, strlen(user_data));   fflush(stdout);   strcpy(buffer,user_data);   }   else{   printf(“Data length must be between 5 and 9\n”);    fflush(stdout);  } }

This is a simple C program which is vulnerable to buffer overflow. If you closely observe this C program, we have a function named validate_data which is taking a command line argument. This argument is being copied into another variable called buffer, which is a character array of length 112. However we are performing this copy using strcpy function. This strcpy function doesn’t perform any bounds checking implicitly and thus we will be able to write more than 256 characters into the variable buffer and that’s when buffer overflow occurs. However, this vulnerability is not exploitable since there is a validation performed on the length of the user supplied data.

 if(length >= 5 && length <= 9)

As we can observe, the Buffer Overflow is not exploitable due to the fact that we cannot pass large  data to the strcpy function. However, this program is also vulnerable to integer overflow, which can be used to bypass the length validation thus leading to successful exploitation of the Buffer Overflow vulnerability.

Let us understand how this can be achieved.

The length of the user supplied data is passed to the variable length of type unsigned char. This variable type can support a maximum value of 255, which is equivalent to FF in hex. If we add 6 to it, an integer overflow occurs resulting in the value 0x05 instead of 0x105 in the variable length. This will help us to bypass the length validation leading to a successful buffer overflow.

Following are various input sizes and responses from the program.

 \$ ./vuln2 \$(perl -e ‘print “A” x 5’) Strong Enough \$ \$ ./vuln2 \$(perl -e ‘print “A” x 4’) Data length must be between 5 and 9 \$ \$ ./vuln2 \$(perl -e ‘print “A” x 10’) Data length must be between 5 and 9 \$ \$ ./vuln2 \$(perl -e ‘print “A” x 255’) Data length must be between 5 and 9 \$ \$ ./vuln2 \$(perl -e ‘print “A” x 261’) Strong Enough Segmentation fault (core dumped) \$

As we can observe in the highlighted entry, when we passed 261 bytes, the length check was bypassed causing a segmentation fault and we should see a new file called core in the current directory. This file is a core dump, which gives us the situation of this program and the time of the crash. So we can use this core file to analyze the crash. Let us see how we can analyze the core file using gdb.

 \$ gdb -core core -q GEF for linux ready, type `gef’ to start, `gef config’ to configure 78 commands loaded for GDB 9.1 using Python engine 3.8 [*] 2 commands could not be loaded, run `gef missing` to know why. [New LWP 313465] [!] ‘./vuln2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA’ not found/readable [!] Failed to get file debug information, most of gef features will not work Core was generated by `./vuln2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA’. Program terminated with signal SIGSEGV, Segmentation fault. #0  0x41414141 in ?? () gef➤

If you look at this gdb output, it shows that the long input has overwritten EIP somewhere. (EIP is  the  register that decides what is next instruction to be executed)

If you notice the next instruction to be executed, it is at the address 0x41414141, which probably is not a valid address and that’s the reason why the application crashed. So as mentioned earlier, we can use this core dump to analyze the crash.  We can also type info registers to understand what values each register is holding and at the time of crash.

 gef➤  info registers eax            0xffffcfcf          0xffffcfcf ecx            0xffffd3d0          0xffffd3d0 edx            0xffffd0ca          0xffffd0ca ebx            0x41414141          0x41414141 esp            0xffffd050          0xffffd050 ebp            0x41414141          0x41414141 esi            0xf7fb4000          0xf7fb4000 edi            0xf7fb4000          0xf7fb4000 eip            0x41414141          0x41414141 eflags         0x10286             [ PF SF IF RF ] cs             0x23                0x23 ss             0x2b                0x2b ds             0x2b                0x2b es             0x2b                0x2b fs             0x0                 0x0 gs             0x63                0x63 gef➤

As I mentioned, EIP is actually overwritten with  0x41414141, which from our input. This confirms that we got control over the EIP register and from here, the Buffer Overflow exploitation is straightforward.

## Conclusion

Integer overflows may look harmless, but they can lead to serious vulnerabilities which include business logic issues in some cases. Imagine an attacker entering a negative amount leading to a positive amount in his/her account due to an integer underflow vulnerability. So, developers must be aware of the risks integer overflow vulnerabilities bring.

## Sources

Posted: November 16, 2020
##### Srinivas
View Profile

Srinivas is an Information Security professional with 4 years of industry experience in Web, Mobile and Infrastructure Penetration Testing. He is currently a security researcher at Infosec Institute Inc. He holds Offensive Security Certified Professional(OSCP) Certification. He blogs atwww.androidpentesting.com. Email: srini0x00@gmail.com