Writeup: CSAW Quals 2019 - Beleaf

Information

  • category : reverse
  • points : 50

Description

tree sounds are best listened to by https://binary.ninja/demo or ghidra

Files : beleaf

Writeup

We don’t want to listen to the description because we can use the powerful cutter with the new plugin r2ghidra-dec.

After analyzing the binary with cutter and renaming some variables the main looks like this:

Our scope is to create an input (flag) which prints "correct". As we can see the first condition to bypass is the flag’s length that must be 0x21 = 33 or the program will execute puts(0xa7b) = "incorrect".

After the first condition we have a while that continues until i < 33.

However there’s an if statement where the program check that checker(flag[i]) == check_array[i], if this condition doesn’t hold than the program prints "incorrect" and exits.

In this case check_array is an array of long int as described in the screen (i * 8), so I opened up a new hexdump view in the address of check_array and I parsed the 33 long int into an array.

We can copy the array in a python script that later we’re gonna use and with nvim remove the lasts characters ULL –> :s/ULL//g.

Now we need to investigate what the checker function does.

The ghidra’s decompiler in cutter parse the controls array in graph view as "w" (I will open an issue on that).
Now we can rewrite the function in pseudocode :

1
2
3
4
5
6
7
8
i = 0;
while(i != -1 and int(arg1) != int(controls[i]))
if int(arg1) < int(controls[i])
i = i * 2 + 1
else
i = (i + 1) * 2

return i

Ok so basically to stop this while iteration and return i, the characters of the flag must be the one in controls.
We can’t stop the while with the condition i != 1 because in the check_array there’s no such value as 0xffffffffffffffff.

So I parsed (again) controls to be an array of int (4 byte) using the hexdump and I copied the values in the python script.

Now we can bruteforce every character of the flag one-by-one using the following exploit.

Exploit

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
import sys

check_array = [
0x0000000000000001, 0x0000000000000009, 0x0000000000000011,
0x0000000000000027, 0x0000000000000002, 0x0000000000000000,
0x0000000000000012, 0x0000000000000003, 0x0000000000000008,
0x0000000000000012, 0x0000000000000009, 0x0000000000000012,
0x0000000000000011, 0x0000000000000001, 0x0000000000000003,
0x0000000000000013, 0x0000000000000004, 0x0000000000000003,
0x0000000000000005, 0x0000000000000015, 0x000000000000002e,
0x000000000000000a, 0x0000000000000003, 0x000000000000000a,
0x0000000000000012, 0x0000000000000003, 0x0000000000000001,
0x000000000000002e, 0x0000000000000016, 0x000000000000002e,
0x000000000000000a, 0x0000000000000012, 0x0000000000000006
]
controls = [
0x00000077, 0x00000066, 0x0000007b, 0x0000005f, 0x0000006e,
0x00000079, 0x0000007d, 0xffffffff, 0x00000062, 0x0000006c,
0x00000072, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0x00000061, 0x00000065, 0x00000069,
0xffffffff, 0x0000006f, 0x00000074, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000067,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0x00000075
]


def create_charset():
charset = ''
for c in controls:
try:
c = chr(c)
if c not in charset:
charset += c
# When c is 0xffffffff then we can't
# convert it to an ascii character
except OverflowError:
continue
return charset

def checker(c):
i = 0
while(c != controls[i]):
if(c <= controls[i]):
i = (i * 2) + 1
else:
i = (i + 1) * 2
return i

def main():
charset = create_charset()
i = 0
while (i < 33):
for c in charset:
a = checker(ord(c))
if a == check_array[i]:
sys.stdout.write(c)
break
i += 1

if __name__ == '__main__':
main()

Flag

flag{we_beleaf_in_your_re_future}