hacktricks/binary-exploitation/integer-overflow.md

7.1 KiB
Raw Blame History

Переповнення цілочисельних значень

{% hint style="success" %} Вивчайте та практикуйте хакінг AWS: Навчання HackTricks AWS Red Team Expert (ARTE)
Вивчайте та практикуйте хакінг GCP: Навчання HackTricks GCP Red Team Expert (GRTE)

Підтримайте HackTricks
{% endhint %}

Основна інформація

У центрі переповнення цілочисельних значень лежить обмеження, накладене розміром типів даних у програмуванні для комп'ютерів та інтерпретацією даних.

Наприклад, 8-бітне беззнакове ціле число може представляти значення від 0 до 255. Якщо ви спробуєте зберегти значення 256 у 8-бітному беззнаковому цілому числі, воно обертається до 0 через обмеження його місткості. Так само для 16-бітного беззнакового цілого числа, яке може утримувати значення від 0 до 65,535, додавання 1 до 65,535 призведе до обертання значення назад до 0.

Більше того, 8-бітне знакове ціле число може представляти значення від -128 до 127. Це тому, що один біт використовується для представлення знака (позитивний або від'ємний), залишаючи 7 біт для представлення величини. Найвід'ємніше число представлене як -128 (двійкове 10000000), а найпозитивніше число - 127 (двійкове 01111111).

Максимальні значення

Для потенційних вразливостей веб-додатків дуже цікаво знати максимально підтримувані значення:

{% 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="Українська" %}

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

Приклади

Чистий переповнення

Надрукований результат буде 0, оскільки ми переповнили символ:

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

Перетворення зі знакового на беззнакове

Розгляньте ситуацію, де знакове ціле число зчитується з введення користувача, а потім використовується в контексті, який розглядає його як беззнакове ціле число, без належної перевірки:

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

У цьому прикладі, якщо користувач вводить від'ємне число, воно буде інтерпретовано як велике беззнакове ціле через спосіб інтерпретації бінарних значень, що потенційно може призвести до непередбачуваної поведінки.

Інші Приклади

(((argv[1] * 0x1064deadbeef4601) & 0xffffffffffffffff) == 0xD1038D2E07B42569)
  • https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/
  • Лише 1B використовується для зберігання розміру пароля, тому можливе переповнення та змушення його вважати довжиною 4, хоча насправді вона 260, щоб обійти захист перевірки довжини та перезаписати на стек наступну локальну змінну та обійти обидва захисти

ARM64

Це не змінюється в ARM64, як можна побачити в цьому блозі.