radare2 con - eXit write up
R2con is a conference oriented to reverse engineering, coding, hacking and other interesting topics. There are some conferences, competitions and other stuff that you should definitely check if you're into this world. I've always wanted to learn radare2 so lets try some challenges.
eXit
This was an easy challenge, so it should be perfect to learn some basic commands with radare2. After downloading the binary, I executed it and looked like it was a text adventure.
Wait.. This looks familiar... This is the game Elliot Alderson play on Mr.Robot!. So I guess we need to input the correct actions and we will get the flag.
After launching r2, I wanted to search for interesting strings, as the right commands may be hard-coded.
There we go. Seems like I got the right commands, so lets try them.
This didn't give me the flag, so lets disassemble the binary. I used Cutter, an awesome UI for radare2. Right before going into the default questions, the return value from the function fcn.000014dd
is stored into uVar1
and then compared it with the value 2
. If the condicion is not satisfied, it will initialize another variable, and then return that value.
Inside fcn.000014dd
we can see that the text of the game is different, but before reaching that line, there's another function fcn.000013a9
which will compare its return value to 0
.
We can see there's a XOR operation going on inside fcn.000013a9
. We can also guess from the code doing the operation that there's an offset to the XORed result, which will be compared to the XORed secret command.
arg1
is the user input, and arg2
is the secret key.
(uint8_t)((arg1[var_64h] ^ *(uint8_t *)((int64_t)&var_60h + (int64_t)var_64h)) + *(char *)((int64_t)&var_40h + (int64_t)var_64h)) != arg2[var_64h]
From the x86 calling convention, its known that arguments are passed to the functions in the RDI
and RSI
registers. Let's setup the debugger on radare2 and setup a breakpoint on the fcn.000014d
call, which happens at 0x14dd
.
Later, setup a breakpoint at 0x55ad1d5ed3a9
, which is the fcn.000013a9
function.
Once inside the function, lets check the registers.
At RDI
is stored my input, which was Test, while at RSI
is the secret command.
First secret command
[0x97,0xcd,0xd2,0xd6,0xc0,0xc7,0xcd,0x84,0xec,0x91,0xad,0x62,0xf5,0xf1,0x65,0x22,0x58,0x82,0xb1,0x37,0x61,0x3e,0x5d,0x2b,0x14,0x4c]
Remember the offset and the XOR key? As they're hardcoded I used hex calculations to get the values.
Offset negative values
var38h = 0x201073317331efac
var30h = 0x73317331efacfeeb
Full offset chain
[0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x37,0x13,0x37,0x13,0xfe,0xca,0x37,0x13,0x37,0x13,0xfe,0xca,0xef,0xbe,0xad,0xde]
XOR key negative values
var60h = 0xeddaebfeeddaebfe
Full XOR key chain
[0xde,0xad,0xbe,0xef,0xde,0xad,0xbe,0xef,0xca,0xfe,0x13,0x37,0xca,0xfe,0x13,0x37,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a]
Note: There are 2 more secret commands, which can be obtained using the same technique.
Second secret command
[0x9c,0xcd,0xe1,0x8e,0xb0,0x92,0xd7,0x91,0xc0,0x9e,0xb2]
Third secret command
[0x97, 0xe2, 0xe7, 0x9d]
To solve this problem I coded a simple python script. It will show on screen the value without the offset, then XOR the values to get the valid commands.
import string
def brute_force_xor():
print ("\n--- brute forcing the xored command ---")
xor_key = [0xde,0xad,0xbe,0xef,0xde,0xad,0xbe,0xef,0xca,0xfe,0x13,0x37,0xca,0xfe,0x13,0x37,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a]
it = 0
command = ""
while (len(xored_command) != 0):
for char in string.printable:
if len(xored_command) != 0:
res = hex(ord(char) ^ xor_key[it])
if int(res,16) == int(xored_command[0],16):
command += char
del(xored_command[0])
if it == len(xor_key) - 1:
it = 0
else:
it = it + 1
else:
break
print(f"command decoded: {command}")
# first command
coded_solution = [0x97,0xcd,0xd2,0xd6,0xc0,0xc7,0xcd,0x84,0xec,0x91,0xad,0x62,0xf5,0xf1,0x65,0x22,0x58,0x82,0xb1,0x37,0x61,0x3e,0x5d,0x2b,0x14,0x4c]
# second command
#coded_solution = [0x9c,0xcd,0xe1,0x8e,0xb0,0x92,0xd7,0x91,0xc0,0x9e,0xb2]
# third command
#coded_solution = [0x97, 0xe2, 0xe7, 0x9d]
# characters got an offset
offset = [0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x37,0x13,0x37,0x13,0xfe,0xca,0x37,0x13,0x37,0x13,0xfe,0xca,0xef,0xbe,0xad,0xde]
xored_command = []
for item in coded_solution:
res = hex(item - offset[counter])
if int(res, 16) < 0:
res = hex(int(res,16) & 0xff)
xored_command.append(hex(int(res,16)))
print(f"{hex(item)} - {hex(offset[counter])} --> {hex(int(res,16))}")
brute_force_xor()
Execute it will give us the secret commands, which we can use to get the flag.