Asis 2018 Quals Babyc
The goal of this challenge is just to find the first 14
characters of the correct input (a bit different, the flag was a hash of the first 14
characters).
Let's take a look at the binary:
$ file babyc
babyc: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, stripped
$ ./babyc
15935728
Wrong!
So it looks like we are dealing with a 32
bit crackme challenge that takes in input via stdin. A crackme challenge is one that takes in input, and checks if it is what it expects (and we have to figure out the correct input). Looking at the assembly code of the binary in Ghidra, it becomes apparant very quickly that this binary has been obfuscated:
08048343 a3 f0 5f MOV [DAT_081f5ff0],EAX
1f 08
08048348 89 15 f4 MOV dword ptr [DAT_081f5ff4],EDX
5f 1f 08
0804834e b8 00 00 MOV EAX,0x0
00 00
08048353 b9 00 00 MOV ECX,0x0
00 00
08048358 c7 05 00 MOV dword ptr [DAT_081f6000],0x0
60 1f 08
00 00 00 00
08048362 66 a1 f0 MOV AX,[DAT_081f5ff0]
5f 1f 08
08048368 66 8b 0d MOV CX,word ptr [DAT_081f5ff4]
f4 5f 1f 08
0804836f 8b 14 85 MOV EDX,dword ptr [PTR_DAT_08060f30 + EAX*0x4] = 080e0f34
30 0f 06 08
08048376 8b 14 8a MOV EDX,dword ptr [EDX + ECX*0x4]
08048379 66 8b 0d MOV CX,word ptr [DAT_081f6002]
02 60 1f 08
08048380 8b 14 95 MOV EDX,dword ptr [PTR_DAT_08060f30 + EDX*0x4] = 080e0f34
30 0f 06 08
08048387 8b 14 8a MOV EDX,dword ptr [EDX + ECX*0x4]
0804838a 66 89 15 MOV word ptr [DAT_081f5ff8],DX
f8 5f 1f 08
Specifically it has been obfuscated using Movfiscator, which is a compiler that obfuscates code by only using the mov
instruction. Starting off I tried to do a side channel attack with perf, however that didn't work here. After I tried using a tool called demovfuscator
(https://github.com/kirschju/demovfuscator) which is a tool designed to help reverse out movfuscated binaries. it can produce a graph showing the control flow through the program, and can even generate a binary from the movfuscated binary.
Let's run the tool to generate a patched version of the binary, and a graph:
$ ./demov -g char.dot -o demov_babyc babyc
and let's convert the .dot file to a pdf:
$ dot -Tpdf char.dot -o char.pdf
Looking at the graph char.pdf
, we see that it starts at 0x804899e
and ends at 0x804b97c
. In between that we can see there is a string of conditionals, which if any of them fail it will lead us to 0x804b5d0
. These conditionals are at these addresses:
0x8049853:
0x8049b26:
0x8049e50:
0x804a17a:
0x804a6fc:
Let's take a look at the code for the 0x8049853
conditional, we see this (this is from the demovfuscated patched binary):
Let's us objdump to view it:
$ objdump -D demov_babyc -M intel | less
Then we see this:
8049847: a1 e0 5f 1f 08 mov eax,ds:0x81f5fe0
804984c: 85 c0 test eax,eax
804984e: 90 nop
804984f: 90 nop
8049850: 90 nop
8049851: 90 nop
8049852: 90 nop
8049853: 0f 85 77 1d 00 00 jne 804b5d0 <strncmp@plt+0x3350>
So we can see that the comparison which determines if there is a jump is made at 0x804984c
. Let's see what the memory looks like there in gdb:
gef➤ b *0x804984c
Breakpoint 1 at 0x804984c
gef➤ r
Starting program: /Hackery/pod/modules/movfuscation/asis18_babyc/demov_babyc
15935728
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0x1
$ebx : 0xf7ffd000 → 0x00026f34
$ecx : 0x1
$edx : 0x0
$esp : 0x085f6124 → 0x085f6133 → "35728"
$ebp : 0x0
$esi : 0xffffd0fc → 0xffffd2de → "CLUTTER_IM_MODULE=xim"
$edi : 0x0804829c → mov DWORD PTR ds:0x83f6140, esp
$eip : 0x0804984c → test eax, eax
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x085f6124│+0x0000: 0x085f6133 → "35728" ← $esp
0x085f6128│+0x0004: 0x0804d036 → "m0vfu3c4t0r!"
0x085f612c│+0x0008: or al, 0x0
0x085f6130│+0x000c: "15935728"
0x085f6134│+0x0010: "5728"
0x085f6138│+0x0014: or al, BYTE PTR [eax]
0x085f613c│+0x0018: add BYTE PTR [eax], al
0x085f6140│+0x001c: add BYTE PTR [eax], al
──────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x804983e mov edx, DWORD PTR ds:0x804d07c
0x8049844 mov DWORD PTR [eax+0xc], edx
0x8049847 mov eax, ds:0x81f5fe0
→ 0x804984c test eax, eax
0x804984e nop
0x804984f nop
0x8049850 nop
0x8049851 nop
0x8049852 nop
──────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "demov_babyc", stopped, reason: BREAKPOINT
────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x804984c → test eax, eax
─────────────────────────────────────────────────────────────────────────────────────────────────────
Breakpoint 1, 0x0804984c in ?? ()
gef➤
So we can see that our input is on the stack, or more specifically our input after the first three characters. After that is the string m0vfu3c4t0r!
, which it is probably comparing our input after the first three characters to. When we input the string 012m0vfu3c4t0r!
we see that we pass this check which confirms our assumption.
The next check we have is at 0x8049b26
:
8049a71: a3 e0 5f 1f 08 mov ds:0x81f5fe0,eax
8049a76: a1 e0 5f 1f 08 mov eax,ds:0x81f5fe0
8049a7b: 8b 04 85 60 61 3f 08 mov eax,DWORD PTR [eax*4+0x83f6160]
8049a82: 8b 15 00 61 1f 08 mov edx,DWORD PTR ds:0x81f6100
8049a88: 89 10 mov DWORD PTR [eax],edx
8049a8a: 8b 0d e0 5f 1f 08 mov ecx,DWORD PTR ds:0x81f5fe0
8049a90: c7 05 74 61 3f 08 90 mov DWORD PTR ds:0x83f6174,0x85f6190
8049a97: 61 5f 08
8049a9a: 8b 04 8d 70 61 3f 08 mov eax,DWORD PTR [ecx*4+0x83f6170]
8049aa1: 8b 15 50 d0 04 08 mov edx,DWORD PTR ds:0x804d050
8049aa7: 89 10 mov DWORD PTR [eax],edx
8049aa9: 8b 15 54 d0 04 08 mov edx,DWORD PTR ds:0x804d054
8049aaf: 89 50 04 mov DWORD PTR [eax+0x4],edx
8049ab2: 8b 15 58 d0 04 08 mov edx,DWORD PTR ds:0x804d058
8049ab8: 89 50 08 mov DWORD PTR [eax+0x8],edx
8049abb: 8b 15 5c d0 04 08 mov edx,DWORD PTR ds:0x804d05c
8049ac1: 89 50 0c mov DWORD PTR [eax+0xc],edx
8049ac4: c7 05 74 61 3f 08 a0 mov DWORD PTR ds:0x83f6174,0x85f61a0
8049acb: 61 5f 08
8049ace: 8b 04 8d 70 61 3f 08 mov eax,DWORD PTR [ecx*4+0x83f6170]
8049ad5: 8b 15 60 d0 04 08 mov edx,DWORD PTR ds:0x804d060
8049adb: 89 10 mov DWORD PTR [eax],edx
8049add: 8b 15 64 d0 04 08 mov edx,DWORD PTR ds:0x804d064
8049ae3: 89 50 04 mov DWORD PTR [eax+0x4],edx
8049ae6: c7 05 74 61 3f 08 a8 mov DWORD PTR ds:0x83f6174,0x85f61a8
8049aed: 61 5f 08
8049af0: 8b 04 8d 70 61 3f 08 mov eax,DWORD PTR [ecx*4+0x83f6170]
8049af7: 8b 15 70 d0 04 08 mov edx,DWORD PTR ds:0x804d070
8049afd: 89 10 mov DWORD PTR [eax],edx
8049aff: 8b 15 74 d0 04 08 mov edx,DWORD PTR ds:0x804d074
8049b05: 89 50 04 mov DWORD PTR [eax+0x4],edx
8049b08: 8b 15 78 d0 04 08 mov edx,DWORD PTR ds:0x804d078
8049b0e: 89 50 08 mov DWORD PTR [eax+0x8],edx
8049b11: 8b 15 7c d0 04 08 mov edx,DWORD PTR ds:0x804d07c
8049b17: 89 50 0c mov DWORD PTR [eax+0xc],edx
8049b1a: a1 e0 5f 1f 08 mov eax,ds:0x81f5fe0
8049b1f: 85 c0 test eax,eax
8049b21: 90 nop
8049b22: 90 nop
8049b23: 90 nop
8049b24: 90 nop
8049b25: 90 nop
8049b26: 0f 85 ca 18 00 00 jne 804b3f6 <strncmp@plt+0x3176>
This might seem like a lot, however I set a breakpoint for 0x8049a71
and stepped through this code while watching the registers. While stepping through I noticed something interesting.
We see that the edx
register gets loaded with our first character:
gef➤ s
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0x085f6190 → add BYTE PTR [eax], al
$ebx : 0xf7ffd000 → 0x00026f34
$ecx : 0x1
$edx : 0x30
$esp : 0x085f6124 → 0x085f6133 → "m0vfu3c4t0r!"
$ebp : 0x0
$esi : 0xffffd0fc → 0xffffd2de → "CLUTTER_IM_MODULE=xim"
$edi : 0x0804829c → mov DWORD PTR ds:0x83f6140, esp
$eip : 0x08049aa7 → mov DWORD PTR [eax], edx
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x085f6124│+0x0000: 0x085f6133 → "m0vfu3c4t0r!" ← $esp
0x085f6128│+0x0004: 0x0804d036 → "m0vfu3c4t0r!"
0x085f612c│+0x0008: or al, 0x0
0x085f6130│+0x000c: "012m0vfu3c4t0r!"
0x085f6134│+0x0010: "0vfu3c4t0r!"
0x085f6138│+0x0014: "3c4t0r!"
0x085f613c│+0x0018: 0x0a217230 ("0r!"?)
0x085f6140│+0x001c: add BYTE PTR [eax], al
──────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8049a90 mov DWORD PTR ds:0x83f6174, 0x85f6190
0x8049a9a mov eax, DWORD PTR [ecx*4+0x83f6170]
0x8049aa1 mov edx, DWORD PTR ds:0x804d050
→ 0x8049aa7 mov DWORD PTR [eax], edx
0x8049aa9 mov edx, DWORD PTR ds:0x804d054
0x8049aaf mov DWORD PTR [eax+0x4], edx
0x8049ab2 mov edx, DWORD PTR ds:0x804d058
0x8049ab8 mov DWORD PTR [eax+0x8], edx
0x8049abb mov edx, DWORD PTR ds:0x804d05c
──────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "demov_babyc", stopped, reason: SINGLE STEP
────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8049aa7 → mov DWORD PTR [eax], edx
─────────────────────────────────────────────────────────────────────────────────────────────────────
0x08049aa7 in ?? ()
gef➤
Proceeding that, the edx
register gets loaded with the character A
(0x41
):
gef➤ s
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0x085f6190 → 0x00000030 ("0"?)
$ebx : 0xf7ffd000 → 0x00026f34
$ecx : 0x1
$edx : 0x41
$esp : 0x085f6124 → 0x085f6133 → "m0vfu3c4t0r!"
$ebp : 0x0
$esi : 0xffffd0fc → 0xffffd2de → "CLUTTER_IM_MODULE=xim"
$edi : 0x0804829c → mov DWORD PTR ds:0x83f6140, esp
$eip : 0x08049ab8 → mov DWORD PTR [eax+0x8], edx
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x085f6124│+0x0000: 0x085f6133 → "m0vfu3c4t0r!" ← $esp
0x085f6128│+0x0004: 0x0804d036 → "m0vfu3c4t0r!"
0x085f612c│+0x0008: or al, 0x0
0x085f6130│+0x000c: "012m0vfu3c4t0r!"
0x085f6134│+0x0010: "0vfu3c4t0r!"
0x085f6138│+0x0014: "3c4t0r!"
0x085f613c│+0x0018: 0x0a217230 ("0r!"?)
0x085f6140│+0x001c: add BYTE PTR [eax], al
──────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8049aa9 mov edx, DWORD PTR ds:0x804d054
0x8049aaf mov DWORD PTR [eax+0x4], edx
0x8049ab2 mov edx, DWORD PTR ds:0x804d058
→ 0x8049ab8 mov DWORD PTR [eax+0x8], edx
0x8049abb mov edx, DWORD PTR ds:0x804d05c
0x8049ac1 mov DWORD PTR [eax+0xc], edx
0x8049ac4 mov DWORD PTR ds:0x83f6174, 0x85f61a0
0x8049ace mov eax, DWORD PTR [ecx*4+0x83f6170]
0x8049ad5 mov edx, DWORD PTR ds:0x804d060
──────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "demov_babyc", stopped, reason: SINGLE STEP
────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8049ab8 → mov DWORD PTR [eax+0x8], edx
─────────────────────────────────────────────────────────────────────────────────────────────────────
0x08049ab8 in ?? ()
gef➤
From this, I decided to see if it was checking if the first character was A
. After trying the string A12m0vfu3c4t0r!
I saw that we passed this check, so our assumption was correct. Turns out there are just two last checks that we need to worry about, which are here:
0x8049e50: starts at 0x8049d9b
0x804a17a: starts at 0x804a0c5
The process of figuring out what characters they are checking for is exactly the same as with the first character. With that, we can figure out that the first three character it is checking for is Ah_
. THat leaves us with the string Ah_m0vfu3c4t0r!
, which is the first 14
characters of the string, so we have what we need to make the hash for the flag.