El usuario flag13 nos ha preparado otro binario para desafiarnos:
level13@nebula:/home/flag13$ ls -al
total 13
drwxr-x--- 2 flag13 level13 80 2011-11-20 21:22 .
drwxr-xr-x 1 root root 100 2012-08-27 07:18 ..
-rw-r--r-- 1 flag13 flag13 220 2011-05-18 02:54 .bash_logout
-rw-r--r-- 1 flag13 flag13 3353 2011-05-18 02:54 .bashrc
-rwsr-x--- 1 flag13 level13 7321 2011-11-20 21:22 flag13
-rw-r--r-- 1 flag13 flag13 675 2011-05-18 02:54 .profile
level13@nebula:/home/flag13$ file ./flag13
./flag13: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped
Un pequeño examen superficial:
level13@nebula:/home/flag13$ ./flag13
Security failure detected. UID 1014 started us, we expect 1000
The system administrators will be notified of this violation
level13@nebula:/home/flag13$ ltrace ./flag13
__libc_start_main(0x80484c4, 1, 0xbfc396c4, 0x8048600, 0x8048670 <unfinished ...>
getuid() = 1014
getuid() = 1014
printf("Security failure detected. UID %"..., 1014Security failure detected. UID 1014 started us, we expect 1000
) = 63
puts("The system administrators will b"...The system administrators will be notified of this violation
) = 61
exit(1 <unfinished ...>
+++ exited (status 1) +++
Obviamente no pretendemos ni podemos cambiar nuestro real user ID, pero sí podemos utilizar un debugger como
gdb
para modificar el valor devuelto por getuid()
y ver como se comporta ./flag11
:
level13@nebula:/home/flag13$ gdb -q ./flag13 -ex 'set disassembly-flavor intel'
Reading symbols from /home/flag13/flag13...(no debugging symbols found)...done.
(gdb) disass main
Dump of assembler code for function main:
0x080484c4 <+0>: push ebp
0x080484c5 <+1>: mov ebp,esp
0x080484c7 <+3>: push edi
0x080484c8 <+4>: push ebx
0x080484c9 <+5>: and esp,0xfffffff0
0x080484cc <+8>: sub esp,0x130
0x080484d2 <+14>: mov eax,DWORD PTR [ebp+0xc]
0x080484d5 <+17>: mov DWORD PTR [esp+0x1c],eax
0x080484d9 <+21>: mov eax,DWORD PTR [ebp+0x10]
0x080484dc <+24>: mov DWORD PTR [esp+0x18],eax
0x080484e0 <+28>: mov eax,gs:0x14
0x080484e6 <+34>: mov DWORD PTR [esp+0x12c],eax
0x080484ed <+41>: xor eax,eax
0x080484ef <+43>: call 0x80483c0 <getuid@plt>
0x080484f4 <+48>: cmp eax,0x3e8
0x080484f9 <+53>: je 0x8048531 <main+109>
0x080484fb <+55>: call 0x80483c0 <getuid@plt>
0x08048500 <+60>: mov edx,0x80486d0
0x08048505 <+65>: mov DWORD PTR [esp+0x8],0x3e8
0x0804850d <+73>: mov DWORD PTR [esp+0x4],eax
0x08048511 <+77>: mov DWORD PTR [esp],edx
0x08048514 <+80>: call 0x80483a0 <printf@plt>
0x08048519 <+85>: mov DWORD PTR [esp],0x804870c
Pondremos un breakpoint en
main+48
, que es la instrucción donde se compara el valor devuelto por getuid()
con 0x3e8 (1000):
(gdb) break *main+48
Breakpoint 1 at 0x80484f4
(gdb) run
Starting program: /home/flag13/flag13
Breakpoint 1, 0x080484f4 in main ()
Una vez el programa se detiene, modificamos el valor del registro
EAX
antes de que se efectúe la comparación:
(gdb) i r eax
eax 0x3f6 1014
(gdb) set $eax=0x3e8
(gdb) i r eax
eax 0x3e8 1000
(gdb) c
Continuing.
your token is b705702b-76a8-42b0-8844-3adabbe5ac58
[Inferior 1 (process 4403) exited with code 063]
Lo utilizamos como password para flag13:
level13@nebula:/home/flag13$ su flag13
Password:
sh-4.2$ getflag
You have successfully executed getflag on a target account
Pwned!
Lo que sigue es una sesión de reversing con radare2 más detallada:
Lo que sigue es una sesión de reversing con radare2 más detallada:
dpc@kernelinside:~$ r2 ./flag13
-- This is an unregistered copy.
[0x08048410]> 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)
[0x08048410]> iz
000 0x000006d0 0x080486d0 59 60 (.rodata) ascii Security failure detected. UID %d started us, we expect %d\n
001 0x0000070c 0x0804870c 60 61 (.rodata) ascii The system administrators will be notified of this violation
002 0x0000074c 0x0804874c 36 37 (.rodata) ascii 8mjomjh8wml;bwnh8jwbbnnwi;>;88?o;9ob
003 0x00000771 0x08048771 17 18 (.rodata) ascii your token is %s\n
El string
8mjomjh8wml;bwnh8jwbbnnwi;>;88?o;9ob
es sospechoso de ser el token cifrado, curiosamente tiene la misma longitud que aquellos que obtuvimos de flag04 y flag10. El comando afl
no arroja más funciones locales que main()
, veamos si es allí dónde se referencia el token:
[0x08048410]> axt 0x0804874c
sym.main 0x8048547 [DATA] mov edx, str.8mjomjh8wml_bwnh8jwbbnnwi___88_o_9ob
Correcto. Procedemos a hacer reversing con
main()
:
[0x080484c4]> pdf
| ;-- main:
/ (fcn) sym.main 304
| sym.main (int arg_ch, int arg_10h);
| ; var int local_8h @ ebp-0x8
| ; arg int arg_ch @ ebp+0xc
| ; arg int arg_10h @ ebp+0x10
| ; var uid_t local_4h @ esp+0x4
| ; var int local_8h_2 @ esp+0x8
| ; var int local_18h @ esp+0x18
| ; var int local_1ch @ esp+0x1c
| ; var int local_28h @ esp+0x28
| ; var int local_2ch @ esp+0x2c
| ; var int local_12ch @ esp+0x12c
| ; DATA XREF from entry0 (0x8048427)
| 0x080484c4 push ebp
| 0x080484c5 mov ebp, esp
| 0x080484c7 push edi
| 0x080484c8 push ebx
| 0x080484c9 and esp, 0xfffffff0
| 0x080484cc sub esp, 0x130
| 0x080484d2 mov eax, dword [arg_ch] ; [0xc:4]=-1 ; 12
| 0x080484d5 mov dword [local_1ch], eax
| 0x080484d9 mov eax, dword [arg_10h] ; [0x10:4]=-1 ; 16
| 0x080484dc mov dword [local_18h], eax
| 0x080484e0 mov eax, dword gs:[0x14] ; [0x14:4]=-1 ; 20
| 0x080484e6 mov dword [local_12ch], eax
| 0x080484ed xor eax, eax
| 0x080484ef call sym.imp.getuid ; uid_t getuid(void)
| 0x080484f4 cmp eax, 0x3e8 ; 1000
Como ya hemos visto, se llama a
getuid()
y se compara con el valor 0x3e8(1000), en caso de error se imprime la violación de acceso y ./flag13
invoca a exit()
. En caso contrario prosigue con el siguiente fragmento de código:
| 0x08048531 lea eax, [local_2ch] ; 0x2c ; ',' ; 44
| 0x08048535 mov ebx, eax
| 0x08048537 mov eax, 0
| 0x0804853c mov edx, 0x40 ; '@' ; 64
| 0x08048541 mov edi, ebx
| 0x08048543 mov ecx, edx
| 0x08048545 rep stosd dword es:[edi], eax
Este patrón es reconocible al instante y representa la función:
memset(buffer, 0, 0x40)
Cambiemos el nombre
local_2ch
por algo más descriptivo:
[0x080484c4]> afvn local_2ch buffer
Continuamos:
| 0x08048547 mov edx, str.8mjomjh8wml_bwnh8jwbbnnwi___88_o_9ob ; 0x804874c ; "8mjomjh8wml;bwnh8jwbbnnwi;>;88?o;9ob"
| 0x0804854c lea eax, [buffer] ; 0x2c ; ',' ; 44
| 0x08048550 mov ecx, dword [edx]
| 0x08048552 mov dword [eax], ecx
| 0x08048554 mov ecx, dword [edx + 4] ; [0x4:4]=-1 ; 4
| 0x08048557 mov dword [eax + 4], ecx
| 0x0804855a mov ecx, dword [edx + 8] ; [0x8:4]=-1 ; 8
| 0x0804855d mov dword [eax + 8], ecx
| 0x08048560 mov ecx, dword [edx + 0xc] ; [0xc:4]=-1 ; 12
| 0x08048563 mov dword [eax + 0xc], ecx
| 0x08048566 mov ecx, dword [edx + 0x10] ; [0x10:4]=-1 ; 16
| 0x08048569 mov dword [eax + 0x10], ecx
| 0x0804856c mov ecx, dword [edx + 0x14] ; [0x14:4]=-1 ; 20
| 0x0804856f mov dword [eax + 0x14], ecx
| 0x08048572 mov ecx, dword [edx + 0x18] ; [0x18:4]=-1 ; 24
| 0x08048575 mov dword [eax + 0x18], ecx
| 0x08048578 mov ecx, dword [edx + 0x1c] ; [0x1c:4]=-1 ; 28
| 0x0804857b mov dword [eax + 0x1c], ecx
| 0x0804857e mov ecx, dword [edx + 0x20] ; [0x20:4]=-1 ; 32
| 0x08048581 mov dword [eax + 0x20], ecx
| 0x08048584 movzx edx, byte [edx + 0x24] ; [0x24:1]=255 ; '$' ; 36
| 0x08048588 mov byte [eax + 0x24], dl
Este código es muy simple, realiza una copia exacta en bloques de 4 bytes del string
8mjomjh8wml;bwnh8jwbbnnwi;>;88?o;9ob
en buffer
. A continuación un bucle importante:
| 0x0804858b mov dword [local_28h], 0
| ,=< 0x08048593 jmp 0x80485b4
| | ; CODE XREF from sym.main (0x80485c1)
| .--> 0x08048595 lea eax, [buffer] ; 0x2c ; ',' ; 44
| :| 0x08048599 add eax, dword [local_28h]
| :| 0x0804859d movzx eax, byte [eax]
| :| 0x080485a0 mov edx, eax
| :| 0x080485a2 xor edx, 0x5a
| :| 0x080485a5 lea eax, [buffer] ; 0x2c ; ',' ; 44
| :| 0x080485a9 add eax, dword [local_28h]
| :| 0x080485ad mov byte [eax], dl
| :| 0x080485af add dword [local_28h], 1
| :| ; CODE XREF from sym.main (0x8048593)
| :`-> 0x080485b4 lea eax, [buffer] ; 0x2c ; ',' ; 44
| : 0x080485b8 add eax, dword [local_28h]
| : 0x080485bc movzx eax, byte [eax]
| : 0x080485bf test al, al
| `==< 0x080485c1 jne 0x8048595
Ya hemos visto algo similar en
./flag11
, buffer
(que ahora contiene el token cifrado), se recorre byte por byte y se cifra con la clave 0x5a ('Z') mediante una operación XOR, almacenando el resultado en el mismo buffer
. Seguidamente este se imprime por pantalla:
| 0x080485c3 mov eax, str.your_token_is__s ; 0x8048771 ; "your token is %s\n"
| 0x080485c8 lea edx, [buffer] ; 0x2c ; ',' ; 44
| 0x080485cc mov dword [local_4h], edx
| 0x080485d0 mov dword [esp], eax ; const char *format
| 0x080485d3 call sym.imp.printf ; int printf(const char *format)
| 0x080485d8 mov edx, dword [local_12ch] ; [0x12c:4]=-1 ; 300
| 0x080485df xor edx, dword gs:[0x14]
| ,=< 0x080485e6 je 0x80485ed
| | 0x080485e8 call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
| | ; CODE XREF from sym.main (0x80485e6)
| `-> 0x080485ed lea esp, [local_8h]
| 0x080485f0 pop ebx
| 0x080485f1 pop edi
| 0x080485f2 pop ebp
\ 0x080485f3 ret
Podemos hacer un script para descifrar el token, o ya que tratamos con caracteres ASCII utilizar una web que haga el trabajo por nosotros:
Pwned!
No hay comentarios:
Publicar un comentario