Supporter - Nibble anderson_leite Posted July 12, 2021 at 04:23 AM Supporter - Nibble Share Posted July 12, 2021 at 04:23 AM (edited) Olá, decidi começar a publicar alguns desafios de engenharia reversa, criptografia e análise de malware nessa seção do forum! Para iniciar, eis um crackme para Linux chamado "Call me" onde você só precisa encontrar a chave correta e receber a sua mensagem de resolução ? Algumas considerações: Coloque sua resolução com a tag de spoiler, para não mostrar para todos como resolveu Resoluções por brute-force no input são validas, porém é sempre mais elegante entender o que está acontecendo por trás Dica: Caso sua "chave" esteja errada, você recebera um segmentation fault Algum tipo de proteção ? Não O Binário está stripped ? Sim Qual o formato ? ELF Algumas ferramentas open source que podem te ajudar a resolver são: Cutter Radare gdb Boa sorte ? callme Edited July 12, 2021 at 04:45 AM by anderson_leite Quote Link to comment Share on other sites More sharing options...
Supporter - Byte paulosgf Posted December 27, 2021 at 12:05 AM Supporter - Byte Share Posted December 27, 2021 at 12:05 AM Oi anderson_leite! Cara, crackme difícil esse, heim!? O fato de não ter símbolos já dificulta bastante. Eu consegui superar essa fase, mas não estou entendendo a lógica dele. Mas vamos ver até onde cheguei: O binário não tem símbolos. paulo@nirvana:~/crack/crackmes$ nm ./callme nm: ./callme: nenhum símbolo Iniciei sem argumentos para ver o que ele retorna e depois tentei uma string aleatória como entrada. paulo@nirvana:~/crack/crackmes$ ./callme Usage: ./callme key paulo@nirvana:~/crack/crackmes$ ./callme foo Try harder paulo@nirvana:~/crack/crackmes$ Agora no debugger. Uso a extensão GEF no GDB, que facilita bastante o trabalho, retornando o estado dos registradores, Stack, linhas de código executadas e à executar em a cada instrução. Recomento! paulo@nirvana:~/crack/crackmes$ gdb --args ./callme foo GEF for linux ready, type `gef' to start, `gef config' to configure 96 commands loaded for GDB 9.2 using Python engine 3.8 [+] Configuration from '/home/paulo/.gef.rc' restored [+] 32 extra commands added from '~/crack/GDB/gef-scripts/scripts' Voltron loaded. Reading symbols from ./callme... (No debugging symbols found in ./callme) gef➤ break main Function "main" not defined. gef➤ info file Symbols from "/home/paulo/crack/crackmes/callme". Local exec file: `/home/paulo/crack/crackmes/callme', file type elf64-x86-64. Entry point: 0x10a0 0x00000000000002a8 - 0x00000000000002c4 is .interp 0x00000000000002c4 - 0x00000000000002e4 is .note.ABI-tag 0x00000000000002e8 - 0x0000000000000310 is .gnu.hash 0x0000000000000310 - 0x0000000000000448 is .dynsym 0x0000000000000448 - 0x00000000000004f6 is .dynstr 0x00000000000004f6 - 0x0000000000000510 is .gnu.version 0x0000000000000510 - 0x0000000000000530 is .gnu.version_r 0x0000000000000530 - 0x0000000000000698 is .rela.dyn 0x0000000000000698 - 0x0000000000000728 is .rela.plt 0x0000000000001000 - 0x0000000000001017 is .init 0x0000000000001020 - 0x0000000000001090 is .plt 0x0000000000001090 - 0x0000000000001098 is .plt.got 0x00000000000010a0 - 0x0000000000001441 is .text 0x0000000000001444 - 0x000000000000144d is .fini 0x0000000000002000 - 0x0000000000002080 is .rodata 0x0000000000002080 - 0x00000000000020cc is .eh_frame_hdr 0x00000000000020d0 - 0x0000000000002218 is .eh_frame 0x0000000000003de8 - 0x0000000000003df0 is .init_array 0x0000000000003df0 - 0x0000000000003df8 is .fini_array 0x0000000000003df8 - 0x0000000000003fd8 is .dynamic 0x0000000000003fd8 - 0x0000000000004000 is .got 0x0000000000004000 - 0x0000000000004048 is .got.plt 0x0000000000004060 - 0x0000000000004118 is .data 0x0000000000004120 - 0x0000000000004130 is .bss gef➤ break *0x10a0 Ponto de parada 1 at 0x10a0 gef➤ run Starting program: /home/paulo/crack/crackmes/callme foo Warning: Cannot insert breakpoint 1. Não é possível acessar a memória no endereço 0x10a0 gef➤ start [*] gdb is already running gef➤ Como o programa não resolve símbolos, não acha a função main() e nem mesmo o entrypoint. Nestes casos é preciso um recurso novo do GDB 7.x. Spoiler paulo@nirvana:~/crack/crackmes$ gdb --args ./callme foo GEF for linux ready, type `gef' to start, `gef config' to configure 96 commands loaded for GDB 9.2 using Python engine 3.8 [+] Configuration from '/home/paulo/.gef.rc' restored [+] 32 extra commands added from '~/crack/GDB/gef-scripts/scripts' Voltron loaded. Reading symbols from ./callme... (No debugging symbols found in ./callme) gef➤ starti Starting program: /home/paulo/crack/crackmes/callme foo Program stopped. 0x00007ffff7fd0100 in ?? () from /lib64/ld-linux-x86-64.so.2 ... gef➤ info file Symbols from "/home/paulo/crack/crackmes/callme". Native process: Using the running image of child process 137717. While running this, GDB does not access memory from... Local exec file: `/home/paulo/crack/crackmes/callme', file type elf64-x86-64. Entry point: 0x5555555550a0 0x00005555555542a8 - 0x00005555555542c4 is .interp ... gef➤ break *0x5555555550a0 Ponto de parada 1 at 0x5555555550a0 gef➤ continue Continuing. Breakpoint 1, 0x00005555555550a0 in ?? () ... 0x55555555509a add BYTE PTR [rax], al 0x55555555509c add BYTE PTR [rax], al 0x55555555509e add BYTE PTR [rax], al ●→ 0x5555555550a0 xor ebp, ebp 0x5555555550a2 mov r9, rdx 0x5555555550a5 pop rsi 0x5555555550a6 mov rdx, rsp 0x5555555550a9 and rsp, 0xfffffffffffffff0 0x5555555550ad push rax ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "callme", stopped 0x5555555550a0 in ?? (), reason: BREAKPOINT ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x5555555550a0 → xor ebp, ebp ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── gef➤ O comando starti inicia o GDB resolvendo os simbolos da Glibc. Em seguida é preciso achar o entrypoint com o comando info file em seguida colocar um breakpoint em seu endereço e continuar a execução do programa. gef➤ x /50i $rip => 0x5555555550a0: xor ebp,ebp 0x5555555550a2: mov r9,rdx 0x5555555550a5: pop rsi 0x5555555550a6: mov rdx,rsp 0x5555555550a9: and rsp,0xfffffffffffffff0 0x5555555550ad: push rax 0x5555555550ae: push rsp 0x5555555550af: lea r8,[rip+0x38a] # 0x555555555440 0x5555555550b6: lea rcx,[rip+0x323] # 0x5555555553e0 0x5555555550bd: lea rdi,[rip+0x213] # 0x5555555552d7 0x5555555550c4: call QWORD PTR [rip+0x2f16] # 0x555555557fe0 O comando x /50i $rip retorna as próximas 50 instruções a partir do ponteiro de instrução. Percebe-se uma Call a uma função. Colocamos um breakpoint e seguimos até lá. gef➤ break *0x5555555550c4 Ponto de parada 2 at 0x5555555550c4 gef➤ continue Continuing. Breakpoint 2, 0x00005555555550c4 in ?? () ... ●→ 0x5555555550c4 call QWORD PTR [rip+0x2f16] # 0x555555557fe0 0x5555555550ca hlt 0x5555555550cb nop DWORD PTR [rax+rax*1+0x0] 0x5555555550d0 lea rdi, [rip+0x3041] # 0x555555558118 0x5555555550d7 lea rax, [rip+0x303a] # 0x555555558118 0x5555555550de cmp rax, rdi ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── arguments (guessed) ──── *0x555555557fe0 ( $rdi = 0x00005555555552d7 → push rbp, $rsi = 0x0000000000000002, $rdx = 0x00007fffffffdee8 → 0x00007fffffffe238 → "/home/paulo/crack/crackmes/callme", $rcx = 0x00005555555553e0 → push r15, $r8 = 0x0000555555555440 → ret ) ... gef➤ stepi __libc_start_main (main=0x5555555552d7, argc=0x2, argv=0x7fffffffdee8, init=0x5555555553e0, fini=0x555555555440, rtld_fini=0x7ffff7fe0d50, stack_end=0x7fffffffded8) at ../csu/libc-start.c:137 137 ../csu/libc-start.c: Arquivo ou diretório inexistente. O GEF já resolve a chamada à função com seus argumentos. Entramos nela, que se revela a __libc_start_main (). Esta é a função nos binários ELF que por fim chama a main(). Vide seu 1º argumento. Então colocamos um breakpoint no endereço da main() e executamos até entrar nela. gef➤ break *0x5555555552d7 Ponto de parada 3 at 0x5555555552d7 gef➤ continue Continuing. Breakpoint 3, 0x00005555555552d7 in ?? () ... 0x5555555552d1 mov rax, QWORD PTR [rbp-0x8] 0x5555555552d5 leave 0x5555555552d6 ret ●→ 0x5555555552d7 push rbp 0x5555555552d8 mov rbp, rsp 0x5555555552db sub rsp, 0x20 0x5555555552df mov DWORD PTR [rbp-0x14], edi 0x5555555552e2 mov QWORD PTR [rbp-0x20], rsi 0x5555555552e6 cmp DWORD PTR [rbp-0x14], 0x1 A partir daqui já é possível listar todas as instruções da main() com o comando executado anteriormente a partir o ponteiro de instrução. gef➤ x /70i $rip => 0x5555555552d7: push rbp 0x5555555552d8: mov rbp,rsp 0x5555555552db: sub rsp,0x20 0x5555555552df: mov DWORD PTR [rbp-0x14],edi 0x5555555552e2: mov QWORD PTR [rbp-0x20],rsi 0x5555555552e6: cmp DWORD PTR [rbp-0x14],0x1 0x5555555552ea: jg 0x555555555318 0x5555555552ec: mov rax,QWORD PTR [rbp-0x20] 0x5555555552f0: mov rdx,QWORD PTR [rax] 0x5555555552f3: mov rax,QWORD PTR [rip+0x2e26] # 0x555555558120 <stderr> 0x5555555552fa: lea rsi,[rip+0xd64] # 0x555555556065 0x555555555301: mov rdi,rax 0x555555555304: mov eax,0x0 0x555555555309: call 0x555555555060 <fprintf@plt> 0x55555555530e: mov eax,0x1 0x555555555313: jmp 0x5555555553cf 0x555555555318: mov rax,QWORD PTR [rbp-0x20] 0x55555555531c: add rax,0x8 0x555555555320: mov rax,QWORD PTR [rax] 0x555555555323: mov rdi,rax 0x555555555326: call 0x5555555551d1 0x55555555532b: mov QWORD PTR [rbp-0x10],rax 0x55555555532f: cmp QWORD PTR [rbp-0x10],0x0 0x555555555334: jne 0x55555555535d 0x555555555336: mov rax,QWORD PTR [rip+0x2de3] # 0x555555558120 <stderr> 0x55555555533d: mov rcx,rax 0x555555555340: mov edx,0xb 0x555555555345: mov esi,0x1 0x55555555534a: lea rdi,[rip+0xd23] # 0x555555556074 0x555555555351: call 0x555555555080 <fwrite@plt> 0x555555555356: mov eax,0x1 0x55555555535b: jmp 0x5555555553cf 0x55555555535d: mov rax,QWORD PTR [rbp-0x10] 0x555555555361: mov eax,DWORD PTR [rax+0x8] 0x555555555364: cdqe 0x555555555366: lea rdx,[rax*8+0x0] 0x55555555536e: lea rax,[rip+0x2d6b] # 0x5555555580e0 0x555555555375: mov rax,QWORD PTR [rdx+rax*1] 0x555555555379: mov QWORD PTR [rbp-0x8],rax 0x55555555537d: mov rax,QWORD PTR [rbp-0x10] 0x555555555381: mov eax,DWORD PTR [rax+0x4] 0x555555555384: cdqe 0x555555555386: lea rdx,[rax*4+0x0] 0x55555555538e: lea rax,[rip+0x2cdb] # 0x555555558070 0x555555555395: mov edx,DWORD PTR [rdx+rax*1] 0x555555555398: mov rax,QWORD PTR [rbp-0x10] 0x55555555539c: mov eax,DWORD PTR [rax] 0x55555555539e: cdqe 0x5555555553a0: lea rcx,[rax*8+0x0] 0x5555555553a8: lea rax,[rip+0x2cf1] # 0x5555555580a0 0x5555555553af: mov rax,QWORD PTR [rcx+rax*1] 0x5555555553b3: mov rcx,QWORD PTR [rbp-0x8] 0x5555555553b7: mov esi,edx 0x5555555553b9: mov rdi,rax 0x5555555553bc: call rcx 0x5555555553be: mov rax,QWORD PTR [rbp-0x10] 0x5555555553c2: mov rdi,rax 0x5555555553c5: call 0x555555555030 <free@plt> 0x5555555553ca: mov eax,0x0 0x5555555553cf: leave 0x5555555553d0: ret 1 Quote Link to comment Share on other sites More sharing options...
Supporter - Byte paulosgf Posted December 27, 2021 at 12:37 AM Supporter - Byte Share Posted December 27, 2021 at 12:37 AM (edited) Agora a solução do crackme em si, começando pelas suas verificações. Spoiler Há uma chamada em 0x555555555326: call 0x5555555551d1 Basicamente o que ela faz é verificar o tamanho do argumento do programa, e se ele é apenas números. 0x5555555551d9: mov QWORD PTR [rbp-0x18],rdi 0x5555555551dd: mov rax,QWORD PTR [rbp-0x18] 0x5555555551e1: mov rdi,rax 0x5555555551e4: call 0x555555555050 <strlen@plt> 0x5555555551e9: mov edx,DWORD PTR [rip+0x2e99] # 0x555555558088 0x5555555551ef: movsxd rdx,edx 0x5555555551f2: lea rcx,[rdx*4+0x0] 0x5555555551fa: lea rdx,[rip+0x2e87] # 0x555555558088 0x555555555201: mov edx,DWORD PTR [rcx+rdx*1] 0x555555555204: movsxd rdx,edx 0x555555555207: cmp rax,rdx 0x55555555520a: je 0x555555555216 0x55555555520c: mov eax,0x0 0x555555555211: jmp 0x5555555552d5 0x555555555216: mov rax,QWORD PTR [rbp-0x18] 0x55555555521a: movzx eax,BYTE PTR [rax] 0x55555555521d: cmp al,0x2f 0x55555555521f: jle 0x55555555522c 0x555555555221: mov rax,QWORD PTR [rbp-0x18] 0x555555555225: movzx eax,BYTE PTR [rax] 0x555555555228: cmp al,0x39 0x55555555522a: jle 0x555555555236 0x55555555522c: mov eax,0x0 0x555555555231: jmp 0x5555555552d5 0x555555555236: mov rax,QWORD PTR [rbp-0x18] 0x55555555523a: add rax,0x1 0x55555555523e: movzx eax,BYTE PTR [rax] 0x555555555241: cmp al,0x2f 0x555555555243: jle 0x555555555254 0x555555555245: mov rax,QWORD PTR [rbp-0x18] 0x555555555249: add rax,0x1 0x55555555524d: movzx eax,BYTE PTR [rax] 0x555555555250: cmp al,0x39 0x555555555252: jle 0x55555555525b 0x555555555254: mov eax,0x0 0x555555555259: jmp 0x5555555552d5 0x55555555525b: mov rax,QWORD PTR [rbp-0x18] 0x55555555525f: add rax,0x2 0x555555555263: movzx eax,BYTE PTR [rax] 0x555555555266: cmp al,0x2f 0x555555555268: jle 0x555555555279 0x55555555526a: mov rax,QWORD PTR [rbp-0x18] 0x55555555526e: add rax,0x2 0x555555555272: movzx eax,BYTE PTR [rax] 0x555555555275: cmp al,0x39 0x555555555277: jle 0x555555555280 depois da chamada à strlen(), há uma comparação em 0x555555555207: cmp rax,rdx e se o tamanho for diferente de 3 o programa sairá com a mensagem de erro Citar Try harder Para cada dígito é verificado se o mesmo se encontra entre os caracteres ASCII 0x2f == '/' e 0x39 == 9, indicando se tratar de número ou não. Caso contrário o programa também sai com a mesma mensagem de erro acima. Edited December 27, 2021 at 12:40 AM by paulosgf 1 Quote Link to comment Share on other sites More sharing options...
Supporter - Byte paulosgf Posted December 27, 2021 at 02:21 AM Supporter - Byte Share Posted December 27, 2021 at 02:21 AM Quando inciamos com um número de 3 dígitos e executamos o binário até retornar desta última chamada, ele volta para a main() e segue para o algorítimo principal. Spoiler paulo@nirvana:~/crack/crackmes$ gdb --args ./callme 985 GEF for linux ready, type `gef' to start, `gef config' to configure 96 commands loaded for GDB 9.2 using Python engine 3.8 [+] Configuration from '/home/paulo/.gef.rc' restored [+] 32 extra commands added from '~/crack/GDB/gef-scripts/scripts' Voltron loaded. Reading symbols from ./callme... (No debugging symbols found in ./callme) gef➤ starti Starting program: /home/paulo/crack/crackmes/callme 985 Program stopped. 0x00007ffff7fd0100 in ?? () from /lib64/ld-linux-x86-64.so.2 ... 0x555555555326: call 0x5555555551d1 0x55555555532b: mov QWORD PTR [rbp-0x10],rax 0x55555555532f: cmp QWORD PTR [rbp-0x10],0x0 0x555555555334: jne 0x55555555535d 0x555555555336: mov rax,QWORD PTR [rip+0x2de3] # 0x555555558120 <stderr> 0x55555555533d: mov rcx,rax 0x555555555340: mov edx,0xb 0x555555555345: mov esi,0x1 0x55555555534a: lea rdi,[rip+0xd23] # 0x555555556074 0x555555555351: call 0x555555555080 <fwrite@plt> 0x555555555356: mov eax,0x1 0x55555555535b: jmp 0x5555555553cf 0x55555555535d: mov rax,QWORD PTR [rbp-0x10] 0x555555555361: mov eax,DWORD PTR [rax+0x8] 0x555555555364: cdqe 0x555555555366: lea rdx,[rax*8+0x0] 0x55555555536e: lea rax,[rip+0x2d6b] # 0x5555555580e0 0x555555555375: mov rax,QWORD PTR [rdx+rax*1] 0x555555555379: mov QWORD PTR [rbp-0x8],rax 0x55555555537d: mov rax,QWORD PTR [rbp-0x10] 0x555555555381: mov eax,DWORD PTR [rax+0x4] 0x555555555384: cdqe 0x555555555386: lea rdx,[rax*4+0x0] 0x55555555538e: lea rax,[rip+0x2cdb] # 0x555555558070 0x555555555395: mov edx,DWORD PTR [rdx+rax*1] 0x555555555398: mov rax,QWORD PTR [rbp-0x10] 0x55555555539c: mov eax,DWORD PTR [rax] 0x55555555539e: cdqe 0x5555555553a0: lea rcx,[rax*8+0x0] 0x5555555553a8: lea rax,[rip+0x2cf1] # 0x5555555580a0 0x5555555553af: mov rax,QWORD PTR [rcx+rax*1] 0x5555555553b3: mov rcx,QWORD PTR [rbp-0x8] 0x5555555553b7: mov esi,edx 0x5555555553b9: mov rdi,rax 0x5555555553bc: call rcx 0x5555555553be: mov rax,QWORD PTR [rbp-0x10] 0x5555555553c2: mov rdi,rax 0x5555555553c5: call 0x555555555030 <free@plt> 0x5555555553ca: mov eax,0x0 0x5555555553cf: leave 0x5555555553d0: ret 0x5555555553d1: nop WORD PTR cs:[rax+rax*1+0x0] 0x5555555553db: nop DWORD PTR [rax+rax*1+0x0] 0x5555555553e0: push r15 0x5555555553e2: lea r15,[rip+0x29ff] # 0x555555557de8 0x5555555553e9: push r14 0x5555555553eb: mov r14,rdx 0x5555555553ee: push r13 0x5555555553f0: mov r13,rsi 0x5555555553f3: push r12 Então, sem seguida há um salto para 0x55555555535d onde começa a validação do valor do argumento do programa. Há 3 blocos de código separados pela instrução cdqe e ao final há uma chamada à função em 0x5555555553bc: call rcx Cada bloco trata de um dígito do argumento começando pelo último. Aqui o dígito 5 do argumento de entrada 985 é multiplicado por 8, guardando 0x28 em RDX: 0x555555555366 lea rdx, [rax*8+0x0] Depois RAX recebe um endereço em: 0x55555555536e lea rax, [rip+0x2d6b] # 0x5555555580e0 E por fim RAX fica com o resultado da soma de RDX com o próprio RAX, que é o endereço da função que será chamada ao final das validações em call rcx: 0x555555555375 mov rax, QWORD PTR [rdx+rax*1] ... $rax : 0x0000555555555185 → push rbp 1 Quote Link to comment Share on other sites More sharing options...
Supporter - Byte paulosgf Posted December 27, 2021 at 02:30 AM Supporter - Byte Share Posted December 27, 2021 at 02:30 AM (edited) Este valor 5 do último dígito eu deduzi a partir dos valores encontrados subsequentemente ao endereço carregado em EAX: Spoiler 0x5555555580e0 gef➤ x /50xg 0x5555555580e0 0x5555555580e0: 0x0000000000000000 0x0000000000021233 0x5555555580f0: 0xffffffffffffffff 0x0000000000000913 0x555555558100: 0x000000000000ad2c 0x0000555555555185 0x555555558110: 0x0000000000000233 0x0000000000000000 0x555555558120 <stderr>: 0x00007ffff7f985c0 0x0000000000000000 gef➤ x /gx 0x555555558108 0x555555558108: 0x0000555555555185 gef➤ x /50i 0x0000555555555185 0x555555555185: push rbp 0x555555555186: mov rbp,rsp 0x555555555189: sub rsp,0x20 0x55555555518d: mov QWORD PTR [rbp-0x18],rdi 0x555555555191: mov DWORD PTR [rbp-0x1c],esi 0x555555555194: mov DWORD PTR [rbp-0x4],0x0 0x55555555519b: jmp 0x5555555551be 0x55555555519d: mov eax,DWORD PTR [rbp-0x4] 0x5555555551a0: movsxd rdx,eax 0x5555555551a3: mov rax,QWORD PTR [rbp-0x18] 0x5555555551a7: add rax,rdx 0x5555555551aa: movzx eax,BYTE PTR [rax] 0x5555555551ad: movsx eax,al 0x5555555551b0: xor eax,DWORD PTR [rbp-0x1c] 0x5555555551b3: mov edi,eax 0x5555555551b5: call 0x555555555040 <putchar@plt> 0x5555555551ba: add DWORD PTR [rbp-0x4],0x1 0x5555555551be: cmp DWORD PTR [rbp-0x4],0xb 0x5555555551c2: jle 0x55555555519d 0x5555555551c4: mov edi,0xa 0x5555555551c9: call 0x555555555040 <putchar@plt> 0x5555555551ce: nop 0x5555555551cf: leave 0x5555555551d0: ret Entretanto, os valores dos outros dígitos não consegui achar... Tu tens alguma dica? Edited December 27, 2021 at 02:33 AM by paulosgf 1 Quote Link to comment Share on other sites More sharing options...
Supporter - Nibble anderson_leite Posted December 31, 2021 at 02:58 PM Author Supporter - Nibble Share Posted December 31, 2021 at 02:58 PM @paulosgfExcelente analise! O desafio é bem complicadinho mesmo mas a resolução é extremamente simples!, vou colocar minha dica na tag de spoilers Spoiler Toda a sua analise está correta até agora, o ponto chave para resolver agora é notar que sua entrada na vdd é um indice, veja que voce soma ela com um endereço e é dessa maneira que vetores são estruturados em memoria! Incrivel voce fazer tudo isso dentro do GDB! mas talvez se voce usasse o Cutter ou o Radare2, a analise estática já iria te revelar bastante coisa antes mesmo de entrar no debugger! Boa sorte Fico muito feliz que você esteja resolvendo. 1 Quote Link to comment Share on other sites More sharing options...
Supporter - Byte paulosgf Posted January 9, 2022 at 12:03 AM Supporter - Byte Share Posted January 9, 2022 at 12:03 AM Obrigado pelo apoio amigo! Pois então... eu já havia dado uma olhada prévia com o radare2, mas acho que a analise dinâmica sempre dá mais retorno. De qualquer forma não consegui evoluir mais e parti para a força bruta, criei esse script aqui: Spoiler #!/usr/bin/env python3 import random import subprocess as sp import time def main(): """ Testa aproximadamente 900 números compreendendo todos os valores de 3 casas decimais por força bruta em um processo para cada tentativa usando um valor escolhido aleatóreamente em cada tentativa. """ MIN = 100 # valor inicial MAX = 999 # valor final FAX = 899 # espaço de tentativas BIN = "/home/paulo/crack/crackmes/callme" randlist = [] # lista com os valores distribuidos aleatóreamente randlist = random.sample(range(MIN, MAX), FAX) start_time = time.time() # Para cada valor da lista é gerado um novo processo com o binário for i in range(len(randlist)): ex = sp.run([BIN, str(randlist[i])], stdout=sp.PIPE) # Se o valor de retorno do processo for zero, temos saída legível if ex.returncode == 0: print( "Entrada: " + str(randlist[i]) + " --- Saida: " + ex.stdout.decode(errors="replace") ) # Tempo total do atque em segundos: print("--- %s segundos ---" % (time.time() - start_time)) if __name__ == "__main__": main() E dentre outros valores de saída, obtive a seguinte string que pareceu fazer sentido: Entrada: 335 --- Saida: Bom trabalho 1 Quote Link to comment Share on other sites More sharing options...
Supporter - Byte paulosgf Posted January 9, 2022 at 12:33 AM Supporter - Byte Share Posted January 9, 2022 at 12:33 AM A partir dai tentei entender como o algoritmo funciona e achei o seguinte: Spoiler paulo@nirvana:~/crack/crackmes$ gdb --args ./callme 335 ... Voltando àquela parte do código onde é feita a validação dos argumentos em 3 blocos, um para cada dígito, de trás para frente. O primeiro bloco segue aquele raciocinio que expus acima. Já no bloco seguinte é desenvolvida a constante que fará um XOR com uma string fixa e retornará a resposta correta decodificada: ... 0x555555555381 mov eax, DWORD PTR [rax+0x4] (aqui coloca o valor do proximo dígito em EAX == 3) ... 0x555555555386 lea rdx, [rax*4+0x0] (em seguida multiplica esse valor por 4 e coloca em RDX == 0xC) ... 0x55555555538e lea rax, [rip+0x2cdb] # 0x555555558070 (carrega um valor prévio em RAX == 0x23) ... 0x555555555395 mov edx, DWORD PTR [rdx+rax*1] (guarda em EDX o valor da soma de RDX + RAX, que será a constante == 0x3b) Por fim o terceiro bloco é carregada a string a ser decodificada por XOR, que é usada como argumento de chamada da função que decodifica: 0x555555555398 mov rax, QWORD PTR [rbp-0x10] (carrega em RAX o valor do primeiro dígito == 3) ... 0x5555555553a0 lea rcx, [rax*8+0x0] (carrega em RCX o valor de RAX * 3 == 0x18) ... 0x5555555553a8 lea rax, [rip+0x2cf1] # 0x5555555580a0 (RAX recebe o ponteiro que levará a string a ser decodificada) ... 0x5555555553af mov rax, QWORD PTR [rcx+rax*1] (a soma do ponteiro com o valor 0x18 vai apontar para a string em 0x0000555555556043) Por fim call rcx chama a função de decodificação com este ponteiro e a constante como argumentos em 0x555555555185: *0x555555555185 ( $rdi = 0x0000555555556043, $rsi = 0x000000000000003b, $rdx = 0x000000000000003b, $rcx = 0x0000555555555185 ) Quote Link to comment Share on other sites More sharing options...
Supporter - Byte paulosgf Posted January 9, 2022 at 12:40 AM Supporter - Byte Share Posted January 9, 2022 at 12:40 AM A função de decodificação funciona da seguinte maneira: Spoiler gef➤ x /50i $rip => 0x55555555519d: mov eax,DWORD PTR [rbp-0x4] (contador) 0x5555555551a0: movsxd rdx,eax 0x5555555551a3: mov rax,QWORD PTR [rbp-0x18] (RAX recebe ponteiro para a string iniciando em 0x0000555555556043) 0x5555555551a7: add rax,rdx (RAX incrementa endereço em 1) 0x5555555551aa: movzx eax,BYTE PTR [rax] 0x5555555551ad: movsx eax,al 0x5555555551b0: xor eax,DWORD PTR [rbp-0x1c] (aqui é feito o XOR de cada caractere da string com 0x3b) 0x5555555551b3: mov edi,eax 0x5555555551b5: call 0x555555555040 <putchar@plt> (gera string de resposta decodificada no ponteiro $rsi) 0x5555555551ba: add DWORD PTR [rbp-0x4],0x1 (itera endereço da string em 1) 0x5555555551be: cmp DWORD PTR [rbp-0x4],0xb (compara com tamanho 11) 0x5555555551c2: jle 0x55555555519d (se menor, volta para início) 1 Quote Link to comment Share on other sites More sharing options...
Supporter - Byte paulosgf Posted January 9, 2022 at 12:42 AM Supporter - Byte Share Posted January 9, 2022 at 12:42 AM Spoiler Por fim RSI aponta para a string que serve de argumento para putchar() imprimir a resposta final: $rsi : 0x00005555555592c0 → "Bom trabalho\n" ● 0x5555555551c4 mov edi, 0xa 0x5555555551c9 call 0x555555555040 <putchar@plt> → 0x5555555551ce nop 0x5555555551cf leave 0x5555555551d0 ret 1 Quote Link to comment Share on other sites More sharing options...
Supporter - Byte paulosgf Posted January 9, 2022 at 12:47 AM Supporter - Byte Share Posted January 9, 2022 at 12:47 AM Bom, isso foi uma análise feita a partir da força bruta, então teve um certo nível de ajuda para chegar na solução. Por acaso tu terias alguma estratégia de raciocínio que leve a solução sem força bruta? Abraços! 1 Quote Link to comment Share on other sites More sharing options...
Supporter - Nibble anderson_leite Posted February 22, 2022 at 04:53 PM Author Supporter - Nibble Share Posted February 22, 2022 at 04:53 PM (edited) On 1/8/2022 at 9:47 PM, paulosgf said: Bom, isso foi uma análise feita a partir da força bruta, então teve um certo nível de ajuda para chegar na solução. Por acaso tu terias alguma estratégia de raciocínio que leve a solução sem força bruta? Abraços! Parabéns pela solução, e desculpa pela demora no feedback ? Bom, vou deixar uma linha de raciocínio com a tag de spoiler: Spoiler Ao olhar o disassembling é possivel notar que o nosso input é usado como offset, ou seja, indice de um vetor. Você então pode tentar entender o que cada entrada faz com esse vetor e como eles são usados. Vale lembrar que vetores são apenas dados alinhados em memória pelo tamanho da variável usada. Ao analisar isso, você provavelmente iria chegar em algum valor que tem referencia a uma função "oculta" dentro do binário, que não é chamada diretamente. Talvez se você tivesse optado por uma analise estática usando o Cutter, radare ou o IDA isso teria ficado claro bem no inicio, pois essas tools descobrem funções espalhadas pelo binário. :) Edited February 22, 2022 at 04:55 PM by anderson_leite 1 1 Quote Link to comment Share on other sites More sharing options...
Supporter - Byte paulosgf Posted February 24, 2022 at 08:10 PM Supporter - Byte Share Posted February 24, 2022 at 08:10 PM Obrigado @anderson_leite! Essa dica final me deu uma direção a tomar daqui em diante. Vi que tu estavas sem tempo, porque tá elaborando um treinamento justamente sobre o radare2. Vou acompanhar para melhorar minhas habilidades nisso. ? Abraços! 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.