Nuit-du-hack-2016/Matriochka/Step-2

From aldeid
Jump to navigation Jump to search
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:

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