Variáveis Não Inicializadas
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Informações Básicas
A ideia central aqui é entender o que acontece com variáveis não inicializadas, pois elas terão o valor que já estava na área de memória atribuída a elas. Exemplo:
- Function 1:
initializeVariable: Declaramos uma variávelxe atribuímos um valor, digamos0x1234. Essa ação é semelhante a reservar um espaço na memória e colocar um valor específico nele. - Function 2:
useUninitializedVariable: Aqui, declaramos outra variávelymas não atribuímos nenhum valor a ela. Em C, variáveis não inicializadas não são automaticamente zeradas. Em vez disso, elas retêm o valor que foi armazenado por último na sua posição de memória.
Quando executamos essas duas funções sequencialmente:
- Em
initializeVariable,xrecebe um valor (0x1234), que ocupa um endereço de memória específico. - Em
useUninitializedVariable,yé declarada mas não recebe valor, então ocupa a posição de memória imediatamente apósx. Por não inicializary, ela acaba “herdando” o valor da mesma posição de memória usada porx, porque esse foi o último valor presente ali.
Esse comportamento ilustra um conceito chave em programação de baixo nível: o gerenciamento de memória é crucial, e variáveis não inicializadas podem levar a comportamentos imprevisíveis ou vulnerabilidades de segurança, pois podem reter inadvertidamente dados sensíveis deixados na memória.
Variáveis de stack não inicializadas podem representar vários riscos de segurança, como:
- Data Leakage: Informações sensíveis, como senhas, chaves de criptografia ou dados pessoais, podem ser expostas se armazenadas em variáveis não inicializadas, permitindo que atacantes potencialmente leiam esses dados.
- Information Disclosure: O conteúdo de variáveis não inicializadas pode revelar detalhes sobre o layout de memória do programa ou operações internas, auxiliando atacantes a desenvolver exploits direcionados.
- Crashes and Instability: Operações envolvendo variáveis não inicializadas podem resultar em comportamento indefinido, levando a falhas do programa ou resultados imprevisíveis.
- Arbitrary Code Execution: Em certos cenários, atacantes poderiam explorar essas vulnerabilidades para alterar o fluxo de execução do programa, possibilitando a execução de código arbitrário, o que pode incluir ameaças de execução remota de código.
Exemplo
#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;
}
Como Isso Funciona:
initializeAndPrintFunction: Esta função declara uma variável inteirainitializedVar, atribui a ela o valor100e então imprime tanto o endereço de memória quanto o valor da variável. Este passo é simples e mostra como uma variável inicializada se comporta.demonstrateUninitializedVarFunction: Nesta função, declaramos uma variável inteirauninitializedVarsem inicializá-la. Ao tentar imprimir seu valor, a saída pode mostrar um número aleatório. Esse número representa quaisquer dados que estavam previamente naquela posição de memória. Dependendo do ambiente e do compilador, a saída real pode variar, e às vezes, por segurança, alguns compiladores podem inicializar automaticamente variáveis com zero, embora não se deva confiar nisso.mainFunction: A funçãomainchama ambas as funções acima em sequência, demonstrando o contraste entre uma variável inicializada e uma não inicializada.
Padrões práticos de exploração (2024–2025)
O clássico bug “read-before-write” continua relevante porque mitigações modernas (ASLR, canaries) frequentemente dependem do sigilo. Superfícies de ataque típicas:
- Structs parcialmente inicializados copiados para userland: Kernel ou drivers frequentemente
memsetapenas um campo de comprimento e entãocopy_to_user(&u, &local_struct, sizeof(local_struct)). Padding e campos não usados leak metade(s) do stack canary, saved frame pointers ou kernel pointers. Se o struct contém um function pointer, deixá-lo não inicializado pode também permitir controlled overwrite quando for reutilizado mais tarde. - Buffers de stack não inicializados reutilizados como indexes/lengths: Um
size_t len;não inicializado usado para limitarread(fd, buf, len)pode dar aos atacantes leituras/escritas out-of-bounds ou permitir contornar checagens de tamanho quando a posição na stack ainda contém um valor grande de uma chamada anterior. - Padding adicionado pelo compilador: Mesmo quando membros individuais são inicializados, bytes de padding implícitos entre eles não são. Copiar o struct inteiro para userland leak padding que frequentemente contém conteúdo anterior da stack (canaries, pointers).
- ROP/Canary disclosure: Se uma função copia um struct local para stdout para debugging, padding não inicializado pode revelar o stack canary, permitindo exploração de stack overflow subsequente sem brute-force.
Padrão mínimo de PoC para detectar esses problemas durante a revisão:
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;
}
Mitigações & opções do compilador (tenha em mente ao contornar)
- Clang/GCC auto-init: Toolchains recentes expõem
-ftrivial-auto-var-init=zeroou-ftrivial-auto-var-init=pattern, preenchendo toda variável automática (stack) na entrada da função com zeros ou um padrão de poison (0xAA / 0xFE). Isso fecha a maioria dos uninitialized-stack info leaks e torna a exploração mais difícil ao converter segredos em valores conhecidos. - Linux kernel hardening: Kernels compilados com
CONFIG_INIT_STACK_ALLou o mais novoCONFIG_INIT_STACK_ALL_PATTERNinicializam com zero/padrão cada slot da stack na entrada da função, apagando canaries/pointers que, de outra forma, leak. Procure por distros que entreguem kernels compilados com Clang com essas opções habilitadas (comum em configs de hardening 6.8+). - Opt-out attributes: Clang agora permite
__attribute__((uninitialized))em locais/structs específicos para manter áreas críticas para performance não inicializadas mesmo quando o auto-init global está ativado. Revise essas anotações cuidadosamente — elas frequentemente marcam superfície de ataque deliberada para side channels.
Do ponto de vista do atacante, saber se o binário foi construído com essas flags determina se stack-leak primitives são viáveis ou se você deve pivotar para divulgações do heap/seção-de-dados.
Finding uninitialized-stack bugs quickly
- Compiler diagnostics: Compile com
-Wall -Wextra -Wuninitialized(GCC/Clang). Para código C++,clang-tidy -checks=cppcoreguidelines-init-variablesirá auto-fixar muitos casos para zero-init e é útil para localizar locais esquecidos durante a auditoria. - Dynamic tools:
-fsanitize=memory(MSan) no Clang ou o Valgrind com--track-origins=yessinalizam de forma confiável leituras de bytes de stack uninitialized durante fuzzing. Instrumente test harnesses com essas ferramentas para evidenciar padding leaks sutis. - Grepping patterns: Em revisões, procure por chamadas
copy_to_user/writede structs inteiros, oumemcpy/sendde dados da stack onde somente parte do struct é preenchida. Preste atenção especial a caminhos de erro onde a inicialização é pulada.
Exemplo ARM64
This doesn’t change at all in ARM64 as local variables are also managed in the stack, you can check this example were this is shown.
Referências
- CONFIG_INIT_STACK_ALL_PATTERN documentation
- GHSL-2024-197: GStreamer uninitialized stack variable leading to function pointer overwrite
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.


