hacktricks/binary-exploitation/integer-overflow.md
2024-12-12 11:39:29 +01:00

6.4 KiB

Integer Overflow

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

Basic Information

At the heart of an integer overflow is the limitation imposed by the size of data types in computer programming and the interpretation of the data.

For example, an 8-bit unsigned integer can represent values from 0 to 255. If you attempt to store the value 256 in an 8-bit unsigned integer, it wraps around to 0 due to the limitation of its storage capacity. Similarly, for a 16-bit unsigned integer, which can hold values from 0 to 65,535, adding 1 to 65,535 will wrap the value back to 0.

Moreover, an 8-bit signed integer can represent values from -128 to 127. This is because one bit is used to represent the sign (positive or negative), leaving 7 bits to represent the magnitude. The most negative number is represented as -128 (binary 10000000), and the most positive number is 127 (binary 01111111).

Max values

For potential web vulnerabilities it's very interesting to know the maximum supported values:

{% tabs %} {% tab title="Rust" %}

fn main() {

    let mut quantity = 2147483647;
    
    let (mul_result, _) = i32::overflowing_mul(32767, quantity);
    let (add_result, _) = i32::overflowing_add(1, quantity);
    
    println!("{}", mul_result);
    println!("{}", add_result);
}

{% endtab %}

{% tab title="C" %}

#include <stdio.h>
#include <limits.h>

int main() {
    int a = INT_MAX;
    int b = 0;
    int c = 0;
    
    b = a * 100;
    c = a + 1;
    
    printf("%d\n", INT_MAX);
    printf("%d\n", b);
    printf("%d\n", c);
    return 0;
}

{% endtab %} {% endtabs %}

Examples

Pure overflow

The printed result will be 0 as we overflowed the char:

#include <stdio.h>

int main() {
    unsigned char max = 255; // 8-bit unsigned integer
    unsigned char result = max + 1;
    printf("Result: %d\n", result); // Expected to overflow
    return 0;
}

Signed to Unsigned Conversion

Consider a situation where a signed integer is read from user input and then used in a context that treats it as an unsigned integer, without proper validation:

#include <stdio.h>

int main() {
    int userInput; // Signed integer
    printf("Enter a number: ");
    scanf("%d", &userInput);

    // Treating the signed input as unsigned without validation
    unsigned int processedInput = (unsigned int)userInput;

    // A condition that might not work as intended if userInput is negative
    if (processedInput > 1000) {
        printf("Processed Input is large: %u\n", processedInput);
    } else {
        printf("Processed Input is within range: %u\n", processedInput);
    }

    return 0;
}

In this example, if a user inputs a negative number, it will be interpreted as a large unsigned integer due to the way binary values are interpreted, potentially leading to unexpected behavior.

Other Examples

ARM64

This doesn't change in ARM64 as you can see in this blog post.

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}