Uninitialized Variables
Tip
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Βασικές Πληροφορίες
Η βασική ιδέα εδώ είναι να κατανοήσουμε τι συμβαίνει με uninitialized variables καθώς θα έχουν την τιμή που ήδη βρισκόταν στη μνήμη που τους έχει ανατεθεί. Παράδειγμα:
- Function 1:
initializeVariable: Δηλώνουμε μια μεταβλητήxκαι της αναθέτουμε μια τιμή, ας πούμε0x1234. Αυτή η ενέργεια είναι παρόμοια με την κράτηση ενός χώρου στη μνήμη και την τοποθέτηση μιας συγκεκριμένης τιμής σε αυτόν. - Function 2:
useUninitializedVariable: Εδώ δηλώνουμε μια άλλη μεταβλητήyαλλά δεν της αναθέτουμε καμία τιμή. Στη C, οι uninitialized variables δεν μηδενίζονται αυτόματα. Αντίθετα, διατηρούν όποια τιμή είχε αποθηκευτεί τελευταία στη διεύθυνση μνήμης τους.
Όταν εκτελούμε αυτές τις δύο συναρτήσεις διαδοχικά:
- Στη
initializeVariable, στη μεταβλητήxανατίθεται μια τιμή (0x1234), η οποία καταλαμβάνει μια συγκεκριμένη διεύθυνση μνήμης. - Στη
useUninitializedVariable, ηyδηλώνεται αλλά δεν της ανατίθεται τιμή, οπότε καταλαμβάνει τη θέση μνήμης αμέσως μετά τηνx. Επειδή δεν αρχικοποιείται ηy, καταλήγει να «κληρονομεί» την τιμή από την ίδια διεύθυνση μνήμης που χρησιμοποίησε ηx, επειδή αυτή ήταν η τελευταία τιμή που υπήρχε εκεί.
Αυτή η συμπεριφορά καταδεικνύει ένα βασικό σημείο στον προγραμματισμό χαμηλού επιπέδου: Η διαχείριση μνήμης είναι κρίσιμη, και οι uninitialized variables μπορούν να οδηγήσουν σε απρόβλεπτη συμπεριφορά ή σε ευπάθειες ασφαλείας, καθώς μπορεί να κρατούν ακούσια ευαίσθητα δεδομένα που έχουν μείνει στη μνήμη.
Οι Uninitialized stack variables μπορούν να προκαλέσουν αρκετούς κινδύνους ασφάλειας όπως:
- Data Leakage: Ευαίσθητες πληροφορίες όπως κωδικοί πρόσβασης, κλειδιά κρυπτογράφησης ή προσωπικά δεδομένα μπορούν να εκτεθούν αν αποθηκευτούν σε uninitialized variables, επιτρέποντας σε επιτιθέμενους να διαβάσουν αυτά τα δεδομένα.
- Information Disclosure: Το περιεχόμενο των uninitialized variables μπορεί να αποκαλύψει λεπτομέρειες για τη διάταξη μνήμης του προγράμματος ή τις εσωτερικές λειτουργίες του, βοηθώντας τους επιτιθέμενους στην ανάπτυξη στοχευμένων exploits.
- Crashes and Instability: Ενέργειες που αφορούν uninitialized variables μπορεί να οδηγήσουν σε μη καθορισμένη συμπεριφορά, προκαλώντας καταρρεύσεις του προγράμματος ή απρόβλεπτα αποτελέσματα.
- Arbitrary Code Execution: Σε ορισμένα σενάρια, επιτιθέμενοι μπορούν να εκμεταλλευτούν αυτές τις ευπάθειες για να αλλάξουν τη ροή εκτέλεσης του προγράμματος, επιτρέποντάς τους να εκτελέσουν arbitrary code, το οποίο μπορεί να περιλαμβάνει απειλές για remote code execution.
Παράδειγμα
#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;
}
Πώς λειτουργεί αυτό:
initializeAndPrintΣυνάρτηση: Η συνάρτηση αυτή δηλώνει μια ακέραια μεταβλητήinitializedVar, της αναθέτει την τιμή100και στη συνέχεια εκτυπώνει τόσο τη διεύθυνση μνήμης όσο και την τιμή της μεταβλητής. Αυτό το βήμα είναι απλό και δείχνει πώς συμπεριφέρεται μια αρχικοποιημένη μεταβλητή.demonstrateUninitializedVarΣυνάρτηση: Σε αυτή τη συνάρτηση δηλώνουμε μια ακέραια μεταβλητήuninitializedVarχωρίς να την αρχικοποιήσουμε. Όταν προσπαθήσουμε να εκτυπώσουμε την τιμή της, η έξοδος μπορεί να δείξει έναν τυχαίο αριθμό. Αυτός ο αριθμός αντιπροσωπεύει όποια δεδομένα υπήρχαν προηγουμένως σε εκείνη τη θέση μνήμης. Ανάλογα με το περιβάλλον και τον compiler, η πραγματική έξοδος μπορεί να διαφέρει, και μερικές φορές, για λόγους ασφάλειας, κάποιοι compilers μπορεί να αρχικοποιούν αυτόματα τις μεταβλητές στο μηδέν — ωστόσο αυτό δεν πρέπει να θεωρείται δεδομένο.mainΣυνάρτηση: Ηmainσυνάρτηση καλεί και τις δύο παραπάνω συναρτήσεις με τη σειρά, δείχνοντας την αντίθεση μεταξύ μιας αρχικοποιημένης μεταβλητής και μιας μη αρχικοποιημένης.
Πρακτικά μοτίβα εκμετάλλευσης (2024–2025)
Το κλασικό “read-before-write” bug παραμένει σημαντικό επειδή τα σύγχρονα mitigations (ASLR, canaries) συχνά βασίζονται στην μυστικότητα. Τυπικές επιφάνειες επίθεσης:
- Partially initialized structs copied to userland: Kernel ή drivers συχνά κάνουν
memsetμόνο σε ένα πεδίο μήκους και μετάcopy_to_user(&u, &local_struct, sizeof(local_struct)). Padding και μη χρησιμοποιημένα πεδία leak μισά του stack canary, saved frame pointers ή kernel pointers. Εάν το struct περιέχει ένα function pointer, η μη αρχικοποίησή του μπορεί επίσης να επιτρέψει controlled overwrite όταν επαναχρησιμοποιηθεί αργότερα. - Uninitialized stack buffers reused as indexes/lengths: Ένα μη αρχικοποιημένο
size_t len;που χρησιμοποιείται για να περιορίσει τοread(fd, buf, len)μπορεί να δώσει σε επιτιθέμενους out-of-bounds reads/writes ή να επιτρέψει το bypassing των size checks όταν η θέση στο stack εξακολουθεί να περιέχει μια μεγάλη τιμή από προηγούμενη κλήση. - Compiler-added padding: Ακόμα και όταν μεμονωμένα μέλη αρχικοποιούνται, τα implicit padding bytes ανάμεσά τους όχι. Η αντιγραφή ολόκληρου του struct στο userland leaks padding που συχνά περιέχει προηγούμενο περιεχόμενο του stack (canaries, pointers).
- ROP/Canary disclosure: Εάν μια συνάρτηση αντιγράψει ένα local struct στο stdout για debugging, το μη αρχικοποιημένο padding μπορεί να αποκαλύψει το stack canary, επιτρέποντας επακόλουθη εκμετάλλευση stack overflow χωρίς brute-force.
Ελάχιστο μοτίβο PoC για τον εντοπισμό τέτοιων ζητημάτων κατά την ανασκόπηση:
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;
}
Αντιμετώπιση & compiler options (κρατήστε υπόψη όταν παρακάμπτετε)
- Clang/GCC auto-init: Πρόσφατα toolchains εκθέτουν
-ftrivial-auto-var-init=zeroή-ftrivial-auto-var-init=pattern, γεμίζοντας κάθε automatic (stack) μεταβλητή στην είσοδο της συνάρτησης με μηδενικά ή ένα poison pattern (0xAA / 0xFE). Αυτό κλείνει τα περισσότερα uninitialized-stack info leaks και κάνει την εκμετάλλευση πιο δύσκολη μετατρέποντας secrets σε γνωστές τιμές. - Linux kernel hardening: Πυρήνες που χτίζονται με
CONFIG_INIT_STACK_ALLή το νεότεροCONFIG_INIT_STACK_ALL_PATTERNμηδενίζουν/αρχικοποιούν με pattern κάθε θέση του stack κατά την είσοδο συνάρτησης, σβήνοντας canaries/pointers που αλλιώς θα leak. Ψάξτε για διανομές που διανέμουν Clang-built kernels με αυτές τις επιλογές ενεργοποιημένες (συνήθως σε 6.8+ hardening configs). - Opt-out attributes: Το Clang πλέον επιτρέπει
__attribute__((uninitialized))σε συγκεκριμένους locals/structs για να κρατά περιοχές κρίσιμες για απόδοση μη αρχικοποιημένες ακόμα και όταν η global auto-init είναι ενεργή. Ελέγξτε προσεκτικά τέτοιες annotations—συχνά σημειώνουν σκόπιμη επιφάνεια επίθεσης για side channels.
Από την πλευρά του επιτιθέμενου, το αν το binary χτίστηκε με αυτές τις flags καθορίζει αν τα stack-leak primitives είναι βιώσιμα ή αν πρέπει να μεταβείτε σε heap/data-section disclosures.
Εύρεση uninitialized-stack σφαλμάτων γρήγορα
- Compiler diagnostics: Κάντε build με
-Wall -Wextra -Wuninitialized(GCC/Clang). Για C++ κώδικα, τοclang-tidy -checks=cppcoreguidelines-init-variablesθα auto-fixάρει πολλές περιπτώσεις σε zero-init και είναι χρήσιμο για να εντοπίσετε missed locals κατά την audit. - Dynamic tools:
-fsanitize=memory(MSan) στο Clang ή το Valgrind με--track-origins=yesεπισημαίνουν αξιόπιστα αναγνώσεις μη αρχικοποιημένων bytes του stack κατά το fuzzing. Ενσωματώστε test harnesses με αυτά για να αναδείξετε subtle padding leaks. - Grepping patterns: Στις αναθεωρήσεις, ψάξτε για κλήσεις
copy_to_user/writeολόκληρων structs, ήmemcpy/sendδεδομένων του stack όπου μόνο μέρος του struct έχει οριστεί. Δώστε ιδιαίτερη προσοχή σε error paths όπου η αρχικοποίηση παραλείπεται.
Παράδειγμα ARM64
Αυτό δεν αλλάζει καθόλου σε ARM64 καθώς οι τοπικές μεταβλητές διαχειρίζονται επίσης στο stack — μπορείτε να check this example όπου αυτό αποδεικνύεται.
References
- CONFIG_INIT_STACK_ALL_PATTERN documentation
- GHSL-2024-197: GStreamer uninitialized stack variable leading to function pointer overwrite
Tip
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.


