Uninitialized Variables

Aprenda hacking na AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks:

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 memória atribuída a elas. Exemplo:

  • Função 1: initializeVariable: Declaramos uma variável x e atribuímos a ela um valor, digamos 0x1234. Essa ação é semelhante a reservar um local na memória e colocar um valor específico nele.

  • Função 2: useUninitializedVariable: Aqui, declaramos outra variável y, mas não atribuímos nenhum valor a ela. Em C, variáveis não inicializadas não são automaticamente definidas como zero. Em vez disso, elas mantêm o valor que foi armazenado por último em sua localização de memória.

Quando executamos essas duas funções sequencialmente:

  1. Em initializeVariable, x é atribuído um valor (0x1234), que ocupa um endereço de memória específico.

  2. Em useUninitializedVariable, y é declarado, mas não é atribuído um valor, então ele ocupa o local de memória logo após x. Devido à não inicialização de y, ele acaba "herdando" o valor da mesma localização de memória usada por x, porque esse foi o último valor que estava lá.

Esse comportamento ilustra um conceito-chave na programação de baixo nível: A gestão de memória é crucial, e variáveis não inicializadas podem levar a comportamentos imprevisíveis ou vulnerabilidades de segurança, pois podem inadvertidamente conter dados sensíveis deixados na memória.

Variáveis de pilha não inicializadas podem representar vários riscos de segurança, como:

  • Vazamento de Dados: Informações sensíveis, como senhas, chaves de criptografia ou detalhes pessoais, podem ser expostos se armazenados em variáveis não inicializadas, permitindo que os atacantes potencialmente leiam esses dados.

  • Divulgação de Informações: 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 os atacantes no desenvolvimento de exploits direcionados.

  • Falhas e Instabilidade: Operações envolvendo variáveis não inicializadas podem resultar em comportamento indefinido, levando a falhas no programa ou resultados imprevisíveis.

  • Execução de Código Arbitrário: Em cenários específicos, os atacantes poderiam explorar essas vulnerabilidades para alterar o fluxo de execução do programa, permitindo que executem código arbitrário, que pode incluir ameaças de execução de código remoto.

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:

  • Função initializeAndPrint: Esta função declara uma variável inteira initializedVar, atribui a ela o valor 100 e, em seguida, imprime tanto o endereço de memória quanto o valor da variável. Este passo é direto e mostra como uma variável inicializada se comporta.

  • Função demonstrateUninitializedVar: Nesta função, declaramos uma variável inteira uninitializedVar sem inicializá-la. Quando tentamos imprimir seu valor, a saída pode mostrar um número aleatório. Este número representa qualquer dado que estava previamente naquela localizaçã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 para zero, embora isso não deva ser confiável.

  • Função main: A função main chama ambas as funções acima em sequência, demonstrando o contraste entre uma variável inicializada e uma não inicializada.

Exemplo ARM64

Isso não muda em nada no ARM64, pois as variáveis locais também são gerenciadas na pilha, você pode verificar este exemplo onde isso é mostrado.

Last updated