अनप्रारम्भित वेरिएबल्स

Tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें

बुनियादी जानकारी

मुख्य विचार यह समझना है कि अनप्रारम्भित वेरिएबल्स का क्या होता है क्योंकि उनका मान उस मेमोरी में पहले मौजूद मान को ही ले लेता है जिसे उन्हें असाइन किया गया था। उदाहरण:

  • Function 1: initializeVariable: हम एक वेरिएबल x घोषित करते हैं और उसे एक मान देते हैं, उदाहरण के लिए 0x1234। यह कार्रवाई मेमोरी में एक जगह रिज़र्व करने और उसमें एक विशेष मान रखने के समान है।
  • Function 2: useUninitializedVariable: यहाँ हम एक और वेरिएबल y घोषित करते हैं लेकिन उसे कोई मान नहीं देते। C में, अनप्रारम्भित वेरिएबल्स अपने आप ज़ीरो पर सेट नहीं होते। इसके बजाय, वे अपनी मेमोरी लोकेशन पर आख़िरी बार जो मान स्टोर था वही बनाए रखते हैं।

जब हम इन दोनों फंक्शनों को क्रमिक रूप से चलाते हैं:

  1. initializeVariable में, x को एक मान (0x1234) असाइन किया जाता है, जो एक विशिष्ट मेमोरी एड्रेस घेरता है।
  2. useUninitializedVariable में, y घोषित होता है पर किसी मान से असाइन नहीं होता, इसलिए यह x के ठीक बाद वाली मेमोरी स्पॉट ले लेता है। y को इनिशियलाइज़ न करने के कारण, यह उसी मेमोरी लोकेशन का पिछला मान “इनहेरिट” कर लेता है, क्योंकि वही वहाँ आख़िरी बार मौजूद था।

यह व्यवहार लो-लेवल प्रोग्रामिंग में एक महत्वपूर्ण अवधारणा दर्शाता है: मेमोरी प्रबंधन अत्यंत महत्वपूर्ण है, और अनप्रारम्भित वेरिएबल्स अनपेक्षित व्यवहार या सुरक्षा कमजोरियों का कारण बन सकते हैं, क्योंकि वे अनजाने में मेमोरी में छोड़ी गई संवेदनशील जानकारी धारण कर सकते हैं।

अनप्रारम्भित स्टैक वेरिएबल्स कई सुरक्षा जोखिम पैदा कर सकते हैं जैसे:

  • Data Leakage: संवेदनशील जानकारी जैसे पासवर्ड, एन्क्रिप्शन कीज़, या व्यक्तिगत विवरण अनप्रारम्भित वेरिएबल्स में मौजूद होने पर उजागर हो सकते हैं, जिससे attackers संभवतः इन डेटा को पढ़ सकें।
  • सूचना का प्रकटीकरण (Information Disclosure): अनप्रारम्भित वेरिएबल्स की सामग्री प्रोग्राम की मेमोरी लेआउट या आंतरिक कार्यप्रणाली के बारे में संकेत दे सकती है, जिससे attackers लक्षित exploit विकसित करने में मदद मिल सकती है।
  • क्रैश और अस्थिरता (Crashes and Instability): अनप्रारम्भित वेरिएबल्स के साथ संचालन अपरिभाषित व्यवहार का कारण बन सकता है, जिससे प्रोग्राम क्रैश या अनपेक्षित परिणाम हो सकते हैं।
  • Arbitrary Code Execution: कुछ परिस्थितियों में, attackers इन कमजोरियों का लाभ उठाकर प्रोग्राम के execution flow को बदल सकते हैं, जिससे वे arbitrary code execute कर सकें, जिसमें 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;
}

How This Works:

  • initializeAndPrint Function: यह फ़ंक्शन एक integer वेरिएबल initializedVar घोषित करता है, उसे मान 100 असाइन करता है, और फिर वेरिएबल का memory address और value दोनों प्रिंट करता है। यह चरण सरल है और दिखाता है कि एक initialized वेरिएबल कैसे व्यवहार करता है।
  • demonstrateUninitializedVar Function: इस फ़ंक्शन में हम एक integer वेरिएबल uninitializedVar बिना initialize किए घोषित करते हैं। जब हम इसका मान प्रिंट करने का प्रयास करते हैं, तो आउटपुट में एक यादृच्छिक नंबर दिख सकता है। यह संख्या उस मेमोरी लोकेशन पर पहले मौजूद डेटा का प्रतिनिधित्व करती है। वातावरण और compiler के अनुसार वास्तविक आउटपुट भिन्न हो सकता है, और कभी-कभी सुरक्षा के लिए कुछ compilers वेरिएबल्स को अपने आप शून्य पर initialize कर सकते हैं, हालांकि इस पर भरोसा नहीं किया जाना चाहिए।
  • main Function: main फ़ंक्शन ऊपर दिए गए दोनों फ़ंक्शन्स को क्रमशः कॉल करता है, जिससे एक initialized वेरिएबल और एक uninitialized वेरिएबल के बीच का अंतर स्पष्ट होता है।

Practical exploitation patterns (2024–2025)

क्लासिक “read-before-write” bug अभी भी प्रासंगिक है क्योंकि आधुनिक mitigations (ASLR, canaries) अक्सर secrecy पर निर्भर करते हैं। सामान्य attack surfaces:

  • Partially initialized structs copied to userland: Kernel या drivers अक्सर केवल length field को memset करते हैं और फिर copy_to_user(&u, &local_struct, sizeof(local_struct)) करते हैं। Padding और unused fields stack canary halves, saved frame pointers या kernel pointers को leak कर देते हैं। यदि struct में function pointer है, उसे uninitialized छोड़ने से बाद में reuse पर controlled overwrite की अनुमति भी मिल सकती है।
  • Uninitialized stack buffers reused as indexes/lengths: एक uninitialized size_t len; जिसका उपयोग read(fd, buf, len) की सीमा निर्धारित करने के लिए किया जाए, attackers को out-of-bounds reads/writes दे सकता है या size checks को bypass कर सकता है जब stack slot में पहले के कॉल की वजह से बड़ी value बनी हुई हो।
  • Compiler-added padding: भले ही व्यक्तिगत members initialize हों, उनके बीच के implicit padding bytes initialize नहीं होते। पूरे struct को userland में copy करने पर padding leak हो जाती है जो अक्सर prior stack content (canaries, pointers) रखती है।
  • ROP/Canary disclosure: यदि कोई फ़ंक्शन debugging के लिए स्थानीय struct को stdout पर copy करता है, तो uninitialized padding stack canary को reveal कर सकता है जिससे बाद में stack overflow exploitation बिना brute-force के संभव हो जाता है।

Minimal PoC pattern to detect such issues during review:

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 विकल्प (बायपास करते समय ध्यान रखें)

  • Clang/GCC auto-init: हाल के toolchains -ftrivial-auto-var-init=zero या -ftrivial-auto-var-init=pattern एक्सपोज़ करते हैं, जो फंक्शन एंट्री पर हर automatic (stack) वैरिएबल को zeros या एक poison pattern (0xAA / 0xFE) से भर देते हैं। यह अधिकांश uninitialized-stack info leaks बंद कर देता है और secrets को ज्ञात मानों में बदलकर exploitation को कठिन बना देता है।
  • Linux kernel hardening: Kernels जो CONFIG_INIT_STACK_ALL या नए CONFIG_INIT_STACK_ALL_PATTERN के साथ बनाए गए हैं, function entry पर हर stack slot को zero/pattern-initialize करते हैं, उन canaries/pointers को मिटा देते हैं जो अन्यथा leak होते। ऐसे विकल्प सक्षम करके Clang-built kernels भेजने वाले distros देखें (6.8+ hardening configs में सामान्य)।
  • Opt-out attributes: Clang अब specific locals/structs पर __attribute__((uninitialized)) की अनुमति देता है ताकि performance-critical क्षेत्रों को uninitialized रखा जा सके, यहाँ तक कि जब global auto-init सक्षम हो। ऐसे annotations को ध्यान से देखें—वे अक्सर side channels के लिए जानबूझकर attack surface को चिह्नित करते हैं।

एक attacker के नजरिए से, यह जानना कि बाइनरी इन flags के साथ बनी थी या नहीं तय करता है कि stack-leak primitives viable हैं या आपको heap/data-section disclosures की ओर pivot करना होगा।

Finding uninitialized-stack bugs quickly

  • Compiler diagnostics: -Wall -Wextra -Wuninitialized (GCC/Clang) के साथ बिल्ड करें। C++ कोड के लिए, clang-tidy -checks=cppcoreguidelines-init-variables कई मामलों को auto-fix कर के zero-init कर देता है और ऑडिट के दौरान मिस्ड locals को दिखाने में handy है।
  • Dynamic tools: Clang में -fsanitize=memory (MSan) या Valgrind का --track-origins=yes fuzzing के दौरान uninitialized stack bytes के reads को reliably flag करते हैं। टेस्ट हार्नेस को इनसे instrument करें ताकि subtle padding leaks surface हों।
  • Grepping patterns: रिव्यू में उन copy_to_user / write कॉल्स को खोजें जो पूरे structs को भेजती हैं, या ऐसे memcpy/send जो केवल struct के हिस्से को सेट करते हैं पर stack डेटा को भेज देते हैं। खास ध्यान error paths पर रखें जहाँ initialization स्किप किया जाता है।

ARM64 Example

यह ARM64 में बिल्कुल भी नहीं बदलता क्योंकि local variables भी stack पर manage होते हैं, आप check this example में यह देख सकते हैं。

References

Tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें