Nuit-du-hack-2016/Matriochka/Step-2
You are here: | Matriochka (step 2)
|
Description
Can you help me? Recently, I found an executable binary. As I'm a true newbie, Certainly, to solve it, I will have difficulties. Keep in mind, the first step is quite easy. Maybe the last one will be quite tricky. Emulating it could be a good idea.
You must solve step 1 first.
It's a tar archive that we need to uncompress. Once done, it results in a file named stage2.bin. You can download it from the below mirror:
- (mirror) https://github.com/sebastiendamaye/public/raw/master/crackme/ccd56b24f6136ccc3168d34fd6afece3
File:
MD5 | ccd56b24f6136ccc3168d34fd6afece3 |
---|---|
SHA1 | fef8136d263857d84465f4aa230a3136a901585c |
SHA256 | 7024b25760f6d830ad4be7f2db305aef3b27dcaab359834632709f2e542f4ede |
Analysis
Running the executable
The executable seems to expect an argument:
$ ./stage2.bin Usage: ./stage2.bin <pass> $ ./stage2.bin oops Try again...
Graph overview
As shown on the below graph overview, the main function (offset 0x4006F2) is checking each character of the user input independantly:
Checks
Checking number of arguments
First of all, the function checks the number of arguments. If no argument is provided, the function jumps to the "Usage" label:
.text:00000000004006F2 ; int __cdecl main(int, char **, char **)
.text:00000000004006F2 main proc near
.text:00000000004006F2
.text:00000000004006F2 len_pass = qword ptr -30h
.text:00000000004006F2 num_args = dword ptr -24h
.text:00000000004006F2 flag_good = dword ptr -14h
.text:00000000004006F2
.text:00000000004006F2 push rbp
.text:00000000004006F3 mov rbp, rsp
.text:00000000004006F6 push rbx
.text:00000000004006F7 sub rsp, 28h
.text:00000000004006FB mov [rbp+num_args], edi
.text:00000000004006FE mov [rbp+len_pass], rsi
.text:0000000000400702 cmp [rbp+num_args], 2
.text:0000000000400706 jz short loc_40072D
.text:0000000000400708 mov rax, [rbp+len_pass]
.text:000000000040070C mov rdx, [rax]
.text:000000000040070F mov rax, cs:stdout
.text:0000000000400716 mov esi, offset format ; "Usage: %s <pass>\n"
.text:000000000040071B mov rdi, rax ; stream
.text:000000000040071E mov eax, 0
.text:0000000000400723 call _fprintf
.text:0000000000400728 jmp loc_4009B3
Checking pass length
Then, starting at offset 0x40072D, a flag is set to 1 by default. Whenever a test fails, the flag will be set to 0 and the value will be eventually checked at the end of the function to either jump to the good boy or the bad one.
The user input length is gathered at offset 0x400734, and a computation is performed to eventually land to a comparison at offset 0x40075F. If the user (including the trailing character) is 11 characters, the program continues. Else, it will jump to the bad boy.
.text:000000000040072D mov [rbp+flag_good], 1 ; flag_good = 1
.text:0000000000400734 mov rax, [rbp+len_pass]
.text:0000000000400738 add rax, 8
.text:000000000040073C mov rax, [rax]
.text:000000000040073F mov rdi, rax ; s
.text:0000000000400742 call _strlen ; rax = len_pass
.text:0000000000400747 lea rdx, [rax+1] ; rdx = len_pass + 1
.text:000000000040074B mov rax, rdx ; rax = len_pass + 1
.text:000000000040074E shl rax, 2 ; 4*(len_pass+1)
.text:0000000000400752 add rax, rdx ; rax = 5*(len_pass+1)
.text:0000000000400755 shl rax, 2 ; rax = 20*(len_pass+1)
.text:0000000000400759 add rax, rdx ; rax = 21*(len_pass+1)
.text:000000000040075C add rax, rax ; rax = 42*(len_pass+1)
.text:000000000040075F cmp rax, 1F8h ; 504 = 42*(len_pass+1)
.text:0000000000400765 jnz bad_boy ; jump to bad boy if len(pass) != 11
Checking each character
offset 0x40076B
Starting at offset 0x40076B, the 1st character of the user input is checked and compared to 0x50 ("P" letter). If the test fails, the flag will be set to 0.
.text:000000000040076B mov [rbp+flag_good], 1
.text:0000000000400772 mov rax, [rbp+len_pass]
.text:0000000000400776 add rax, 8
.text:000000000040077A mov rax, [rax]
.text:000000000040077D movzx eax, byte ptr [rax] ; eax = pass
.text:0000000000400780 cmp al, 50h ; pass[0] = 'P'
.text:0000000000400782 jz short loc_40078B
.text:0000000000400784 mov [rbp+flag_good], 0
offset 0x40078B
The below code checks that the 4th character of the user input is "d".
.text:000000000040078B mov rax, [rbp+len_pass]
.text:000000000040078F add rax, 8
.text:0000000000400793 mov rax, [rax]
.text:0000000000400796 add rax, 3
.text:000000000040079A movzx eax, byte ptr [rax]
.text:000000000040079D movsx eax, al ; eax = pass[3]
.text:00000000004007A0 add eax, eax ; eax = pass[3] * 2
.text:00000000004007A2 cmp eax, 0C8h ; pass[3] = 0xC8/2 = 0x64 = 'd'
.text:00000000004007A7 jz short loc_4007B0
.text:00000000004007A9 mov [rbp+flag_good], 0
offset 0x4007B0
The below code checks that the 7th character of the user input is "p".
.text:00000000004007B0 mov rax, [rbp+len_pass]
.text:00000000004007B4 add rax, 8
.text:00000000004007B8 mov rax, [rax]
.text:00000000004007BB movzx eax, byte ptr [rax] ; eax = pass[0]
.text:00000000004007BE movsx eax, al
.text:00000000004007C1 lea edx, [rax+10h]
.text:00000000004007C4 mov rax, [rbp+len_pass]
.text:00000000004007C8 add rax, 8
.text:00000000004007CC mov rax, [rax]
.text:00000000004007CF add rax, 6
.text:00000000004007D3 movzx eax, byte ptr [rax] ; eax = pass[6]
.text:00000000004007D6 movsx eax, al
.text:00000000004007D9 sub eax, 10h
.text:00000000004007DC cmp edx, eax ; pass[6] - 0x10 = pass[0] + 0x10
.text:00000000004007DC ; pass[6] = pass[0] + 0x20 = 'p'
.text:00000000004007DE jz short loc_4007E7
.text:00000000004007E0 mov [rbp+flag_good], 0
offset 0x4007E7
The below code checks that the 6th character of the user input is "_".
.text:00000000004007E7 mov rax, [rbp+len_pass]
.text:00000000004007EB add rax, 8
.text:00000000004007EF mov rax, [rax]
.text:00000000004007F2 add rax, 5
.text:00000000004007F6 movzx eax, byte ptr [rax] ; eax = pass[5]
.text:00000000004007F9 movsx rbx, al ; rbx = pass[5]
.text:00000000004007FD mov rax, [rbp+len_pass]
.text:0000000000400801 add rax, 8
.text:0000000000400805 mov rax, [rax]
.text:0000000000400808 mov rdi, rax ; s
.text:000000000040080B call _strlen
.text:0000000000400810 mov rdx, rax ; rdx = len_pass
.text:0000000000400813 mov rax, rdx ; rax = len_pass
.text:0000000000400816 shl rax, 3 ; rax = len_pass * 8
.text:000000000040081A add rax, rdx ; rax = len_pass * 9
.text:000000000040081D sub rax, 4 ; rax = len_pass * 9 - 4
.text:0000000000400821 cmp rbx, rax ; pass[5] = len_pass * 9 - 4
.text:0000000000400821 ; pass[5] = '_'
.text:0000000000400824 jz short loc_40082D
.text:0000000000400826 mov [rbp+flag_good], 0
offset 0x40082D
The below code checks that the 8th character of the user input is the same as the 2nd one:
.text:000000000040082D mov rax, [rbp+len_pass]
.text:0000000000400831 add rax, 8
.text:0000000000400835 mov rax, [rax]
.text:0000000000400838 add rax, 1
.text:000000000040083C movzx edx, byte ptr [rax] ; edx = pass[1]
.text:000000000040083F mov rax, [rbp+len_pass]
.text:0000000000400843 add rax, 8
.text:0000000000400847 mov rax, [rax]
.text:000000000040084A add rax, 7
.text:000000000040084E movzx eax, byte ptr [rax] ; eax = pass[7]
.text:0000000000400851 cmp dl, al ; pass[7] = pass[1]
.text:0000000000400853 jz short loc_40085C
.text:0000000000400855 mov [rbp+flag_good], 0
offset 0x40085C
The below code checks that the 11th charcter of the user input is the same as the 2nd one:
.text:000000000040085C mov rax, [rbp+len_pass]
.text:0000000000400860 add rax, 8
.text:0000000000400864 mov rax, [rax]
.text:0000000000400867 add rax, 1
.text:000000000040086B movzx edx, byte ptr [rax] ; edx = pass[1]
.text:000000000040086E mov rax, [rbp+len_pass]
.text:0000000000400872 add rax, 8
.text:0000000000400876 mov rax, [rax]
.text:0000000000400879 add rax, 0Ah
.text:000000000040087D movzx eax, byte ptr [rax] ; eax = pass[10]
.text:0000000000400880 cmp dl, al ; pass[10] = pass[1]
.text:0000000000400882 jz short loc_40088B
.text:0000000000400884 mov [rbp+flag_good], 0
offset 0x40088B
The below code checks that the 2nd character of the user input is "a". Hence, from the previous test, we also deduce the expected 8th an 11th characters.
.text:000000000040088B mov rax, [rbp+len_pass]
.text:000000000040088F add rax, 8
.text:0000000000400893 mov rax, [rax]
.text:0000000000400896 add rax, 1
.text:000000000040089A movzx eax, byte ptr [rax] ; eax = pass[1]
.text:000000000040089D movsx eax, al
.text:00000000004008A0 lea edx, [rax-11h] ; edx = pass[1] - 0x11
.text:00000000004008A3 mov rax, [rbp+len_pass]
.text:00000000004008A7 add rax, 8
.text:00000000004008AB mov rax, [rax]
.text:00000000004008AE movzx eax, byte ptr [rax] ; eax = pass[0]
.text:00000000004008B1 movsx eax, al
.text:00000000004008B4 cmp edx, eax ; pass[1] = pass[0] + 0x11 = 'a'
.text:00000000004008B6 jz short loc_4008BF
.text:00000000004008B8 mov [rbp+flag_good], 0
offset 0x4008BF
The below code checks that the 10th character of the user input is the same as the 4th one, which is "d".
.text:00000000004008BF mov rax, [rbp+len_pass]
.text:00000000004008C3 add rax, 8
.text:00000000004008C7 mov rax, [rax]
.text:00000000004008CA add rax, 3
.text:00000000004008CE movzx edx, byte ptr [rax] ; edx = pass[3]
.text:00000000004008D1 mov rax, [rbp+len_pass]
.text:00000000004008D5 add rax, 8
.text:00000000004008D9 mov rax, [rax]
.text:00000000004008DC add rax, 9
.text:00000000004008E0 movzx eax, byte ptr [rax]
.text:00000000004008E3 cmp dl, al ; pass[9] = pass[3] = 'd'
.text:00000000004008E5 jz short loc_4008EE
.text:00000000004008E7 mov [rbp+flag_good], 0
offset 0x4008EE
The below code checks that the 5th character of the user input is "i".
.text:00000000004008EE mov rax, [rbp+len_pass]
.text:00000000004008F2 add rax, 8
.text:00000000004008F6 mov rax, [rax]
.text:00000000004008F9 add rax, 4
.text:00000000004008FD movzx eax, byte ptr [rax]
.text:0000000000400900 cmp al, 69h ; pass[4] = 0x69 = 'i'
.text:0000000000400902 jz short loc_40090B
.text:0000000000400904 mov [rbp+flag_good], 0
offset 0x40090B
The below code checks that the 3rd character of the user input is "n".
.text:000000000040090B mov rax, [rbp+len_pass]
.text:000000000040090F add rax, 8
.text:0000000000400913 mov rax, [rax]
.text:0000000000400916 add rax, 2
.text:000000000040091A movzx eax, byte ptr [rax] ; eax = pass[2]
.text:000000000040091D movsx edx, al ; edx = pass[2]
.text:0000000000400920 mov rax, [rbp+len_pass]
.text:0000000000400924 add rax, 8
.text:0000000000400928 mov rax, [rax]
.text:000000000040092B add rax, 1
.text:000000000040092F movzx eax, byte ptr [rax] ; eax = pass[1]
.text:0000000000400932 movsx eax, al
.text:0000000000400935 sub edx, eax
.text:0000000000400937 mov eax, edx
.text:0000000000400939 cmp eax, 0Dh ; pass[2] - pass[1] = 0xD
.text:0000000000400939 ; pass[2] = 'n'
.text:000000000040093C jz short loc_400945
.text:000000000040093E mov [rbp+flag_good], 0
offset 0x400945
The below code checks that the 9th character of the user input is "n".
.text:0000000000400945 mov rax, [rbp+len_pass]
.text:0000000000400949 add rax, 8
.text:000000000040094D mov rax, [rax]
.text:0000000000400950 add rax, 8
.text:0000000000400954 movzx eax, byte ptr [rax]
.text:0000000000400957 movsx edx, al ; edx = pass[8]
.text:000000000040095A mov rax, [rbp+len_pass]
.text:000000000040095E add rax, 8
.text:0000000000400962 mov rax, [rax]
.text:0000000000400965 add rax, 7
.text:0000000000400969 movzx eax, byte ptr [rax] ; eax = pass[7]
.text:000000000040096C movsx eax, al
.text:000000000040096F sub edx, eax ; edx = pass[8] - pass[7]
.text:0000000000400971 mov eax, edx
.text:0000000000400973 cmp eax, 0Dh ; pass[8] - pass[7] = 0xD
.text:0000000000400973 ; pass[8] = 'n'
.text:0000000000400976 jz short loc_40097F
.text:0000000000400978 mov [rbp+flag_good], 0
Final check
The final check starts at offset 0x40097F. It checks the value of the flag that had initially been set to 1 and overwritten to 1 if any of the previous test failed. If the value is 1, a success message is displayed. Otherwise, the string "Try again..." is displayed.
.text:000000000040097F loc_40097F:
.text:000000000040097F cmp [rbp+flag_good], 0
.text:0000000000400983 jz short bad_boy
.text:0000000000400985 mov rax, [rbp+len_pass]
.text:0000000000400989 add rax, 8
.text:000000000040098D mov rax, [rax]
.text:0000000000400990 mov rdi, rax
.text:0000000000400993 call sub_40064D
.text:0000000000400998 jmp short loc_4009B3
.text:000000000040099A ; ---------------------------------------------------------------------------
.text:000000000040099A
.text:000000000040099A bad_boy:
.text:000000000040099A mov rax, cs:stdout
.text:00000000004009A1 mov esi, offset aTryAgain___ ; "Try again...\n"
.text:00000000004009A6 mov rdi, rax ; stream
.text:00000000004009A9 mov eax, 0
.text:00000000004009AE call _fprintf
Solution
The solution is "Pandi_panda". Once again, it outputs a base64 string.
$ ./stage2.bin Pandi_panda Good good! f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAEAdAAAAAAABAAAAAAAAAANBBAAAAAAAAAAAAAEAAOAAJ AEAAHAAbAAYAAAAFAAAAQAAAAAAAAABAAEAAAAAAAEAAQAAAAAAA+AEAAAAAAAD4AQAAAAAAAAgA AAAAAAAAAwAAAAQAAAA4AgAAAAAAADgCQAAAAAAAOAJAAAAAAAAcAAAAAAAAABwAAAAAAAAAAQAA AAAAAAABAAAABQAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAEwyAAAAAAAATDIAAAAAAAAAACAA AAAAAAEAAAAGAAAAED4AAAAAAAAQPmAAAAAAABA+YAAAAAAAeAIAAAAAAACwBgAAAAAAAAAAIAAA [...SNIP...] AAAAAAAAAAgAAAAAAAAACAAAAAAAAADbAAAAAQAAAAMAAAAAAAAAAEBgAAAAAAAAQAAAAAAAAHgA AAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAA5AAAAAEAAAADAAAAAAAAAHhAYAAAAAAAeEAA AAAAAAAQAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAOoAAAAIAAAAAwAAAAAAAACgQGAA AAAAAIhAAAAAAAAAIAQAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAADvAAAAAQAAADAAAAAA AAAAAAAAAAAAAACIQAAAAAAAAE0AAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAMA AAAAAAAAAAAAAAAAAAAAAAAA1UAAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AA==
Copy the output (but the "Good good!" part) to a file and decode it. It produces the stage3 binary that we will analyze here.
$ cat stage2.base64 | base64 -d > stage3.bin $ file stage3.bin stage3.bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=2ea586c925bc94d07d2d3087837dac6ef32a180c, stripped
Comments
Keywords: nuit-du-hack-2016 NDH2K16 challenge reversing