The Problem

I’m a bit late to the game, as this challenge was posted almost 7 months ago – but better late then never, right?

This was a very fun challenge. I initially looked at it a few weeks ago, but after a couple hours, I was unable to solve it. I decided to come back to it today with fresh eyes, and after about 40 minutes, I was able to solve it. You can download the binary at 0x00SEC. Here are the rules:

Hints
No hints... Hints are for wimps... there are no hints in real life...

Rules
Post at least 3 valid username/keys combination in the comments. Do not forget to use the spoiler tag

Write-ups are welcomed...

This is a keygen style binary. It asks for a username and a key. The key is based off of the username.

The Twisted Road to Despair
by p1co

Enter Username (min 10 char): AAAAAAAAAA
Enter Key: My_Key
Keep Trying
Binary MD5 Download Link
TRtD 77eac5b018e58cfe262244546cd633bb 0x00sec.org

The Solution

This “solution” is a bit dirty. I can generate an unlimited number of valid user/key combos, but in order for me to do so I need to run the binary in a debugger.

The first check takes the username and checks to see if it shorter than 10 bytes long. If the username is shorter than 10 bytes long, the program will print “Username too short…try again…” and then exits.

0x004001c2      4183fd09       cmp r13d, 9                           ; 9
0x004001c6      c6040400       mov byte [rsp + rax], 0
0x004001ca      7f16           jg 0x4001e2
0x004001cc      488b353d3e20.  mov rsi, qword [0x00604010]           ; [0x604010:8]=0x604020
0x004001d3      bfd92d4000     mov edi, str.Username_to_short..._try_again..._n ; 0x402dd9 ; "Username to short... try again...\n"

If the username is longer then 9 bytes long, we get taken to 0x4001e2.

   ; JMP XREF from 0x004001ca (fcn.00400150)
0x004001e2      4889e7         mov rdi, rsp
0x004001e5      e826010000     call fcn.00400310
0x004001ea      bffc2d4000     mov edi, str.Enter_Key:               ; 0x402dfc ; "Enter Key: "
0x004001ef      31c0           xor eax, eax
0x004001f1      e8390a0000     call fcn.00400c2f
0x004001f6      488b15733e20.  mov rdx, qword [0x00604070]           ; [0x604070:8]=0x604080
0x004001fd      be00040000     mov esi, 0x400                        ; 1024
0x00400202      4889ef         mov rdi, rbp
0x00400205      e83a080000     call fcn.00400a44
0x0040020a      4c89e1         mov rcx, r12
0x0040020d      4889ef         mov rdi, rbp
0x00400210      4488f0         mov al, r14b
0x00400213      f2ae           repne scasb al, byte [rdi]
0x00400215      4489ea         mov edx, r13d
0x00400218      4889e7         mov rdi, rsp
0x0040021b      4889ee         mov rsi, rbp
0x0040021e      48f7d1         not rcx
0x00400221      448d61fe       lea r12d, [rcx - 2]
0x00400225      4d63e4         movsxd r12, r12d
0x00400228      42c684240004.  mov byte [rsp + r12 + 0x400], 0
0x00400231      e84c000000     call fcn.00400282
0x00400236      85c0           test eax, eax
0x00400238      bf082e4000     mov edi, str.Well_Done__n             ; 0x402e08 ; "Well Done!\n"
0x0040023d      7405           je 0x400244

At the end of this code block (0x00400236) we can see “test eax, eax”. In order for the “Well Done!\n” message to be displayed (and not overwritten), EAX must be zero. Before this check, EAX gets set to either zero (good) or one (bad) in fcn.00400282.

┌ (fcn) fcn.00400282 141
│   fcn.00400282 ();
│    ; CALL XREF from 0x00400231 (fcn.00400150)
│ 0x00400282      4156           push r14
│ 0x00400284      31c0           xor eax, eax
│ 0x00400286      4883c9ff       or rcx, 0xffffffffffffffff
│ 0x0040028a      4155           push r13
│ 0x0040028c      4989fd         mov r13, rdi
│ 0x0040028f      4889f7         mov rdi, rsi
│ 0x00400292      4154           push r12
│ 0x00400294      4989f4         mov r12, rsi
│ 0x00400297      55             push rbp
│ 0x00400298      53             push rbx
│ 0x00400299      89d3           mov ebx, edx
│ 0x0040029b      4883ec10       sub rsp, 0x10
│ 0x0040029f      f2ae           repne scasb al, byte [rdi]
│ 0x004002a1      48f7d1         not rcx
│ 0x004002a4      8d41ff         lea eax, [rcx - 1]
│ 0x004002a7      b902000000     mov ecx, 2
│ 0x004002ac      99             cdq
│ 0x004002ad      f7f9           idiv ecx
│ 0x004002af      8d7801         lea edi, [rax + 1]                    ; 1
│ 0x004002b2      89c5           mov ebp, eax
│ 0x004002b4      4863ff         movsxd rdi, edi
│ 0x004002b7      e893010000     call fcn.0040044f
│ 0x004002bc      39dd           cmp ebp, ebx
│ 0x004002be      7539           jne 0x4002f9
│ 0x004002c0      4989c6         mov r14, rax
│ 0x004002c3      31db           xor ebx, ebx
│    ; JMP XREF from 0x004002f7 (fcn.00400282)
│ 0x004002c5      488d3c1b       lea rdi, [rbx + rbx]
│ 0x004002c9      4c01e7         add rdi, r12                          ; 'n'
│ 0x004002cc      39dd           cmp ebp, ebx
│ 0x004002ce      7e30           jle 0x400300
│ 0x004002d0      488d54240c     lea rdx, [rsp + 0xc]                  ; 12
│ 0x004002d5      31c0           xor eax, eax
│ 0x004002d7      be902d4000     mov esi, str._02x                     ; section..rodata ; 0x402d90 ; "%02x"
│ 0x004002dc      e8c8030000     call fcn.004006a9
│ 0x004002e1      8b54240c       mov edx, dword [rsp + 0xc]            ; [0xc:4]=-1 ; 12
│ 0x004002e5      410fbe441d00   movsx eax, byte [r13 + rbx]
│ 0x004002eb      4188141e       mov byte [r14 + rbx], dl
│ 0x004002ef      0fb6d2         movzx edx, dl
│ 0x004002f2      48ffc3         inc rbx
│ 0x004002f5      39c2           cmp edx, eax
│ 0x004002f7      74cc           je 0x4002c5
│    ; JMP XREF from 0x004002be (fcn.00400282)
│ 0x004002f9      b801000000     mov eax, 1
│ 0x004002fe      eb02           jmp 0x400302
│    ; JMP XREF from 0x004002ce (fcn.00400282)
│ 0x00400300      31c0           xor eax, eax
│    ; JMP XREF from 0x004002fe (fcn.00400282)
│ 0x00400302      4883c410       add rsp, 0x10
│ 0x00400306      5b             pop rbx
│ 0x00400307      5d             pop rbp
│ 0x00400308      415c           pop r12
│ 0x0040030a      415d           pop r13
│ 0x0040030c      415e           pop r14
└ 0x0040030e      c3             ret

First, this function checks the length of the key (0x004002bc). The key length must be 2*length of the username. If the length of the key is good, we then enter a loop at 0x004002c5. This loop compares each byte of the user inputted key to the correct key. Within the loop at 0x004002eb, if you check the value of “r13 + rbx,” the correct key will be shown. Here are three valid user/key combos:

ReversingIsFun!!!:566576617677656c6b49714473622d251f
CarlosWasHere:4f617260637755657f46697063
CoolChal!!:4f6b6d604f666d601f21

The Twisted Road to Despair
by p1co

Enter Username (min 10 char): CarlosWasHere
Enter Key: 4f617260637755657f46697063
Well Done!

The key is generated off of the username in fcn.00401946. This is a huge and complicated function that I have not yet completely reversed. I’d be curious to read any write-ups that show the process of reversing this function.

Overall, this was a really fun challenge and I enjoyed working through it.