Basic crack me.
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"
.
Now we can rewrite the function in pseudocode :
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
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}