SharifCTF: dMd
This was the first reverse engineering challenge from SharifCTF. It was a pretty straightforward 64-bit ELF binary, and despite being decently easy, was still pretty fun and a great first-level RE challenge.
Analyzing the Execution
If we dump the beginning of main
using objdump we can see it’s written in C++ and that it first prints out a message, takes our input, hahes it, and stores it in RAX
.
$ objdump -d -M intel dMd | grep -A30 main.:
0000000000400e8d <main>:
400e8d: 55 push rbp
400e8e: 48 89 e5 mov rbp,rsp
400e91: 53 push rbx
400e92: 48 83 ec 78 sub rsp,0x78
400e96: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28
400e9d: 00 00
400e9f: 48 89 45 e8 mov QWORD PTR [rbp-0x18],rax
400ea3: 31 c0 xor eax,eax
400ea5: be a8 28 40 00 mov esi,0x4028a8
400eaa: bf 60 42 60 00 mov edi,0x604260
400eaf: e8 0c fe ff ff call 400cc0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
400eb4: 48 8d 45 b0 lea rax,[rbp-0x50]
400eb8: 48 89 c6 mov rsi,rax
400ebb: bf 40 41 60 00 mov edi,0x604140
400ec0: e8 3b fe ff ff call 400d00 <_ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_PS3_@plt>
400ec5: 48 8d 45 8f lea rax,[rbp-0x71]
400ec9: 48 89 c7 mov rdi,rax
400ecc: e8 8f fe ff ff call 400d60 <_ZNSaIcEC1Ev@plt>
400ed1: 48 8d 55 8f lea rdx,[rbp-0x71]
400ed5: 48 8d 4d b0 lea rcx,[rbp-0x50]
400ed9: 48 8d 45 90 lea rax,[rbp-0x70]
400edd: 48 89 ce mov rsi,rcx
400ee0: 48 89 c7 mov rdi,rax
400ee3: e8 08 fe ff ff call 400cf0 <_ZNSsC1EPKcRKSaIcE@plt>
400ee8: 48 8d 45 a0 lea rax,[rbp-0x60]
400eec: 48 8d 55 90 lea rdx,[rbp-0x70]
400ef0: 48 89 d6 mov rsi,rdx
400ef3: 48 89 c7 mov rdi,rax
400ef6: e8 2b 16 00 00 call 402526 <_Z3md5Ss>
400efb: 48 8d 45 a0 lea rax,[rbp-0x60]
This gives us a pretty good idea of where this is heading, but let’s keep going just to be careful.
Looking ahead we can see a lot of comparisons happening between the bytes of our hashed input and some other hardcoded bytes:
...
400f2f: 48 8b 45 a8 mov rax,QWORD PTR [rbp-0x58]
400f33: 0f b6 00 movzx eax,BYTE PTR [rax]
400f36: 3c 37 cmp al,0x37
400f38: 0f 85 5d 03 00 00 jne 40129b <main+0x40e>
400f3e: 48 8b 45 a8 mov rax,QWORD PTR [rbp-0x58]
400f42: 48 83 c0 01 add rax,0x1
400f46: 0f b6 00 movzx eax,BYTE PTR [rax]
400f49: 3c 38 cmp al,0x38
400f4b: 0f 85 4a 03 00 00 jne 40129b <main+0x40e>
400f51: 48 8b 45 a8 mov rax,QWORD PTR [rbp-0x58]
400f55: 48 83 c0 02 add rax,0x2
400f59: 0f b6 00 movzx eax,BYTE PTR [rax]
400f5c: 3c 30 cmp al,0x30
400f5e: 0f 85 37 03 00 00 jne 40129b <main+0x40e>
400f64: 48 8b 45 a8 mov rax,QWORD PTR [rbp-0x58]
400f68: 48 83 c0 03 add rax,0x3
400f6c: 0f b6 00 movzx eax,BYTE PTR [rax]
400f6f: 3c 34 cmp al,0x34
400f71: 0f 85 24 03 00 00 jne 40129b <main+0x40e>
400f77: 48 8b 45 a8 mov rax,QWORD PTR [rbp-0x58]
...
Pulling out all of those bytes gives:
0x3738303433386435623665323964623038393862633466303232353933356330
Converting that back to ascii we get:
780438d5b6e29db0898bc4f0225935c0
That should be the md5 hash of our desired password. All we have to do is crack that hash and we sould have our flag. If we do a quick search online we find that what that hash is the md5x2 of the word grape
. An md5x2 is the md5 operation applied twice:
With that in mind if we hash the word grape
we should have the valid key, assuming no more checks were done.
Let’s Check:
$ echo $(echo -n "grape" | md5sum) | ./dMd
Enter the valid key!
The key is valid :)
It accepted our key! Our flag is: