Przepełnienie całkowite

Reading time: 4 minutes

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks

Podstawowe informacje

W sercu przepełnienia całkowitego leży ograniczenie narzucone przez rozmiar typów danych w programowaniu komputerowym oraz interpretację danych.

Na przykład, 8-bitowa liczba całkowita bez znaku może reprezentować wartości od 0 do 255. Jeśli spróbujesz przechować wartość 256 w 8-bitowej liczbie całkowitej bez znaku, zostanie ona zawinięta do 0 z powodu ograniczenia pojemności. Podobnie, dla 16-bitowej liczby całkowitej bez znaku, która może przechowywać wartości od 0 do 65,535, dodanie 1 do 65,535 spowoduje zawinięcie wartości z powrotem do 0.

Ponadto, 8-bitowa liczba całkowita ze znakiem może reprezentować wartości od -128 do 127. Dzieje się tak, ponieważ jeden bit jest używany do reprezentacji znaku (dodatniego lub ujemnego), pozostawiając 7 bitów do reprezentacji wartości bezwzględnej. Najbardziej ujemna liczba jest reprezentowana jako -128 (binarne 10000000), a najbardziej dodatnia liczba to 127 (binarne 01111111).

Maksymalne wartości

Dla potencjalnych luk w zabezpieczeniach w sieci bardzo interesujące jest poznanie maksymalnych wspieranych wartości:

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);
}

Przykłady

Czyste przepełnienie

Wydrukowany wynik będzie równy 0, ponieważ przepełniliśmy char:

c
#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

Rozważ sytuację, w której podpisana liczba całkowita jest odczytywana z wejścia użytkownika, a następnie używana w kontekście, który traktuje ją jako liczbę całkowitą bez znaku, bez odpowiedniej walidacji:

c
#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;
}

W tym przykładzie, jeśli użytkownik wprowadzi liczbę ujemną, zostanie ona zinterpretowana jako duża liczba całkowita bez znaku z powodu sposobu interpretacji wartości binarnych, co może prowadzić do nieoczekiwanego zachowania.

Inne przykłady

(((argv[1] * 0x1064deadbeef4601) & 0xffffffffffffffff) == 0xD1038D2E07B42569)

ARM64

To nie zmienia się w ARM64, jak można zobaczyć w tym wpisie na blogu.

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks