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.

results matching ""

    No results matching ""