Uninitialisierte Variablen
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Grundlegende Informationen
Die Kernidee ist zu verstehen, was mit uninitialisierten Variablen passiert: sie enthalten den Wert, der bereits an der ihnen zugewiesenen Speicherstelle vorhanden war. Beispiel:
- Function 1:
initializeVariable: Wir deklarieren eine Variablexund weisen ihr einen Wert zu, sagen wir0x1234. Diese Aktion ist vergleichbar damit, einen Platz im Speicher zu reservieren und einen bestimmten Wert dort abzulegen. - Function 2:
useUninitializedVariable: Hier deklarieren wir eine weitere Variabley, weisen ihr aber keinen Wert zu. In C werden uninitialisierte Variablen nicht automatisch auf Null gesetzt. Stattdessen behalten sie den Wert, der zuletzt an ihrer Speicheradresse gespeichert war.
Wenn wir diese beiden Funktionen nacheinander ausführen:
- In
initializeVariablewirdxein Wert (0x1234) zugewiesen, der eine bestimmte Speicheradresse belegt. - In
useUninitializedVariablewirdydeklariert, aber nicht initialisiert, sodass es den direkt nachxfolgenden Speicherplatz einnimmt. Daynicht initialisiert wurde, “erbt” es schließlich den Wert aus derselben Speicherstelle, die vonxverwendet wurde, weil das der zuletzt dort abgelegte Wert ist.
Dieses Verhalten veranschaulicht ein zentrales Konzept in der Low-Level-Programmierung: Speichermanagement ist entscheidend, und uninitialisierte Variablen können zu nicht vorhersehbarem Verhalten oder Sicherheitslücken führen, da sie unbeabsichtigt sensible Daten enthalten können, die im Speicher zurückgelassen wurden.
Uninitialisierte Stack-Variablen können mehrere Sicherheitsrisiken darstellen, wie zum Beispiel:
- Data Leakage: Sensible Informationen wie Passwörter, Verschlüsselungskeys oder persönliche Daten können offengelegt werden, wenn sie in uninitialisierten Variablen gespeichert sind, was Angreifern möglicherweise das Lesen dieser Daten erlaubt.
- Information Disclosure: Der Inhalt uninitialisierter Variablen kann Details über das Speicherlayout des Programms oder seine internen Abläufe offenbaren und Angreifern beim Entwickeln gezielter Exploits helfen.
- Crashes and Instability: Operationen mit uninitialisierten Variablen können zu undefiniertem Verhalten führen, was Programmabstürze oder unvorhersehbare Ergebnisse zur Folge haben kann.
- Arbitrary Code Execution: In bestimmten Szenarien könnten Angreifer diese Schwachstellen ausnutzen, um den Programmablauf zu verändern und beliebigen Code auszuführen, was auch remote code execution-Bedrohungen umfassen kann.
Beispiel
#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;
}
Wie das funktioniert:
initializeAndPrintFunction: Diese Funktion deklariert eine GanzzahlvariableinitializedVar, weist ihr den Wert100zu und gibt dann sowohl die Speicheradresse als auch den Wert der Variable aus. Dieser Schritt ist unkompliziert und zeigt, wie sich eine initialisierte Variable verhält.demonstrateUninitializedVarFunction: In dieser Funktion deklarieren wir eine GanzzahlvariableuninitializedVarohne sie zu initialisieren. Wenn wir versuchen, ihren Wert auszugeben, kann die Ausgabe eine zufällige Zahl zeigen. Diese Zahl repräsentiert die Daten, die zuvor an dieser Speicherstelle vorhanden waren. Abhängig von der Umgebung und dem Compiler kann die tatsächliche Ausgabe variieren, und manchmal initialisieren einige Compiler aus Sicherheitsgründen Variablen automatisch auf null, obwohl man sich darauf nicht verlassen sollte.mainFunction: Diemain-Funktion ruft beide oben genannten Funktionen nacheinander auf und demonstriert den Kontrast zwischen einer initialisierten Variable und einer uninitialisierten.
Praktische Exploit-Muster (2024–2025)
Der klassische “read-before-write” Bug bleibt relevant, weil moderne Mitigations (ASLR, canaries) oft auf Geheimhaltung beruhen. Typische Angriffsflächen:
- Partially initialized structs copied to userland: Kernel oder Treiber führen häufig nur ein
memsetfür ein Längenfeld aus und danncopy_to_user(&u, &local_struct, sizeof(local_struct)). Padding und ungenutzte Felder leak stack canary halves, saved frame pointers or kernel pointers. Wenn die Struct einen function pointer enthält, kann das Nicht-Initialisieren später auch eine controlled overwrite ermöglichen, wenn sie wiederverwendet wird. - Uninitialized stack buffers reused as indexes/lengths: Ein uninitialisiertes
size_t len;, das verwendet wird, umread(fd, buf, len)zu begrenzen, kann Angreifern out-of-bounds reads/writes erlauben oder das Umgehen von Größenprüfungen ermöglichen, wenn der Stack-Slot noch einen großen Wert aus einem vorherigen Aufruf enthält. - Compiler-added padding: Selbst wenn einzelne Members initialisiert sind, werden implizite Padding-Bytes dazwischen nicht initialisiert. Das Kopieren der gesamten Struct in userland leak padding, das häufig vorherigen Stack-Content (canaries, pointers) enthält.
- ROP/Canary disclosure: Wenn eine Funktion eine lokale Struct zum Debugging auf stdout kopiert, kann uninitialisiertes Padding den stack canary offenbaren und damit eine nachfolgende stack overflow exploitation ohne Brute-Force ermöglichen.
Minimaler PoC-Pattern, um solche Probleme während der Review zu erkennen:
struct msg {
char data[0x20];
uint32_t len;
};
ssize_t handler(int fd) {
struct msg m; // never fully initialized
m.len = read(fd, m.data, sizeof(m.data));
// later debug helper
write(1, &m, sizeof(m)); // leaks padding + stale stack
return m.len;
}
Gegenmaßnahmen & Compiler-Optionen (bei Umgehung beachten)
- Clang/GCC auto-init: Neuere Toolchains bieten
-ftrivial-auto-var-init=zerooder-ftrivial-auto-var-init=pattern, die jede automatische (Stack-)Variable beim Funktionsbeginn mit Nullen oder einem Poison-Pattern (0xAA / 0xFE) füllen. Das schließt die meisten uninitialized-stack info leaks und erschwert Exploits, indem Secrets in bekannte Werte umgewandelt werden. - Linux kernel hardening: Kernel, die mit
CONFIG_INIT_STACK_ALLoder dem neuerenCONFIG_INIT_STACK_ALL_PATTERNgebaut werden, null-/pattern-initialisieren beim Funktionsbeginn jedes Stack-Slot und löschen damit Canaries/Pointer, die sonst leaken würden. Achte auf distros, die Clang-gebuildete Kernel mit diesen Optionen ausliefern (häufig in 6.8+ Hardening-Konfigurationen). - Opt-out-Attribute: Clang erlaubt jetzt
__attribute__((uninitialized))auf bestimmten Locals/Structs, um performance-kritische Bereiche uninitialisiert zu lassen, auch wenn globales auto-init aktiviert ist. Überprüfe solche Annotationen sorgfältig — sie markieren oft eine beabsichtigte Angriffsfläche für Side-Channels.
Aus Sicht eines Angreifers bestimmt die Kenntnis, ob das Binary mit diesen Flags gebaut wurde, ob stack-leak primitives praktikabel sind oder ob man auf heap/data-section disclosures ausweichen muss.
Schnelles Auffinden von uninitialized-stack Bugs
- Compiler-Diagnostics: Baue mit
-Wall -Wextra -Wuninitialized(GCC/Clang). Für C++-Code hilftclang-tidy -checks=cppcoreguidelines-init-variables, das viele Fälle automatisch auf zero-init setzt und praktisch ist, um übersehene Locals während eines Audits zu finden. - Dynamische Tools:
-fsanitize=memory(MSan) in Clang oder Valgrind’s--track-origins=yesmelden zuverlässig Reads von uninitialized Stack-Bytes während Fuzzing. Instrumentiere Test-Harnesses damit, um subtile Padding-leaks aufzudecken. - Grepping-Muster: Suche in Reviews nach
copy_to_user/write-Aufrufen ganzer Structs odermemcpy/sendvon Stack-Daten, wenn nur ein Teil des Structs gesetzt ist. Achte besonders auf Error-Pfade, in denen die Initialisierung übersprungen wird.
ARM64 Example
Dies ändert sich nicht bei ARM64, da lokale Variablen ebenfalls im Stack verwaltet werden — du kannst check this example sehen, wo dies gezeigt wird.
Referenzen
- CONFIG_INIT_STACK_ALL_PATTERN documentation
- GHSL-2024-197: GStreamer uninitialized stack variable leading to function pointer overwrite
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.


