Are you able to cheat me and get the flag?
Enumeration
We can run file against the binary and find that this is a 64 bit ELF Binary:
impossible_password.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.32, BuildID[sha1]=ba116ba1912a8c3779ddeb579404e2fdf34b1568, stripped
We can run strings too to see if we can find anything interesting:
We see SuperSeKretKey
, let’s run the binary and see what happens:
We’re given ta different output, we’re also given the opportunity to enter further characters. Let’s reverse this binary and see what we can find:
; DATA XREF from entry0 @ 0x4006bd
┌ 283: int main (int argc, char **argv);
│ ; var char **var_50h @ rbp-0x50
│ ; var int64_t var_44h @ rbp-0x44
│ ; var int64_t var_40h @ rbp-0x40
│ ; var int64_t var_3fh @ rbp-0x3f
│ ; var int64_t var_3eh @ rbp-0x3e
│ ; var int64_t var_3dh @ rbp-0x3d
│ ; var int64_t var_3ch @ rbp-0x3c
│ ; var int64_t var_3bh @ rbp-0x3b
│ ; var int64_t var_3ah @ rbp-0x3a
│ ; var int64_t var_39h @ rbp-0x39
│ ; var int64_t var_38h @ rbp-0x38
│ ; var int64_t var_37h @ rbp-0x37
│ ; var int64_t var_36h @ rbp-0x36
│ ; var int64_t var_35h @ rbp-0x35
│ ; var int64_t var_34h @ rbp-0x34
│ ; var int64_t var_33h @ rbp-0x33
│ ; var int64_t var_32h @ rbp-0x32
│ ; var int64_t var_31h @ rbp-0x31
│ ; var int64_t var_30h @ rbp-0x30
│ ; var int64_t var_2fh @ rbp-0x2f
│ ; var int64_t var_2eh @ rbp-0x2e
│ ; var int64_t var_2dh @ rbp-0x2d
│ ; var char *s1 @ rbp-0x20
│ ; var uint32_t var_ch @ rbp-0xc
│ ; var char *s2 @ rbp-0x8
│ ; arg int argc @ rdi
│ ; arg char **argv @ rsi
│ 0x0040085d 55 push rbp
│ 0x0040085e 4889e5 mov rbp, rsp
│ 0x00400861 4883ec50 sub rsp, 0x50
│ 0x00400865 897dbc mov dword [var_44h], edi ; argc
│ 0x00400868 488975b0 mov qword [var_50h], rsi ; argv
│ 0x0040086c 48c745f8700a. mov qword [s2], str.SuperSeKretKey ; 0x400a70 ; "SuperSeKretKey"
│ 0x00400874 c645c041 mov byte [var_40h], 0x41 ; 'A' ; 65
│ 0x00400878 c645c15d mov byte [var_3fh], 0x5d ; ']' ; 93
│ 0x0040087c c645c24b mov byte [var_3eh], 0x4b ; 'K' ; 75
│ 0x00400880 c645c372 mov byte [var_3dh], 0x72 ; 'r' ; 114
│ 0x00400884 c645c43d mov byte [var_3ch], 0x3d ; '=' ; 61
│ 0x00400888 c645c539 mov byte [var_3bh], 0x39 ; '9' ; 57
│ 0x0040088c c645c66b mov byte [var_3ah], 0x6b ; 'k' ; 107
│ 0x00400890 c645c730 mov byte [var_39h], 0x30 ; '0' ; 48
│ 0x00400894 c645c83d mov byte [var_38h], 0x3d ; '=' ; 61
│ 0x00400898 c645c930 mov byte [var_37h], 0x30 ; '0' ; 48
│ 0x0040089c c645ca6f mov byte [var_36h], 0x6f ; 'o' ; 111
│ 0x004008a0 c645cb30 mov byte [var_35h], 0x30 ; '0' ; 48
│ 0x004008a4 c645cc3b mov byte [var_34h], 0x3b ; ';' ; 59
│ 0x004008a8 c645cd6b mov byte [var_33h], 0x6b ; 'k' ; 107
│ 0x004008ac c645ce31 mov byte [var_32h], 0x31 ; '1' ; 49
│ 0x004008b0 c645cf3f mov byte [var_31h], 0x3f ; '?' ; 63
│ 0x004008b4 c645d06b mov byte [var_30h], 0x6b ; 'k' ; 107
│ 0x004008b8 c645d138 mov byte [var_2fh], 0x38 ; '8' ; 56
│ 0x004008bc c645d231 mov byte [var_2eh], 0x31 ; '1' ; 49
│ 0x004008c0 c645d374 mov byte [var_2dh], 0x74 ; 't' ; 116
│ 0x004008c4 bf7f0a4000 mov edi, 0x400a7f ; const char *format
│ 0x004008c9 b800000000 mov eax, 0
│ 0x004008ce e82dfdffff call sym.imp.printf ; int printf(const char *format)
│ 0x004008d3 488d45e0 lea rax, [s1]
│ 0x004008d7 4889c6 mov rsi, rax
│ 0x004008da bf820a4000 mov edi, str._20s ; 0x400a82 ; "%20s" ; const char *format
│ 0x004008df b800000000 mov eax, 0
│ 0x004008e4 e887fdffff call sym.imp.__isoc99_scanf ; int scanf(const char *format)
│ 0x004008e9 488d45e0 lea rax, [s1]
│ 0x004008ed 4889c6 mov rsi, rax
│ 0x004008f0 bf870a4000 mov edi, str.__s__n ; 0x400a87 ; "[%s]\n" ; const char *format
│ 0x004008f5 b800000000 mov eax, 0
│ 0x004008fa e801fdffff call sym.imp.printf ; int printf(const char *format)
│ 0x004008ff 488b55f8 mov rdx, qword [s2]
│ 0x00400903 488d45e0 lea rax, [s1]
│ 0x00400907 4889d6 mov rsi, rdx ; const char *s2
│ 0x0040090a 4889c7 mov rdi, rax ; const char *s1
│ 0x0040090d e81efdffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2)
│ 0x00400912 8945f4 mov dword [var_ch], eax
│ 0x00400915 837df400 cmp dword [var_ch], 0
│ ┌─< 0x00400919 740a je 0x400925
│ │ 0x0040091b bf01000000 mov edi, 1 ; int status
│ │ 0x00400920 e85bfdffff call sym.imp.exit ; void exit(int status)
│ │ ; CODE XREF from main @ 0x400919
│ └─> 0x00400925 bf8d0a4000 mov edi, 0x400a8d ; const char *format
│ 0x0040092a b800000000 mov eax, 0
│ 0x0040092f e8ccfcffff call sym.imp.printf ; int printf(const char *format)
│ 0x00400934 488d45e0 lea rax, [s1]
│ 0x00400938 4889c6 mov rsi, rax
│ 0x0040093b bf820a4000 mov edi, str._20s ; 0x400a82 ; "%20s" ; const char *format
│ 0x00400940 b800000000 mov eax, 0
│ 0x00400945 e826fdffff call sym.imp.__isoc99_scanf ; int scanf(const char *format)
│ 0x0040094a bf14000000 mov edi, 0x14 ; 20 ; size_t arg1
│ 0x0040094f e839feffff call fcn.0040078d
│ 0x00400954 4889c2 mov rdx, rax
│ 0x00400957 488d45e0 lea rax, [s1]
│ 0x0040095b 4889d6 mov rsi, rdx ; const char *s2
│ 0x0040095e 4889c7 mov rdi, rax ; const char *s1
│ 0x00400961 e8cafcffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2)
│ 0x00400966 85c0 test eax, eax
│ ┌─< 0x00400968 750c jne 0x400976
│ │ 0x0040096a 488d45c0 lea rax, [var_40h]
│ │ 0x0040096e 4889c7 mov rdi, rax ; int64_t arg1
│ │ 0x00400971 e802000000 call fcn.00400978
│ │ ; CODE XREF from main @ 0x400968
│ └─> 0x00400976 c9 leave
└ 0x00400977 c3 ret
We have a lot of declerations being used to assigned characters and integer values to variables. We already know how to get around the first string comparison however the 2nd comparison is a little more difficult. We can check main and see that there’s an unconditional jump to 0040078d
, this function contains:
; CALL XREF from main @ 0x40094f
┌ 208: fcn.0040078d (size_t arg1);
│ ; var size_t size @ rbp-0x24
│ ; var int64_t var_1ch @ rbp-0x1c
│ ; var int64_t var_18h @ rbp-0x18
│ ; var int64_t var_14h @ rbp-0x14
│ ; var void *var_10h @ rbp-0x10
│ ; var int64_t var_4h @ rbp-0x4
│ ; arg size_t arg1 @ rdi
│ 0x0040078d 55 push rbp
│ 0x0040078e 4889e5 mov rbp, rsp
│ 0x00400791 4883ec30 sub rsp, 0x30
│ 0x00400795 897ddc mov dword [size], edi ; arg1
│ 0x00400798 48c745f00000. mov qword [var_10h], 0
│ 0x004007a0 c745ec7e0000. mov dword [var_14h], 0x7e ; '~' ; 126
│ 0x004007a7 c745e8210000. mov dword [var_18h], 0x21 ; '!' ; 33
│ 0x004007ae bf00000000 mov edi, 0 ; time_t *timer
│ 0x004007b3 e898feffff call sym.imp.time ; time_t time(time_t *timer)
│ 0x004007b8 89c2 mov edx, eax
│ 0x004007ba 8b45dc mov eax, dword [size]
│ 0x004007bd 0fafd0 imul edx, eax
│ 0x004007c0 8b05ae082000 mov eax, dword [0x00601074] ; [0x601074:4]=0x17da710
│ 0x004007c6 83c001 add eax, 1
│ 0x004007c9 8905a5082000 mov dword [0x00601074], eax ; [0x601074:4]=0x17da710
│ 0x004007cf 8b059f082000 mov eax, dword [0x00601074] ; [0x601074:4]=0x17da710
│ 0x004007d5 01d0 add eax, edx
│ 0x004007d7 89c7 mov edi, eax ; int seed
│ 0x004007d9 e842feffff call sym.imp.srand ; void srand(int seed)
│ 0x004007de 8b45dc mov eax, dword [size]
│ 0x004007e1 83c001 add eax, 1
│ 0x004007e4 4898 cdqe
│ 0x004007e6 4889c7 mov rdi, rax ; size_t size
│ 0x004007e9 e872feffff call sym.imp.malloc ; void *malloc(size_t size)
│ 0x004007ee 488945f0 mov qword [var_10h], rax
│ 0x004007f2 48837df000 cmp qword [var_10h], 0
│ ┌─< 0x004007f7 7458 je 0x400851
│ │ 0x004007f9 c745fc000000. mov dword [var_4h], 0
│ ┌──< 0x00400800 eb31 jmp 0x400833
│ ││ ; CODE XREF from fcn.0040078d @ 0x400839
│ ┌───> 0x00400802 e889feffff call sym.imp.rand ; int rand(void)
│ ╎││ 0x00400807 8b55ec mov edx, dword [var_14h]
│ ╎││ 0x0040080a 83c201 add edx, 1
│ ╎││ 0x0040080d 89d1 mov ecx, edx
│ ╎││ 0x0040080f 2b4de8 sub ecx, dword [var_18h]
│ ╎││ 0x00400812 99 cdq
│ ╎││ 0x00400813 f7f9 idiv ecx
│ ╎││ 0x00400815 8b45e8 mov eax, dword [var_18h]
│ ╎││ 0x00400818 01d0 add eax, edx
│ ╎││ 0x0040081a 8945e4 mov dword [var_1ch], eax
│ ╎││ 0x0040081d 8b45fc mov eax, dword [var_4h]
│ ╎││ 0x00400820 4863d0 movsxd rdx, eax
│ ╎││ 0x00400823 488b45f0 mov rax, qword [var_10h]
│ ╎││ 0x00400827 4801c2 add rdx, rax
│ ╎││ 0x0040082a 8b45e4 mov eax, dword [var_1ch]
│ ╎││ 0x0040082d 8802 mov byte [rdx], al
│ ╎││ 0x0040082f 8345fc01 add dword [var_4h], 1
│ ╎││ ; CODE XREF from fcn.0040078d @ 0x400800
│ ╎└──> 0x00400833 8b45fc mov eax, dword [var_4h]
│ ╎ │ 0x00400836 3b45dc cmp eax, dword [size]
│ └───< 0x00400839 7cc7 jl 0x400802
│ │ 0x0040083b 8b45dc mov eax, dword [size]
│ │ 0x0040083e 4863d0 movsxd rdx, eax
│ │ 0x00400841 488b45f0 mov rax, qword [var_10h]
│ │ 0x00400845 4801d0 add rax, rdx
│ │ 0x00400848 c60000 mov byte [rax], 0
│ │ 0x0040084b 488b45f0 mov rax, qword [var_10h]
│ ┌──< 0x0040084f eb0a jmp 0x40085b
│ ││ ; CODE XREF from fcn.0040078d @ 0x4007f7
│ │└─> 0x00400851 bf01000000 mov edi, 1 ; int status
│ │ 0x00400856 e825feffff call sym.imp.exit ; void exit(int status)
│ │ ; CODE XREF from fcn.0040078d @ 0x40084f
│ └──> 0x0040085b c9 leave
└ 0x0040085c c3 ret
Solve
We don’t really have any reason to reverse this and find what it does, all we need to know is that it’s generating randomly strings seeded by the time function. Fortunately, using radare2 we can patch this comparison out of the binary and continue down the path of execution. If we look back to our main function, we have a condition jump:
│ 0x00400966 85c0 test eax, eax
│ ┌─< 0x00400968 750c jne 0x400976
We’ll replace this with a “nop” instruction:
This over-writes our instruction at 0x00400968
with “nop”, meaning no operation will be performed and we can continue down our line of execution. After doing so, we can save and re-run the binary: