x32 Binary

Aperi'CTF 2019 - Reverse (250 pts).

Aperi’CTF 2019: x32 Binary

Challenge details

Event Challenge Category Points Solves
Aperi’CTF 2019 x32 Binary Reverse 250 8

Chez ENO.corp, ils sont tellement fiers de leur microcontrôleur qu’ils en ont mis partout ! Vous avez mis la main sur le firmware du microcontrôleur qui contrôle l’accès à la salle des serveurs. Votre mission est de retrouver le mot de passe attendu par celui-ci et pénétrer dans cette salle !

Fichier : binary.bin - md5sum: a9452cffaeb90f0461158ab17e20c351

Methodology

The provided file is just x32 bytecode. This challenge is meant to be solved after having solved the x32 Emulator challenge.

General idea

Use the emulator you wrote for the x32 Emulator challenge to run the binary (misc). If you haven’t already, you should read the write-up for this challenge before continuing.

You will need to modify slightly your emulator IO so you can interact with the binary. Printing the currently executed x32 instruction with it’s parameters will server as some sort of debugger and is very helpful for solving this task.

The modified Emulator class can be found in emulator.py.

Solution

runner.py is the script that will allow us to interact with the binary using our emulator. Let’s see what it looks like.

runner.py:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author: ENOENT

from emulator import Emulator

if __name__ == "__main__":
    compiled = open("binary.bin", "rb").read()
    try:
        emu = Emulator(compiled, debug=True)
        emu.run()
    except Exception as inst:
        x, y = inst.args
        print("{} : {}".format(x,y))
python3 runner.py
0 : SET A1 17
3 : SUB SP A1
5 : SET R4 SP
7 : IN R4

The execution is stopped at this point because an input is needed (IN instruction), the password obviously. From A1 we know that 17 bytes are needed, thus the password must be of this size.

Fill the buffer with recognizable data and see what happens.

AAAAAAAAAAAAAAAAA
9 : PUSH 10
12 : PUSH 33
15 : PUSH 32
18 : PUSH 68
21 : PUSH 79
24 : PUSH 79
27 : PUSH 71
30 : PUSH 10
33 : PUSH 33
36 : PUSH 32
39 : PUSH 68
42 : PUSH 65
45 : PUSH 66
48 : SET R3 0
51 : SET R2 R4
53 : ADD R2 1
56 : LOAD R1 R2
58 : XOR R1 152
61 : SUB R1 6
64 : CMP R1 194
67 : JE 73
70 : ADD R3 1
73 : SET R2 R4
75 : ADD R2 3
78 : LOAD R1 R2
80 : ADD R1 114
83 : XOR R1 103
86 : CMP R1 218
89 : JE 95
92 : ADD R3 1
95 : SET R2 R4
97 : LOAD R1 R2
99 : SUB R1 65
102 : JE 108
108 : SET R2 R4
110 : ADD R2 2
113 : LOAD R1 R2
115 : ADD R1 65
118 : CMP R1 147
121 : JE 127
124 : ADD R3 1
127 : SET R2 R4
129 : ADD R2 8
132 : LOAD R1 R2
134 : ADD R1 30
137 : XOR R1 58
140 : CMP R1 71
143 : JE 149
146 : ADD R3 1
149 : SET R2 R4
151 : ADD R2 10
154 : LOAD R1 R2
156 : ADD R1 101
159 : XOR R1 35
162 : CMP R1 187
165 : JE 171
168 : ADD R3 1
171 : SET R2 R4
173 : ADD R2 14
176 : LOAD R1 R2
178 : ADD R1 101
181 : XOR R1 35
184 : CMP R1 182
187 : JE 193
190 : ADD R3 1
193 : SET R2 R4
195 : ADD R2 16
198 : LOAD R1 R2
200 : SUB R1 30
203 : XOR R1 200
206 : CMP R1 151
209 : JE 215
212 : ADD R3 1
215 : SET R2 R4
217 : ADD R2 12
220 : LOAD R1 R2
222 : SUB R1 21
225 : XOR R1 239
228 : CMP R1 165
231 : JE 237
234 : ADD R3 1
237 : SET R2 R4
239 : ADD R2 5
242 : LOAD R1 R2
244 : SUB R1 8
247 : XOR R1 35
250 : CMP R1 114
253 : JE 259
256 : ADD R3 1
259 : SET R2 R4
261 : ADD R2 9
264 : LOAD R1 R2
266 : ADD R1 30
269 : XOR R1 58
272 : CMP R1 172
275 : JE 281
278 : ADD R3 1
281 : SET R2 R4
283 : ADD R2 4
286 : LOAD R1 R2
288 : SUB R1 35
291 : XOR R1 135
294 : CMP R1 223
297 : JE 303
300 : ADD R3 1
303 : SET R2 R4
305 : ADD R2 15
308 : LOAD R1 R2
310 : ADD R1 30
313 : XOR R1 58
316 : CMP R1 184
319 : JE 325
322 : ADD R3 1
325 : SET R2 R4
327 : ADD R2 6
330 : LOAD R1 R2
332 : SUB R1 21
335 : XOR R1 227
338 : CMP R1 248
341 : JE 347
344 : ADD R3 1
347 : SET R2 R4
349 : ADD R2 11
352 : LOAD R1 R2
354 : SUB R1 21
357 : XOR R1 239
360 : CMP R1 242
363 : JE 369
366 : ADD R3 1
369 : SET R2 R4
371 : ADD R2 7
374 : LOAD R1 R2
376 : ADD R1 101
379 : XOR R1 239
382 : CMP R1 53
385 : JE 391
388 : ADD R3 1
391 : SET R2 R4
393 : ADD R2 13
396 : LOAD R1 R2
398 : SUB R1 78
401 : XOR R1 138
404 : CMP R1 147
407 : JE 413
410 : ADD R3 1
413 : CMP R3 0
416 : JE 432
419 : SET R1 R4
421 : SUB R1 13
424 : SET A1 6
427 : OUT R1
BAD !
429 : GOTO 445

The first few PUSH instructions construct a string on the stack : "BAD !\nGOOD !\n"

At the end of execution one of them is printed.

R4 contains a pointer to our input string. We can recognize patterns that load a byte at a certain offset from R4, perform a XOR operation with a constant and subtract or add another constant before comparing to a specific value. If the condition is not satisfied, R3 is incremented and at the end, R3 is expected to be 0 for the password to be valid.

if (password[1] ^ 152) - 6 != 194:
    R3 += 1
if (password[3] + 114) ^ 103 != 218:
    R3 += 1
if password[0] - 65 == 0:
    R3 += 1
...

if R3 == 0:
    print("GOOD !")
else:
    print("BAD !")

By extracting all the conditions we can easily recover the expected password.

Flag

APRK{Y0u_x32_g0d}

ENOENT