hacktricks/binary-exploitation/stack-overflow/uninitialized-variables.md

6.8 KiB

Uninitialized Variables

{% 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

Główna idea tutaj polega na zrozumieniu, co się dzieje z niezainicjowanymi zmiennymi, ponieważ będą one miały wartość, która już znajdowała się w przydzielonej pamięci. Przykład:

  • Funkcja 1: initializeVariable: Deklarujemy zmienną x i przypisujemy jej wartość, powiedzmy 0x1234. Ta akcja jest podobna do rezerwacji miejsca w pamięci i umieszczenia w nim konkretnej wartości.
  • Funkcja 2: useUninitializedVariable: Tutaj deklarujemy inną zmienną y, ale nie przypisujemy jej żadnej wartości. W C, niezainicjowane zmienne nie są automatycznie ustawiane na zero. Zamiast tego, zachowują wartość, która była ostatnio przechowywana w ich lokalizacji pamięci.

Kiedy uruchamiamy te dwie funkcje sekwencyjnie:

  1. W initializeVariable, x otrzymuje wartość (0x1234), która zajmuje określony adres pamięci.
  2. W useUninitializedVariable, y jest deklarowane, ale nie przypisano mu wartości, więc zajmuje miejsce w pamięci tuż po x. Z powodu nieinicjowania y, kończy się na "dziedziczeniu" wartości z tej samej lokalizacji pamięci używanej przez x, ponieważ to była ostatnia wartość, która tam była.

To zachowanie ilustruje kluczową koncepcję w programowaniu niskiego poziomu: Zarządzanie pamięcią jest kluczowe, a niezainicjowane zmienne mogą prowadzić do nieprzewidywalnego zachowania lub luk w zabezpieczeniach, ponieważ mogą niezamierzenie przechowywać wrażliwe dane pozostawione w pamięci.

Niezainicjowane zmienne stosu mogą stwarzać kilka zagrożeń bezpieczeństwa, takich jak:

  • Wycieki danych: Wrażliwe informacje, takie jak hasła, klucze szyfrowania lub dane osobowe, mogą być ujawnione, jeśli są przechowywane w niezainicjowanych zmiennych, co pozwala atakującym na potencjalne odczytanie tych danych.
  • Ujawnienie informacji: Zawartość niezainicjowanych zmiennych może ujawniać szczegóły dotyczące układu pamięci programu lub wewnętrznych operacji, co pomaga atakującym w opracowywaniu ukierunkowanych exploitów.
  • Awaria i niestabilność: Operacje związane z niezainicjowanymi zmiennymi mogą prowadzić do nieokreślonego zachowania, co skutkuje awariami programu lub nieprzewidywalnymi wynikami.
  • Wykonanie dowolnego kodu: W niektórych scenariuszach, atakujący mogą wykorzystać te luki, aby zmienić przepływ wykonania programu, co umożliwia im wykonanie dowolnego kodu, co może obejmować zagrożenia związane z zdalnym wykonaniem kodu.

Example

#include <stdio.h>

// Function to initialize and print a variable
void initializeAndPrint() {
int initializedVar = 100; // Initialize the variable
printf("Initialized Variable:\n");
printf("Address: %p, Value: %d\n\n", (void*)&initializedVar, initializedVar);
}

// Function to demonstrate the behavior of an uninitialized variable
void demonstrateUninitializedVar() {
int uninitializedVar; // Declare but do not initialize
printf("Uninitialized Variable:\n");
printf("Address: %p, Value: %d\n\n", (void*)&uninitializedVar, uninitializedVar);
}

int main() {
printf("Demonstrating Initialized vs. Uninitialized Variables in C\n\n");

// First, call the function that initializes its variable
initializeAndPrint();

// Then, call the function that has an uninitialized variable
demonstrateUninitializedVar();

return 0;
}

Jak to działa:

  • Funkcja initializeAndPrint: Ta funkcja deklaruje zmienną całkowitą initializedVar, przypisuje jej wartość 100, a następnie drukuje zarówno adres pamięci, jak i wartość zmiennej. Ten krok jest prosty i pokazuje, jak zachowuje się zainicjowana zmienna.
  • Funkcja demonstrateUninitializedVar: W tej funkcji deklarujemy zmienną całkowitą uninitializedVar bez jej inicjalizacji. Gdy próbujemy wydrukować jej wartość, wynik może pokazać losową liczbę. Ta liczba reprezentuje dane, które wcześniej znajdowały się w tej lokalizacji pamięci. W zależności od środowiska i kompilatora, rzeczywisty wynik może się różnić, a czasami, dla bezpieczeństwa, niektóre kompilatory mogą automatycznie inicjować zmienne do zera, chociaż na tym nie należy polegać.
  • Funkcja main: Funkcja main wywołuje obie powyższe funkcje w kolejności, demonstrując kontrast między zainicjowaną a niezainicjowaną zmienną.

Przykład ARM64

To w ogóle się nie zmienia w ARM64, ponieważ zmienne lokalne są również zarządzane na stosie, możesz sprawdzić ten przykład, gdzie to jest pokazane.

{% hint style="success" %} Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks
{% endhint %}