Pages

sábado, 21 de julio de 2018

Nebula CTF - level14

Ya queda menos para completar todos los retos de la máquina Nebula y los tokens vuelven a la carga:

level14@nebula:/home/flag14$ ls -al
total 17
drwxr-x--- 1 flag14  level14   60 2018-07-21 10:35 .
drwxr-xr-x 1 root    root     180 2012-08-27 07:18 ..
-rw------- 1 flag14  flag14    13 2018-07-21 10:35 .bash_history
-rw-r--r-- 1 flag14  flag14   220 2011-05-18 02:54 .bash_logout
-rw-r--r-- 1 flag14  flag14  3353 2011-05-18 02:54 .bashrc
-rwsr-x--- 1 flag14  level14 7272 2011-12-05 18:59 flag14
-rw-r--r-- 1 flag14  flag14   675 2011-05-18 02:54 .profile
-rw------- 1 level14 level14   37 2011-12-05 18:59 token

What!? En este caso tenemos permisos de lectura:

level14@nebula:/home/flag14$ cat token
857:g67?5ABBo:BtDA?tIvLDKL{MQPSRQWW.

Otro reto de criptografía. Veamos cuál es la función del binario:

level14@nebula:/home/flag14$ ./flag14
./flag14
 -e Encrypt input
level14@nebula:/home/flag14$ ./flag14 -e testtest
AAAAAAAA
ABCDEFGH^C

Si ./flag14 utiliza un algoritmo de cifrado, podemos utilizar radare2 para hacer reversing sobre el mismo y crear el algoritmo inverso:

dpc@kernelinside:~$ r2 ./flag14
 -- Everything up-to-date.
[0x080483b0]> 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)
[0x080483b0]> iz
000 0x00000663 0x08048663  21  22 (.rodata) ascii %s\n\t-e\tEncrypt input\n

[0x080483b0]> afl
0x08048310    3 46           sym._init
0x08048350    1 6            sym.imp.read
0x08048360    1 6            sym.imp.printf
0x08048370    1 6            loc.imp.__gmon_start
0x08048380    1 6            sym.imp.exit
0x08048390    1 6            sym.imp.__libc_start_main
0x080483a0    1 6            sym.imp.write
0x080483b0    1 33           entry0
0x080483e0    6 85           sym.__do_global_dtors_aux
0x08048440    4 35           sym.frame_dummy
0x08048464   11 293          main
0x08048590    4 97           sym.__libc_csu_init
0x08048600    1 2            sym.__libc_csu_fini
0x08048602    1 4            sym.__i686.get_pc_thunk.bx
0x08048610    4 42           sym.__do_global_ctors_aux
0x0804863c    1 26           sym._fini

Hasta aquí nada importante salvo main():

[0x080483b0]> s sym.main
[0x08048464]> pdf
/ (fcn) main 293
|   main (signed int arg_8h, int arg_ch);
|           ; arg signed int arg_8h @ ebp+0x8
|           ; arg int arg_ch @ ebp+0xc
|           ; var void *ptr @ esp+0x4
|           ; var size_t nbytes @ esp+0x8
|           ; var int local_1ch @ esp+0x1c
|           ; var int local_2ch @ esp+0x2c
|           ; var int local_30h @ esp+0x30
|           ; var ssize_t local_34h @ esp+0x34
|           ; var ssize_t local_38h @ esp+0x38
|           ; var int local_3ch @ esp+0x3c
|           ; var int local_7ch @ esp+0x7c
|           ; DATA XREF from entry0 (0x80483c7)
|           0x08048464      push ebp
|           0x08048465      mov ebp, esp
|           0x08048467      push edi
|           0x08048468      push esi
|           0x08048469      and esp, 0xfffffff0
|           0x0804846c      add esp, 0xffffffffffffff80
|           0x0804846f      mov eax, dword [arg_ch]                    ; [0xc:4]=-1 ; 12
|           0x08048472      mov dword [local_1ch], eax
|           0x08048476      mov eax, dword gs:[0x14]                   ; [0x14:4]=-1 ; 20
|           0x0804847c      mov dword [local_7ch], eax
|           0x08048480      xor eax, eax
|           0x08048482      mov dword [local_2ch], 0
|           0x0804848a      cmp dword [arg_8h], 1                      ; [0x1:4]=-1 ; 1

Se compara el número de argumentos pasados a ./flag14, si es menor o igual a 1 imprime el modo de uso y sale. Anotamos aquí que la variable local_2ch se inicia a 0 (la llamaremos x). Continuamos

|       ,=< 0x0804848e      jle 0x80484be
|       |   0x08048490      mov eax, dword [local_1ch]                 ; [0x1c:4]=-1 ; 28
|       |   0x08048494      add eax, 4
|       |   0x08048497      mov eax, dword [eax]
|       |   0x08048499      mov edx, eax
|       |   0x0804849b      mov eax, 0x8048660                         ; "-e"
|       |   0x080484a0      mov ecx, 3
|       |   0x080484a5      mov esi, edx
|       |   0x080484a7      mov edi, eax
|       |   0x080484a9      repe cmpsb byte [esi], byte ptr es:[edi]   ; [0x170000001c:1]=255 ; 98784247836
|       |   0x080484ab      seta dl
|       |   0x080484ae      setb al
|       |   0x080484b1      mov ecx, edx
|       |   0x080484b3      sub cl, al
|       |   0x080484b5      mov eax, ecx
|       |   0x080484b7      movsx eax, al
|       |   0x080484ba      test eax, eax

Una simple comprobación de argumentos que se traduce como:

strcmp(argv[1], "-e\0", 3);

Anotamos local_1ch = argv[]. Si el resultado es verdadero llegamos aquí:

|      `--> 0x080484e2      mov dword [nbytes], 0x40                   ; '@' ; [0x40:4]=-1 ; 64 ; size_t nbyte
|       :   0x080484ea      lea eax, [local_3ch]                       ; 0x3c ; '<' ; 60
|       :   0x080484ee      mov dword [ptr], eax                       ; void *buf
|       :   0x080484f2      mov dword [esp], 0                         ; int fildes
|       :   0x080484f9      call sym.imp.read                          ; ssize_t read(int fildes, void *buf, size_t nbyte)
|       :   0x080484fe      mov dword [local_34h], eax
|       :   0x08048502      cmp dword [local_34h], 0

Se leen hasta 64 bytes desde stdin y se almacenan en local_3ch (la renombraremos por buffer). El resultado de read() se almacena en local_34h (la cambiaremos por readed_bytes). Si es 0 se llama a exit() directamente. Ahora procederemos a renombrar las variables anteriores para facilitar la lectura del siguiente fragmento de código

[0x08048464]> afvn local_3ch buffer
[0x08048464]> afvn local_34h readed_bytes
[0x08048464]> afvn local_2ch x
[0x08048464]> afvn local_30h i
|      `--> 0x08048515      mov dword [i], 0
|      ,==< 0x0804851d      jmp 0x8048548
|      |:   ; CODE XREF from main (0x8048550)
|     .---> 0x0804851f      lea eax, [buffer]                          ; 0x3c ; '<' ; 60
|     :|:   0x08048523      add eax, dword [i]
|     :|:   0x08048527      movzx eax, byte [eax]
|     :|:   0x0804852a      mov edx, eax
|     :|:   0x0804852c      mov eax, dword [x]                         ; [0x2c:4]=-1 ; ',' ; 44
|     :|:   0x08048530      add eax, edx
|     :|:   0x08048532      mov edx, eax
|     :|:   0x08048534      lea eax, [buffer]                          ; 0x3c ; '<' ; 60
|     :|:   0x08048538      add eax, dword [i]
|     :|:   0x0804853c      mov byte [eax], dl
|     :|:   0x0804853e      add dword [x], 1
|     :|:   0x08048543      add dword [i], 1
|     :|:   ; CODE XREF from main (0x804851d)
|     :`--> 0x08048548      mov eax, dword [i]                         ; [0x30:4]=-1 ; '0' ; 48
|     : :   0x0804854c      cmp eax, dword [readed_bytes]              ; [0x34:4]=-1 ; '4' ; 52
|     `===< 0x08048550      jl 0x804851f

Con las variables renombradas es muy fácil traducir a pseudocódigo:

x = 0
for (i = 0; i < strlen(buffer); i++)
{
    buffer[i] += x;
    x += 1;
}

Ya solo queda imprimir el resultado por pantalla y salir:

|       :   0x08048552      mov eax, dword [readed_bytes]              ; [0x34:4]=-1 ; '4' ; 52
|       :   0x08048556      mov dword [nbytes], eax                    ; size_t nbytes
|       :   0x0804855a      lea eax, [buffer]                          ; 0x3c ; '<' ; 60
|       :   0x0804855e      mov dword [ptr], eax                       ; void *ptr
|       :   0x08048562      mov dword [esp], 1                         ; int fd
|       :   0x08048569      call sym.imp.write                         ; ssize_t write(int fd, void *ptr, size_t nbytes)
|       :   0x0804856e      mov dword [local_38h], eax
|       :   0x08048572      cmp dword [local_38h], 0
|       `=< 0x08048577      jg 0x80484e1
|           0x0804857d      mov dword [esp], 0                         ; int status
\           0x08048584      call sym.imp.exit                          ; void exit(int status)

Con esta información en las manos, creamos un pequeño script en Python con el algoritmo inverso:

import sys

def decode(encoded):
        x = 0
        dec = ""
        for i in range(len(encoded)):
                dec += chr(ord(encoded[i]) - x)
                x += 1

        return dec

if __name__ == "__main__":
        if (len(sys.argv) > 1):
                print(decode(sys.argv[1]))
        else:
                print(sys.argv[0] + " str_encoded");

Probamos con nuestro token:

level14@nebula:/home/flag14$ python /tmp/l14_exploit.py `cat token`
8457c118-887c-4e40-a5a6-33a25353165

Y para terminar vamos a por nuestro flag:

level14@nebula:/home/flag14$ su flag14
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...