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