Uninitialized Variables

Aprende hacking en AWS de cero a héroe con htARTE (Experto en Red Team de AWS de HackTricks)!

Otras formas de apoyar a HackTricks:

Información Básica

La idea principal aquí es entender qué sucede con las variables no inicializadas, ya que tendrán el valor que ya estaba asignado en la memoria asignada a ellas. Ejemplo:

  • Función 1: initializeVariable: Declaramos una variable x y le asignamos un valor, digamos 0x1234. Esta acción es similar a reservar un espacio en la memoria y poner un valor específico en él.

  • Función 2: useUninitializedVariable: Aquí, declaramos otra variable y pero no le asignamos ningún valor. En C, las variables no inicializadas no se establecen automáticamente en cero. En su lugar, conservan el valor que estaba almacenado en su ubicación de memoria.

Cuando ejecutamos estas dos funciones secuencialmente:

  1. En initializeVariable, x se le asigna un valor (0x1234), que ocupa una dirección de memoria específica.

  2. En useUninitializedVariable, se declara y pero no se le asigna un valor, por lo que toma el lugar de memoria justo después de x. Debido a no inicializar y, termina "heredando" el valor de la misma ubicación de memoria utilizada por x, porque ese fue el último valor que estaba allí.

Este comportamiento ilustra un concepto clave en la programación de bajo nivel: La gestión de memoria es crucial, y las variables no inicializadas pueden llevar a un comportamiento impredecible o vulnerabilidades de seguridad, ya que pueden contener involuntariamente datos sensibles dejados en la memoria.

Las variables de la pila no inicializadas podrían plantear varios riesgos de seguridad como:

  • Fuga de datos: Información sensible como contraseñas, claves de cifrado o detalles personales pueden ser expuestos si se almacenan en variables no inicializadas, lo que permite a los atacantes potencialmente leer estos datos.

  • Divulgación de información: El contenido de variables no inicializadas podría revelar detalles sobre el diseño de memoria del programa u operaciones internas, ayudando a los atacantes a desarrollar exploits dirigidos.

  • Fallos e Inestabilidad: Las operaciones que involucran variables no inicializadas pueden resultar en un comportamiento indefinido, lo que lleva a bloqueos del programa o resultados impredecibles.

  • Ejecución de Código Arbitrario: En ciertos escenarios, los atacantes podrían explotar estas vulnerabilidades para alterar el flujo de ejecución del programa, lo que les permite ejecutar código arbitrario, que podría incluir amenazas de ejecución de código remoto.

Ejemplo

#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;
}

Cómo Funciona:

  • Función initializeAndPrint: Esta función declara una variable entera initializedVar, le asigna el valor 100 y luego imprime tanto la dirección de memoria como el valor de la variable. Este paso es directo y muestra cómo se comporta una variable inicializada.

  • Función demonstrateUninitializedVar: En esta función, declaramos una variable entera uninitializedVar sin inicializarla. Cuando intentamos imprimir su valor, la salida puede mostrar un número aleatorio. Este número representa cualquier dato que estuviera previamente en esa ubicación de memoria. Dependiendo del entorno y del compilador, la salida real puede variar y, a veces, por seguridad, algunos compiladores podrían inicializar automáticamente las variables a cero, aunque no se debe confiar en esto.

  • Función main: La función main llama a ambas funciones anteriores en secuencia, demostrando la diferencia entre una variable inicializada y una no inicializada.

Ejemplo ARM64

Esto no cambia en absoluto en ARM64, ya que las variables locales también se gestionan en la pila, puedes ver este ejemplo donde se muestra esto.

Última actualización