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.