.:: @OREL ::. - Save Scooby - by mrT4ntr4
[ 10/09/2019 ]
Original copy : [https]:// --- [0 - Description [1 - High level analysis [1.1 - Symbols & strings [1.2 - Program behaviour [2 - In depth analysis [2.1 - Disassembly [2.2 - Reversing the key algorithm [3 - Final word ---
[0 - Description
Author Name Difficulty mrT4ntr4 + Save Scooby + 1 - Very easy | | +--------------------------------------------------------------+ | | Platform + Language + Upload Unix/linux etc. C/C++ 4:19 PM 07/31/2019 It seems Scooby forgot where he is. Help him find his way!! 1. Try to reverse without decompiling. (don't be lazy XD) 2. Patching is obviously not allowed. 3. Write a keygen. It's my first crackme so feel free to ping me for suggestions or feedback!! Contact : Happy Reversing !! ;-)
[1 - High level analysis
[1.1 - Symbols & strings
Starting with high level analysis, we use the file command to retrieve the binary metada and get a first look on potential obfuscation techniques :
[voidboy@voidsys Save Scooby]$ file save_scooby save_scooby: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/, for GNU/Linux 3.2.0 BuildID[sha1]=ed02ca1ecc34e77bc2846aba814ec64654296506, not stripped
According to file's output, the binary wasn't stripped nor statically linked. The challenge difficulty is set to 'Very easy' so it does make sense. We then can extract symbols and strings from binary using objdump and strings :
[voidboy@voidsys Save Scooby]$ objdump -T save_scooby save_scooby: format de fichier elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000000 w D *UND* [...] _ITM_deregisterTMCloneTable 0000000000000000 DF *UND* [...] GLIBC_2.2.5 puts 0000000000000000 DF *UND* [...] GLIBC_2.2.5 strlen 0000000000000000 DF *UND* [...] GLIBC_2.2.5 getcwd 0000000000000000 DF *UND* [...] GLIBC_2.2.5 __libc_start_main 0000000000000000 w D *UND* [...] __gmon_start__ 0000000000000000 DF *UND* [...] GLIBC_2.7 __isoc99_scanf 0000000000000000 w D *UND* [...] _ITM_registerTMCloneTable 0000000000000000 w DF *UND* [...] GLIBC_2.2.5 __cxa_finalize
[voidboy@voidsys Save Scooby]$ strings -n 5 save_scooby [...] Hi Scooby !! Where are you?? You won a medal Scooby !! Scooby Doobie Doo!! Not too easy [...]
Both commands outputted pretty useful information, objdump's output showed that getcwd was used in the binary, it might be used to compute the key but we can not be sure yet. Strings's output revealed strings that seems to be use as good and bad boy messages.
[1.2 - Program behaviour
Launching save_scooby give us the following ouput :
[voidboy@voidsys Save Scooby]$ ./save_scooby Hi Scooby !! Where are you?? 1234 Scooby Doobie Doo!! Not too easy
We're welcome with a "Hi Scooby !!" message and invite to answer the prompt "Where are you??". If we answer with "1234", the program display "Scooby Doobie Doo!! Not too easy" and quit. After testing differents inputs, the program doesn't seem to handle other case and display this uniq message.
[2 - In depth analysis
Since we know a little bit more about save_scooby thanks to metadata gathered in the High level analysis section we can now use what we learned to build a simple view on how the disassembly may looks like and identify the key generation part.
[2.1 - Disassembly
Disassembling save_scooby's main give us following output :
gef➤ disas main Dump of assembler code for function main: [1 - Get current path 0x000000000000073a <+0>: push rbp 0x000000000000073b <+1>: mov rbp,rsp 0x000000000000073e <+4>: sub rsp,0x1120 0x0000000000000745 <+11>: lea rax,[rbp-0x1020] 0x000000000000074c <+18>: mov esi,0x1000 0x0000000000000751 <+23>: mov rdi,rax 0x0000000000000754 <+26>: call 0x600 <getcwd@plt> 0x0000000000000759 <+31>: lea rax,[rbp-0x1020] 0x0000000000000760 <+38>: mov rdi,rax 0x0000000000000763 <+41>: call 0x5f0 <strlen@plt> 0x0000000000000768 <+46>: mov DWORD PTR [rbp-0x10],eax 0x000000000000076b <+49>: mov DWORD PTR [rbp-0x4],0x0 0x0000000000000772 <+56>: jmp 0x820 <main+230> ---------------------------------------------------------------------------- [2 - key generation 0x0000000000000777 <+61>: mov eax,DWORD PTR [rbp-0x4] 0x000000000000077a <+64>: cdqe 0x000000000000077c <+66>: movzx eax,BYTE PTR [rbp+rax*1-0x1020] 0x0000000000000784 <+74>: cmp al,0x2f 0x0000000000000786 <+76>: jne 0x79a <main+96> 0x0000000000000788 <+78>: mov eax,DWORD PTR [rbp-0x4] 0x000000000000078b <+81>: cdqe 0x000000000000078d <+83>: mov BYTE PTR [rbp+rax*1-0x1020],0x24 0x0000000000000795 <+91>: jmp 0x81c <main+226> 0x000000000000079a <+96>: mov eax,DWORD PTR [rbp-0x4] 0x000000000000079d <+99>: cdqe 0x000000000000079f <+101>: movzx eax,BYTE PTR [rbp+rax*1-0x1020] 0x00000000000007a7 <+109>: cmp al,0x60 0x00000000000007a9 <+111>: jle 0x7dc <main+162> 0x00000000000007ab <+113>: mov eax,DWORD PTR [rbp-0x4] 0x00000000000007ae <+116>: cdqe 0x00000000000007b0 <+118>: movzx eax,BYTE PTR [rbp+rax*1-0x1020] 0x00000000000007b8 <+126>: cmp al,0x7a 0x00000000000007ba <+128>: jg 0x7dc <main+162> 0x00000000000007bc <+130>: mov eax,DWORD PTR [rbp-0x4] 0x00000000000007bf <+133>: cdqe 0x00000000000007c1 <+135>: movzx eax,BYTE PTR [rbp+rax*1-0x1020] 0x00000000000007c9 <+143>: sub eax,0x1e 0x00000000000007cc <+146>: mov edx,eax 0x00000000000007ce <+148>: mov eax,DWORD PTR [rbp-0x4] 0x00000000000007d1 <+151>: cdqe 0x00000000000007d3 <+153>: mov BYTE PTR [rbp+rax*1-0x1020],dl 0x00000000000007da <+160>: jmp 0x81c <main+226> 0x00000000000007dc <+162>: mov eax,DWORD PTR [rbp-0x4] 0x00000000000007df <+165>: cdqe 0x00000000000007e1 <+167>: movzx eax,BYTE PTR [rbp+rax*1-0x1020] 0x00000000000007e9 <+175>: cmp al,0x40 0x00000000000007eb <+177>: jle 0x81c <main+226> 0x00000000000007ed <+179>: mov eax,DWORD PTR [rbp-0x4] 0x00000000000007f0 <+182>: cdqe 0x00000000000007f2 <+184>: movzx eax,BYTE PTR [rbp+rax*1-0x1020] 0x00000000000007fa <+192>: cmp al,0x5a 0x00000000000007fc <+194>: jg 0x81c <main+226> 0x00000000000007fe <+196>: mov eax,DWORD PTR [rbp-0x4] 0x0000000000000801 <+199>: cdqe 0x0000000000000803 <+201>: movzx eax,BYTE PTR [rbp+rax*1-0x1020] 0x000000000000080b <+209>: add eax,0x1e 0x000000000000080e <+212>: mov edx,eax 0x0000000000000810 <+214>: mov eax,DWORD PTR [rbp-0x4] 0x0000000000000813 <+217>: cdqe 0x0000000000000815 <+219>: mov BYTE PTR [rbp+rax*1-0x1020],dl 0x000000000000081c <+226>: add DWORD PTR [rbp-0x4],0x1 0x0000000000000820 <+230>: mov eax,DWORD PTR [rbp-0x4] 0x0000000000000823 <+233>: cmp eax,DWORD PTR [rbp-0x10] 0x0000000000000826 <+236>: jl 0x777 <main+61> ---------------------------------------------------------------------------- [3 - Get user input 0x000000000000082c <+242>: lea rdi,[rip+0x135] # 0x968 0x0000000000000833 <+249>: call 0x5e0 <puts@plt> 0x0000000000000838 <+254>: lea rax,[rbp-0x1120] 0x000000000000083f <+261>: mov rsi,rax 0x0000000000000842 <+264>: lea rdi,[rip+0x13c] # 0x985 0x0000000000000849 <+271>: mov eax,0x0 0x000000000000084e <+276>: call 0x610 <__isoc99_scanf@plt> 0x0000000000000853 <+281>: lea rax,[rbp-0x1120] 0x000000000000085a <+288>: mov rdi,rax 0x000000000000085d <+291>: call 0x5f0 <strlen@plt> 0x0000000000000862 <+296>: mov DWORD PTR [rbp-0x14],eax 0x0000000000000865 <+299>: mov DWORD PTR [rbp-0x8],0x0 0x000000000000086c <+306>: mov DWORD PTR [rbp-0xc],0x0 0x0000000000000873 <+313>: jmp 0x8a2 <main+360> ---------------------------------------------------------------------------- [4 - Verify key 0x0000000000000875 <+315>: mov eax,DWORD PTR [rbp-0x8] 0x0000000000000878 <+318>: cdqe 0x000000000000087a <+320>: movzx edx,BYTE PTR [rbp+rax*1-0x1020] 0x0000000000000882 <+328>: mov eax,DWORD PTR [rbp-0x8] 0x0000000000000885 <+331>: cdqe 0x0000000000000887 <+333>: movzx eax,BYTE PTR [rbp+rax*1-0x1120] 0x000000000000088f <+341>: cmp dl,al 0x0000000000000891 <+343>: jne 0x899 <main+351> 0x0000000000000893 <+345>: add DWORD PTR [rbp-0x8],0x1 0x0000000000000897 <+349>: jmp 0x8a2 <main+360> 0x0000000000000899 <+351>: mov DWORD PTR [rbp-0xc],0xffffffff 0x00000000000008a0 <+358>: jmp 0x8b2 <main+376> 0x00000000000008a2 <+360>: mov eax,DWORD PTR [rbp-0x8] 0x00000000000008a5 <+363>: cmp eax,DWORD PTR [rbp-0x14] 0x00000000000008a8 <+366>: jge 0x8b2 <main+376> 0x00000000000008aa <+368>: mov eax,DWORD PTR [rbp-0x8] 0x00000000000008ad <+371>: cmp eax,DWORD PTR [rbp-0x10] 0x00000000000008b0 <+374>: jl 0x875 <main+315> 0x00000000000008b2 <+376>: cmp DWORD PTR [rbp-0xc],0x0 0x00000000000008b6 <+380>: jne 0x8c6 <main+396> 0x00000000000008b8 <+382>: lea rdi,[rip+0xc9] # 0x988 0x00000000000008bf <+389>: call 0x5e0 <puts@plt> 0x00000000000008c4 <+394>: jmp 0x8d2 <main+408> 0x00000000000008c6 <+396>: lea rdi,[rip+0xdb] # 0x9a8 0x00000000000008cd <+403>: call 0x5e0 <puts@plt> 0x00000000000008d2 <+408>: mov eax,0x0 0x00000000000008d7 <+413>: leave 0x00000000000008d8 <+414>: ret End of assembler dump.
I splitted the all disassembly in 4 parts, each represents a specific action. As we guessed in our high level analysis, getcwd is used to compute the key. The first block goes from main+0 until main+61 where there is an unconditonnal jump to main+230. In this first block getcwd's output an his lenght are stored on the stack at rbp-0x1020 and rbp-0x10. The execution in then passed to the second block which will generate the key base on getcwd's ouput.
[2.2 - Reversing the key algorithm
In this second block, getcwd's output is compared against multiples hex values 0x2f, 0x60, 0x7a, 0x40, 0x5a. The following algorithm is used :
if(key[i] == 0x2f) // '/' flag[i] = 0x24 else if(key[i] > 0x60 && key[i] <= 0x7a) // 'a' to 'z' flag[i] = key[i] - 0x1e else if(key[i] > 0x40 && key[i] <= 0x5a) // 'A' to 'Z' flag[i] = key[i] + 0x1e
The program is basically substracting 0x1e to all lowercase caracters and adding 0x1e to all uppercase caracters. It also replaces all '/' with 0x24, a '$'. All the others caracters are not handled by the program meaning that it will simply skip them. Third and fourth parts are not very relevant they only deal with the user input and flag verification. We now have all the information to build our keygen, here's mine written in C :
[voidboy@voidsys Save Scooby]$ cat solve.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define BUFF_SIZE 0x100 int main(int argc, char *argv[]) { int i =0; char s[BUFF_SIZE] = {0}; char k[BUFF_SIZE] = {0}; if(s == getcwd(s, BUFF_SIZE)) { while(s[i] != '\0') { if(s[i] == 0x2f) k[i] = 0x24; else if(s[i] > 0x60 && s[i] <= 0x7a) k[i] = s[i] - 0x1e; else if(s[i] > 0x40 && s[i] <= 0x5a) k[i] = s[i] + 0x1e; i++; } k[i] = '\0'; } printf("Current path : %s\n", s); printf("Your key is : %s\n", k); return 0; } [voidboy@voidsys Save Scooby]$ gcc -o solve solve.c [voidboy@voidsys Save Scooby]$ ./solve Current path : /home/voidboy/ Scooby Your key is : $JQOG$XQKFDQ[$ETCEMOGU [voidboy@voidsys Save Scooby]$ ./save_scooby Hi Scooby !! Where are you?? $JQOG$XQKFDQ[$ETCEMOGU You won a medal Scooby !!
3] - Final word
'$' is the golden ticket.

Tout est faux tout est conforme.