未初始化变量
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
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
基本信息
这里的核心思想是要理解 未初始化变量会具有分配给它们的内存中已经存在的值。 示例:
- Function 1:
initializeVariable:我们声明一个变量x并赋予它一个值,比如0x1234。这个操作类似于在内存中保留一个位置并写入一个特定的值。 - Function 2:
useUninitializedVariable:在这里,我们声明另一个变量y但不为它赋值。在 C 中,未初始化的变量不会自动被置零。相反,它们会保留该内存位置上最后存放的值。
当我们按 顺序 运行这两个函数时:
- 在
initializeVariable中,x被赋值(0x1234),该值占据了一个特定的内存地址。 - 在
useUninitializedVariable中,y被声明但没有被赋值,因此它占用了紧接在x之后的内存位置。由于没有初始化y,它最终会“继承”来自x使用过的同一内存位置的值,因为那是该位置上最后的值。
这种行为说明了底层编程中的一个关键概念:内存管理至关重要,未初始化的变量可能导致不可预测的行为或安全漏洞,因为它们可能无意中保存了内存中残留的敏感数据。
未初始化的栈变量可能带来多种安全风险,例如:
- 数据泄露:如果敏感信息(如密码、加密密钥或个人信息)存储在未初始化的变量中,可能会被暴露,使攻击者能够读取这些数据。
- 信息披露:未初始化变量的内容可能泄露程序的内存布局或内部操作细节,帮助攻击者开发针对性的利用手段。
- 崩溃与不稳定:涉及未初始化变量的操作可能导致未定义行为,从而引发程序崩溃或不可预测的结果。
- 任意代码执行:在某些场景中,攻击者可能利用这些漏洞改变程序的执行流程,使其执行任意代码,这可能包括远程代码执行的风险。
示例
#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;
}
工作原理:
initializeAndPrintFunction:这个函数声明了一个整型变量initializedVar,将其赋值为100,然后打印该变量的内存地址和变量的值。这个步骤很直接,展示了已初始化变量的行为。demonstrateUninitializedVarFunction:在此函数中,我们声明了一个整型变量uninitializedVar,但没有对其进行初始化。当尝试打印其值时,输出可能显示一个随机数。该数字代表之前位于该内存位置的数据。根据运行环境和编译器,实际输出会有所不同,有时出于安全考虑,部分编译器可能会自动将变量初始化为零,但不应依赖这一点。mainFunction:main函数按顺序调用上述两个函数,展示已初始化变量与未初始化变量之间的对比。
实际利用模式(2024–2025)
经典的 “read-before-write” 漏洞仍然相关,因为现代缓解措施(ASLR、canaries)通常依赖保密性。典型攻击面:
- 部分初始化的 structs 被复制到 userland:kernel 或 drivers 常常只对长度字段
memset,然后copy_to_user(&u, &local_struct, sizeof(local_struct))。Padding 和未使用的字段会 leak stack canary halves、saved frame pointers 或 kernel pointers。如果 struct 包含 function pointer,保持未初始化在之后重用时也可能允许 controlled overwrite。 - Uninitialized stack buffers reused as indexes/lengths:未初始化的
size_t len;用来限制read(fd, buf, len)可能导致攻击者进行越界读/写,或在栈槽仍包含先前调用留下的大值时绕过大小检查。 - Compiler-added padding:即使各个成员被初始化,成员间的隐式 padding 字节通常不会被初始化。将整个 struct 复制到 userland 时会 leak padding,这些 padding 经常包含先前的栈内容(canaries、pointers)。
- ROP/Canary disclosure:如果函数为调试目的将本地 struct 复制到 stdout,未初始化的 padding 可能暴露 stack canary,从而使后续的栈溢出利用在无需暴力破解的情况下成为可能。
在审查期间检测此类问题的最小 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;
}
缓解措施与编译器选项(在绕过时请注意)
- Clang/GCC auto-init: 最近的 toolchains 暴露
-ftrivial-auto-var-init=zero或-ftrivial-auto-var-init=pattern,在函数入口将 every 自动(stack)变量填充为零或毒性模式 (0xAA / 0xFE)。这阻断了大多数 uninitialized-stack info leak 并通过把机密转为已知值使 exploitation 更困难。 - Linux kernel hardening: 使用
CONFIG_INIT_STACK_ALL或更新的CONFIG_INIT_STACK_ALL_PATTERN构建的 kernel 在函数入口对每个 stack slot 进行 zero/pattern 初始化,清除本来可能会 leak 的 canaries/pointers。查找那些以 Clang 构建且启用这些选项的 distros(常见于 6.8+ 的 hardening 配置)。 - Opt-out attributes: Clang 现在允许在特定的 locals/structs 上使用
__attribute__((uninitialized)),即使全局 auto-init 已启用也能让性能关键区域保持未初始化。仔细审查这些注解——它们常常标记出为 side channels 提供的故意 attack surface。
从 attacker 的角度,知道 binary 是否用这些 flags 构建能决定 stack-leak primitives 是否可行,或是否必须 pivot 到 heap/data-section disclosures。
快速定位 uninitialized-stack 漏洞
- Compiler diagnostics: 用
-Wall -Wextra -Wuninitialized(GCC/Clang) 构建。对于 C++ 代码,clang-tidy -checks=cppcoreguidelines-init-variables会自动修复很多案例为 zero-init,并且在审计时便于发现被遗漏的 locals。 - Dynamic tools: 在 Clang 中使用
-fsanitize=memory(MSan) 或 Valgrind 的--track-origins=yes,在 fuzzing 时能可靠标记对未初始化 stack 字节的读取。用这些工具去 instrument 测试 harness 以暴露微妙的 padding leak。 - Grepping patterns: 在代码审查中,搜索对整个 struct 的
copy_to_user/write调用,或对 stack 数据的memcpy/send,而 struct 仅被部分赋值的情况。特别关注那些因错误路径而跳过初始化的分支。
ARM64 示例
这在 ARM64 上完全相同,因为 local variables 也在 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 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。


