[Reverse] 34C3ctf - m0rph


m0rph 49 Solves: 163

To get you started :)

files: Link

difficulty: easy

Opening the binary in IDA:

As we can see mmap is being used to reserve a place in memory with read,write and execute permissions, by knowing this we can expect that some shellcode is going to be inserted in some place in the memory and then executed. The shellcode is being inserted in the final for loop that you can see in the image above, but in each run, this mini shellcodes are going to be executed in a different order because they are being randomized in sub_55C4675D7987. This isn’t a problem we can use radare2 to check this shellcode on each loop iteration, by reading the shellcode we can check which byte is being checked in cmp instructions.

The commands I used in radare2 to this were the following:

1
2
3
4
5
6
7
8
ood argv1 # starts the binary with argv1
pdf @ main # disassembles the main function
db # setting breakpoints
dc # continue
pd 4@rax # to disassemble the first 4 instructions before call rax
dr # show register values
dr rax # show a specific value of a register in this case rax
dr rax = 0x1 # modifies the value of rax in this case to 0x1

Since pie protection is enabled the addresses to breakpoint will be different in each debug running attempt, to circumvent this in my python script I’m disassembling the main function and parsing the code to get this addresses. Since we don’t know the flag yet on each byte checks we can’t forget to update in the argv1 string the correct bytes otherwise (before cmp), if the check goes incorrect the shellcode will call syscall_exit and terminates.

My radare2 script to do this (r2pipe):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import r2pipe
import re

def write_lowest_byte(n, b):
return n & 0xffffffffffffff00 | b

flag_begin = '34C3_'
flag = flag_begin + 'A'*(23-len(flag_begin))

r2=r2pipe.open('./morph',flags=['-2'])
r2.cmd("ood %s" % flag)
r2.cmd("aa")
source_main = r2.cmd("pdf @ main")
bp_lines = [line for line in source_main.split('\n') if "call rax" in line]
bps = [re.search(r'0x[0-9a-f]+', bp).group(0) for bp in bp_lines]
for bp in bps:
r2.cmd('db %s'%bp)
r2.cmd("dc")
flag_bytes = []
for i in range(23):
code = r2.cmd("pd 4@rax")
line = code.split('\n')[-1]
finds = re.findall(r'0x[0-9a-f]+', line)
cmp_b = finds[0]
flag_byte = finds[1]
t = (int(flag_byte,16), int(r2.cmd("dr rax"), 16))
flag_bytes.append(t)
r2.cmd("db %s" % cmp_b)
r2.cmd("dc")
address = int(r2.cmd("dr rax"), 16)
r2.cmd("dr rax = %s" % hex(write_lowest_byte(address, int(flag_byte,16))))
r2.cmd("dc")
flag = ''
flag_bytes = sorted(flag_bytes,key=lambda tup: tup[1])
print ''.join([chr(t[0]) for t in flag_bytes])
r2.quit()

If you putted a breakpoint before the end, you could check the full shellcode with radare2 by using this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
[0x55769853cb95]> pd 140@ 0x7f3e3151b000
;-- rcx:
;-- rsi:
0x7f3e3151b000 56 push rsi
0x7f3e3151b001 52 push rdx
0x7f3e3151b002 8a07 mov al, byte [rdi]
0x7f3e3151b004 3c33 cmp al, 0x33 ; '3' ; 51
,=< 0x7f3e3151b006 0f85db020000 jne 0x7f3e3151b2e7
,==< 0x7f3e3151b00c e9b8020000 jmp 0x7f3e3151b2c9
|| 0x7f3e3151b011 56 push rsi
|| 0x7f3e3151b012 52 push rdx
|| 0x7f3e3151b013 8a07 mov al, byte [rdi]
|| 0x7f3e3151b015 3c34 cmp al, 0x34 ; '4' ; 52
,===< 0x7f3e3151b017 0f85ca020000 jne 0x7f3e3151b2e7
,====< 0x7f3e3151b01d e9a7020000 jmp 0x7f3e3151b2c9
|||| 0x7f3e3151b022 56 push rsi
|||| 0x7f3e3151b023 52 push rdx
|||| 0x7f3e3151b024 8a07 mov al, byte [rdi]
|||| 0x7f3e3151b026 3c43 cmp al, 0x43 ; 'C' ; 67
,=====< 0x7f3e3151b028 0f85b9020000 jne 0x7f3e3151b2e7
,======< 0x7f3e3151b02e e996020000 jmp 0x7f3e3151b2c9
|||||| 0x7f3e3151b033 56 push rsi
|||||| 0x7f3e3151b034 52 push rdx
|||||| 0x7f3e3151b035 8a07 mov al, byte [rdi]
|||||| 0x7f3e3151b037 3c33 cmp al, 0x33 ; '3' ; 51
,=======< 0x7f3e3151b039 0f85a8020000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b03f e985020000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b044 56 push rsi
||||||| 0x7f3e3151b045 52 push rdx
||||||| 0x7f3e3151b046 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b048 3c5f cmp al, 0x5f ; '_' ; 95
========< 0x7f3e3151b04a 0f8597020000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b050 e974020000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b055 56 push rsi
||||||| 0x7f3e3151b056 52 push rdx
||||||| 0x7f3e3151b057 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b059 3c4d cmp al, 0x4d ; 'M' ; 77
========< 0x7f3e3151b05b 0f8586020000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b061 e963020000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b066 56 push rsi
||||||| 0x7f3e3151b067 52 push rdx
||||||| 0x7f3e3151b068 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b06a 3c31 cmp al, 0x31 ; '1' ; 49
========< 0x7f3e3151b06c 0f8575020000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b072 e952020000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b077 56 push rsi
||||||| 0x7f3e3151b078 52 push rdx
||||||| 0x7f3e3151b079 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b07b 3c47 cmp al, 0x47 ; 'G' ; 71
========< 0x7f3e3151b07d 0f8564020000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b083 e941020000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b088 56 push rsi
||||||| 0x7f3e3151b089 52 push rdx
||||||| 0x7f3e3151b08a 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b08c 3c48 cmp al, 0x48 ; 'H' ; 72
========< 0x7f3e3151b08e 0f8553020000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b094 e930020000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b099 56 push rsi
||||||| 0x7f3e3151b09a 52 push rdx
||||||| 0x7f3e3151b09b 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b09d 3c54 cmp al, 0x54 ; 'T' ; 84
========< 0x7f3e3151b09f 0f8542020000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b0a5 e91f020000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b0aa 56 push rsi
||||||| 0x7f3e3151b0ab 52 push rdx
||||||| 0x7f3e3151b0ac 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b0ae 3c59 cmp al, 0x59 ; 'Y' ; 89
========< 0x7f3e3151b0b0 0f8531020000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b0b6 e90e020000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b0bb 56 push rsi
||||||| 0x7f3e3151b0bc 52 push rdx
||||||| 0x7f3e3151b0bd 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b0bf 3c5f cmp al, 0x5f ; '_' ; 95
========< 0x7f3e3151b0c1 0f8520020000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b0c7 e9fd010000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b0cc 56 push rsi
||||||| 0x7f3e3151b0cd 52 push rdx
||||||| 0x7f3e3151b0ce 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b0d0 3c4d cmp al, 0x4d ; 'M' ; 77
========< 0x7f3e3151b0d2 0f850f020000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b0d8 e9ec010000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b0dd 56 push rsi
||||||| 0x7f3e3151b0de 52 push rdx
||||||| 0x7f3e3151b0df 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b0e1 3c30 cmp al, 0x30 ; '0' ; 48
========< 0x7f3e3151b0e3 0f85fe010000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b0e9 e9db010000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b0ee 56 push rsi
||||||| 0x7f3e3151b0ef 52 push rdx
||||||| 0x7f3e3151b0f0 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b0f2 3c52 cmp al, 0x52 ; 'R' ; 82
========< 0x7f3e3151b0f4 0f85ed010000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b0fa e9ca010000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b0ff 56 push rsi
||||||| 0x7f3e3151b100 52 push rdx
||||||| 0x7f3e3151b101 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b103 3c50 cmp al, 0x50 ; 'P' ; 80
========< 0x7f3e3151b105 0f85dc010000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b10b e9b9010000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b110 56 push rsi
||||||| 0x7f3e3151b111 52 push rdx
||||||| 0x7f3e3151b112 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b114 3c68 cmp al, 0x68 ; 'h' ; 104
========< 0x7f3e3151b116 0f85cb010000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b11c e9a8010000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b121 56 push rsi
||||||| 0x7f3e3151b122 52 push rdx
||||||| 0x7f3e3151b123 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b125 3c31 cmp al, 0x31 ; '1' ; 49
========< 0x7f3e3151b127 0f85ba010000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b12d e997010000 jmp 0x7f3e3151b2c9
||||||| ;-- rax:
||||||| 0x7f3e3151b132 56 push rsi
||||||| 0x7f3e3151b133 52 push rdx
||||||| 0x7f3e3151b134 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b136 3c6e cmp al, 0x6e ; 'n' ; 110
========< 0x7f3e3151b138 0f85a9010000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b13e e986010000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b143 56 push rsi
||||||| 0x7f3e3151b144 52 push rdx
||||||| 0x7f3e3151b145 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b147 3c47 cmp al, 0x47 ; 'G' ; 71
========< 0x7f3e3151b149 0f8598010000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b14f e975010000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b154 56 push rsi
||||||| 0x7f3e3151b155 52 push rdx
||||||| 0x7f3e3151b156 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b158 3c5f cmp al, 0x5f ; '_' ; 95
========< 0x7f3e3151b15a 0f8587010000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b160 e964010000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b165 56 push rsi
||||||| 0x7f3e3151b166 52 push rdx
||||||| 0x7f3e3151b167 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b169 3c67 cmp al, 0x67 ; 'g' ; 103
========< 0x7f3e3151b16b 0f8576010000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b171 e953010000 jmp 0x7f3e3151b2c9
||||||| 0x7f3e3151b176 56 push rsi
||||||| 0x7f3e3151b177 52 push rdx
||||||| 0x7f3e3151b178 8a07 mov al, byte [rdi]
||||||| 0x7f3e3151b17a 3c30 cmp al, 0x30 ; '0' ; 48
========< 0x7f3e3151b17c 0f8565010000 jne 0x7f3e3151b2e7
========< 0x7f3e3151b182 e942010000 jmp 0x7f3e3151b2c9

As you can you can see you can already check the flag and all comparisons that were being made, in my python script I was saving the addresses where each mini shellcode starts, so I could sort the byte order to get the right flag in the end:

1
2
$ python morph.py
34C3_M1GHTY_M0RPh1nG_g0