Tema 19
Los desbordamientos de buffer son una familia clásica de errores de memoria. Comprenderlos ayuda a interpretar crashes, evaluar impacto, reconocer control de flujo indebido y aplicar mitigaciones que reducen el riesgo de explotación.
Un buffer overflow ocurre cuando un programa escribe más datos de los que una región de memoria puede contener. Esa escritura excedente puede alterar datos cercanos, corromper estructuras internas, provocar un crash o, bajo condiciones específicas, modificar el flujo de ejecución.
Históricamente, los buffer overflows fueron una de las vías más conocidas para explotación de memoria. Hoy existen mitigaciones importantes, pero siguen siendo relevantes para comprender fallos, analizar crashes y evaluar riesgos en software nativo, parsers, servicios, drivers y componentes embebidos.
Este tema explica los conceptos desde una mirada educativa y defensiva. El objetivo es entender qué hace peligroso un fallo de memoria, no construir código ofensivo contra sistemas reales.
Un buffer es una zona de memoria reservada para almacenar datos temporalmente: una cadena, un paquete, una ruta, un bloque de archivo, una estructura o cualquier secuencia de bytes.
El riesgo aparece cuando el programa asume tamaños incorrectos o no valida límites. Si un buffer tiene espacio para cierta cantidad de datos y se escriben más, lo sobrante termina en memoria adyacente.
Un overflow escribe fuera de límites. Un overread lee fuera de límites. Ambos son errores de memoria, pero su impacto suele ser distinto.
| Error | Qué ocurre | Impacto posible |
|---|---|---|
| Buffer overflow | Se escriben datos más allá del buffer | Crash, corrupción, control de flujo o alteración de estado |
| Buffer overread | Se leen datos más allá del buffer | Fuga de información, crash o exposición de memoria |
| Out-of-bounds access | Acceso fuera del rango esperado | Lectura o escritura indebida según el caso |
Un stack overflow ocurre cuando la escritura fuera de límites afecta memoria ubicada en el stack. El stack contiene datos temporales de funciones, variables locales, direcciones de retorno y valores guardados.
Desde el análisis defensivo, interesa observar:
Un heap overflow ocurre cuando se escriben datos fuera de un bloque reservado dinámicamente. El heap contiene objetos y buffers cuya vida puede extenderse más allá de una función.
Los errores de heap pueden ser más difíciles de analizar porque la corrupción puede no provocar crash inmediato. El fallo visible puede aparecer mucho después, cuando el programa usa una estructura dañada.
Control de flujo es el orden en que el programa ejecuta instrucciones. Normalmente lo determinan llamadas, retornos, saltos y condiciones. Una corrupción de memoria se vuelve especialmente grave si altera un dato que influye en ese flujo.
Elementos relacionados:
No todo overflow controla flujo. Algunos solo provocan crash o alteran datos. La evaluación debe demostrar qué se controla y con qué estabilidad.
Un crash indica que el programa falló, pero no prueba por sí solo que exista explotación práctica. Para evaluar impacto se necesita entender causa, control, repetibilidad y mitigaciones.
| Observación | Pregunta defensiva | Conclusión posible |
|---|---|---|
| Acceso inválido a memoria | La dirección depende de entrada controlada | Puede ser crash sin control o indicio mayor |
| RIP/EIP alterado | El valor proviene de datos controlados | Potencial control de flujo |
| Crash no determinista | Se reproduce de forma estable | Puede requerir más análisis |
| Mitigación detiene ejecución | Qué defensa actuó | Impacto reducido o explotación más compleja |
Para que una vulnerabilidad de memoria sea explotable suelen coincidir varias condiciones. La ausencia de una condición puede reducir impacto, aunque no elimina la necesidad de corregir el fallo.
Los overflows pueden activarse por datos provenientes de muchas fuentes. El analista debe identificar la ruta completa desde entrada hasta escritura.
Muchos errores aparecen por diferencias entre longitud real, longitud esperada y representación. Un campo puede medirse en caracteres, bytes, palabras o unidades codificadas.
Riesgos típicos:
El crash analysis busca entender qué falló y por qué. Debe realizarse en entorno controlado, con símbolos cuando estén disponibles y registro cuidadoso de entradas.
Durante un crash, los registros muestran el estado inmediato del procesador. En análisis de explotación, interesa especialmente si valores controlados por entrada llegan a registros críticos.
| Registro | Qué indica | Valor para análisis |
|---|---|---|
| RIP/EIP | Instrucción actual o destino de ejecución | Evalúa control de flujo |
| RSP/ESP | Puntero al stack | Permite revisar retornos y datos cercanos |
| RBP/EBP | Base de frame, si se usa | Ayuda a reconstruir función |
| RAX/EAX | Resultados o valores temporales | Puede explicar operaciones fallidas |
| Flags | Resultado de comparaciones | Explica ramas previas al fallo |
En laboratorio, los patrones controlados permiten saber qué parte de una entrada llegó a una región de memoria o registro. Esto ayuda a medir offset y alcance de corrupción sin usar datos reales ni payloads dañinos.
Desde la defensa, la idea es responder:
Los stack canaries son valores colocados para detectar corrupción del stack antes de que una función retorne. Si el canary cambia, el programa puede abortar ejecución antes de usar una dirección de retorno dañada.
Valor defensivo:
DEP o NX impide ejecutar código en páginas marcadas como datos. Esto dificulta técnicas clásicas donde se escribía código en stack o heap y se intentaba ejecutarlo directamente.
En análisis, una excepción por ejecución en memoria no ejecutable puede indicar que una mitigación actuó. La corrección sigue siendo necesaria: DEP reduce impacto, pero no elimina la escritura fuera de límites.
ASLR aleatoriza direcciones de memoria para dificultar que una explotación dependa de ubicaciones fijas. Afecta módulos, stack, heap y otras regiones según plataforma y configuración.
Al analizar fallos, ASLR explica por qué direcciones cambian entre ejecuciones. También obliga a distinguir entre offset relativo, base de módulo y dirección virtual real.
Las tecnologías de integridad de flujo intentan validar que llamadas y saltos indirectos vayan a destinos esperados. Su objetivo es reducir abuso de punteros de función, retornos o transiciones anómalas.
Estas mitigaciones no reemplazan correcciones de código, pero agregan barreras. En un reporte, conviene indicar si el fallo fue detenido por una mitigación y qué impacto residual queda.
Los allocators modernos incluyen verificaciones para detectar corrupción de heap, metadatos inconsistentes o uso indebido de bloques. Esto puede convertir una corrupción explotable en un crash temprano.
Señales:
El mismo tipo de fallo puede tener impactos muy distintos según dónde ocurra.
| Contexto | Riesgo | Prioridad defensiva |
|---|---|---|
| Parser local de archivo | Requiere que usuario abra archivo malicioso | Alta si el formato es común o correo lo distribuye |
| Servicio expuesto | Entrada remota sin interacción | Muy alta si está accesible a internet |
| Proceso con privilegios | Puede elevar impacto del fallo | Alta por permisos y alcance |
| Componente aislado | Mitigado por sandbox o contenedor | Depende de escape o datos afectados |
La prevención comienza en diseño y código. Las mitigaciones de plataforma ayudan, pero el objetivo principal es evitar la corrupción.
Un reporte de fallo de memoria debe demostrar causa e impacto sin incluir material peligroso innecesario.
Los buffer overflows siguen siendo una base importante para comprender explotación de memoria, análisis de crashes y mitigaciones modernas. El valor defensivo está en identificar causa, condiciones, impacto y corrección con evidencia clara.
En el próximo tema estudiaremos desarrollo de exploits en laboratorio desde una mirada controlada: fuzzing, crash analysis y prueba de concepto segura para validar impacto sin afectar sistemas reales.