Event | Points | Solves | Categories | Task ID |
CSAW CTF Qualification Round 2017 | 100 | 342 | Reversing | 4656 |
Bobby was talking about tables a bunch, so I made some table stuff. I think this is what he was talking about…
We check out the binary:
$ file tablez
tablez: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=72adea86090fb7deeb319e95681fd2c669dcc503, not stripped
When we execute the binary, we are prompted to input the flag. Since we don’t know it (yet), the response is a simple “WRONG” message. We open the binary in IDA and see that it is quite small so we can quickly analyze the code and figure out what is going on:
So our input has to be 37 characters long and each character is replaced by a corresponding value that is stored in a table. Finally, the transformed input is compared to the constant value that was defined at the beginning and if they are equal, the “CORRECT <3” message is printed. There are many ways to reverse the table lookup for the constant value, but we decide to let the binary do the work for us.
The get_tbl_entry
function looks like the following:
The lookup table is constructed in the following way: [a_0, b_0, a_1, b_1, ..., a_254, b_254]
where a_1, a_2, ..., a_254
are the input bytes and b_1, b_2, ..., b_254
are the corresponding bytes that they are mapped to (a_i -> b_i
). The function first determines the index of the input byte by iterating through the array with a stepsize of 2 and checks if a_i
is equals the input byte. If the right index was found, it is used to query the array with an offset of 1 to find b_i
. If we want to reverse the mapping, all we would have to do is swap out the two references to the lookup table such that the function would first determine the index on the b_i
value and then find the corresponding a_i
value. This only requires two little patches to the binary: First, increment the reference to trans_tbl
at loc_85D
and decrement the next reference to trans_tbl
by 1. We do so and save the patched binary.
Now we can give the binary the constant value and it will reverse it for us so that we obtain the flag! Since we don’t know how to pass non-printable characters to a read()
call, we execute the binary with gdb and set a breakpoint after the read call to manually change the bytes in memory:
$ gdb ./tablez
gdb-peda$ b *0x555555554938 // break after fgets
Breakpoint 1 at 0x555555554938
gdb-peda$ b *0x5555555549F7 // break on strcmp
Breakpoint 2 at 0x5555555549f7
gdb-peda$ r
Starting program: ./tablez
Please enter the flag: // input a 37 char long string
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, 0x0000555555554938 in main ()
gdb-peda$ n
gdb-peda$ p strcpy($rax, "\xCE\x65\x05\x99\x23\xF9\x27\x99\x23\xBE\x11\x11\x65\xB1\x99\x65\x23\x73\x99\x71\x1B\x30\xF4\xF9\xF9\xB3\x99\xBE\xB3\xB1\xE7\x11\xF5\x9D\x73\xB3\x27\x0a") // change string in memory to constant from binary
$1 = 0xffffe320
gdb-peda$ c
Continuing.
Breakpoint 2, 0x00005555555549f7 in main ()
gdb-peda$ x/s $rax // read flag from $rax after strcmp break
0x7fffffffe320: "}3m_r0f_rett3b_3ra_spuk00l_elb4t{galf"
Looks like the flag is reversed, let me fix that for you:
$ python -c 'print "}3m_r0f_rett3b_3ra_spuk00l_elb4t{galf"[::-1]'
flag{t4ble_l00kups_ar3_b3tter_f0r_m3}
Sweet!