From Zero To X

zTrix's Blog

The Power of PEDA - ASIS CTF Quals 2014 Serial Number Writeup

by Wenlei Zhu (zTrix@blue-lotus)

Again, another ELF x86_64 binary with very long bit operation logic asking for some serial number.

I can still remember the time trying to solve Turututu, during which I spent a lot of time trying to figure out what happened under the instructions, but sadly with no luck, it’s totally a mess. The lucky thing is that peda is really a powerful tool, and I can guess the hidden logic with the help of peda, so I finally solved Turututu.

So let’s show what a powerful tool peda is by solving this challenge without figuring out that long bit operation logic(over 1 thousand lines of asm code!).

First, it’s easy to understand the overall logic. Here is the pseudocode(python-like).

serial_number = some_8_bytes_integer
state = 0   # four bit state
correct_serial = 0

while serial_number > 0:
    nibble = serial_number & 0xf
    serial_number = serial_number >> 4
    correct_serial = nibble == 0xd && state == 0xd
    state = some_very_long_logic_over_a_thousand_lines_of_asm(nibble, state)

if correct_serial:
    print 'Congratulations!'

Here, nibble and state are both 4 bit variables. So no matter what the some_very_long_logic_over_a_thousand_lines_of_asm function is, there is only 16 x 16 = 256 possibilities. Just brute-force them all using peda!

So I wrote the following peda helper function, by setting breakpoint at end of some_very_long_logic_over_a_thousand_lines_of_asm and using set $rip=0x400788, we can run that piece of code multiple times.

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
def god(self, *arg):
    """
    god mode
    """
    msg('god mode')
    rbp = peda.getreg('rbp')
    array_pointers = peda.read_int(rbp-0x28, 8)

    def get_state():
        ret = []
        for i in range(4):
            pointer = peda.read_int(array_pointers + i * 8, 8)
            ret.append(peda.read_int(pointer + 4, 4))
        return ret

    def set_state(state):
        for i in range(4):
            pointer = peda.read_int(array_pointers + i * 8, 8)
            peda.write_int(pointer + 4, state[i], 4)

    def check_one(t, init):
        peda.write_int(rbp-0x68, t, 1)
        peda.execute('set $rip=0x400788')

        # msg('extracted = %d%d%d%d, remain = %x' % (peda.read_int(rbp-0x44, 4), peda.read_int(rbp-0x48, 4), peda.read_int(rbp-0x4c, 4), peda.read_int(rbp-0x50, 4), peda.read_int(rbp-0x68, 8)))

        set_state(init)
        peda.execute('continue')
        state2 = get_state()

        return state2

    final = []
    for i in range(16):
        init = [i & 1, (i >> 1) & 1, (i>>2) & 1, (i>>3) & 1]
        for j in range(16):
            rs = check_one(j, init)
            msg('%x: %s -> %s' % (j, init, rs))
            final.append((j, init, rs))

    msg('%s' % final)
    return

So, the only thing left is gdb -x gdb.x, here it is.

file serial-number
b *0x4014d8
b *0x40149c
run

Then use god mode to brute force.

After brute force and a little processing, we can get the following jump table

0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0]
1 [0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
2 [0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
3 [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
4 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12]
5 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0]
6 [0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
7 [7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
8 [0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0]
9 [0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
10 [0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
11 [11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11]
12 [0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]
13 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
14 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
15 [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15]

So the serial number is da34c5217f9d.

Can you feel the power of peda tonight?

Comments