Home HITCON CTF 2016 Quals -- flame
Post
Cancel

HITCON CTF 2016 Quals -- flame

Category: PPC ( more like Reverse )
Points: 150

We were given a 32 bit PowerPC ELF.
Fortunately I’ve got qemu-ppc-static installed on my ctf-box, so we can actually run the program by the following command:

1
2
3
4
5
6
7
8
9
# root @ 9c51322c8256 in /mnt/files/hitcon-ctf-2016-qual/flame [7:51:02] 
$ qemu-ppc-static ./flame
*************************************
*                                   *
*   HITCON CTF 2016 Flag Verifier   *
*                                   *
*************************************
Check your flag before submission: AAAA
Your flag is incorrect :(

Kind of appreciate that this is a static linked binary, because if it’s a dynamic linked binary then I’ll have to spend more time to installed the PPC version of libc.

Anyway we can see that the program will ask us to input the flag, and check if the flag is correct or not.

To do the dynamic analysis, I first use qemu-ppc-static -g 10001 ./flame to launch the program and listen for a gdb connection at port 10001, then I use gdb-multiarch to debug the program with target remote localhost:10001. As for the static analysis, I launch the program with IDA Pro.

After done some reversing, I summarize the program behavior with the following pseudo code:

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
int main()
{
    scanf("%s", flag); // let's just ignore the buffer overflow lol
    if( strlen(flag) == 35)
    {
        srandom(0x1e61);
        int i;
        for (i = 0 ; i < 35 ; i++)
        {
            r = rand();
            check[i] = flag[i] ^ (r & 0xfff);
        }
        for (i = 0 ; i < 35 ; i++)
        {
            if ( check[i] != secret[i] )
            {
                fail();
            }
        }
        success();
    }
    else
    {
        fail();
    }
}

The most challenging part is the line check[i] = flag[i] ^ (r & 0xfff);, it actually look like this in the PowerPC assembly:

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
// r = rand();
bl        rand
mr        r9, r3
// r = r & 0xfff
clrlwi    r10, r9, 20 <-- clear the high-order 20 bits
lwz       r9, 0x18(r31)
slwi      r9, r9, 2
addi      r8, r31, 0x1A0
add       r9, r8, r9
addi      r9, r9, -0x180
stw       r10, 0(r9)
lwz       r9, 0x18(r31)
slwi      r9, r9, 2
addi      r10, r31, 0x1A0
add       r9, r10, r9
addi      r9, r9, -0x180
lwz       r9, 0(r9)
mr        r8, r9
// c = flag[i]
addi      r10, r31, 0x138
lwz       r9, 0x18(r31)
add       r9, r10, r9
lbz       r9, 0(r9)
// check[i] = c ^ r
xor       r9, r8, r9
mr        r10, r9
lwz       r9, 0x18(r31)
slwi      r9, r9, 2
addi      r8, r31, 0x1A0
add       r9, r8, r9
addi      r9, r9, -0x180
stw       r10, 0(r9)
// i++
lwz       r9, 0x18(r31)
addi      r9, r9, 1
stw       r9, 0x18(r31)

Took me a while to figure out the whole operation.

So now we know that the flag is a string with 35 characters. The program will do some operation on our input, then store the result into the check buffer. Then it will compare each byte between the check buffer and the secret buffer, and print out the success message if their content were the same.

We can dump the content of the secret buffer by using the debbuger.

1
2
3
4
5
6
7
8
9
0xf6fff86c:     0x00000cfe      0x00000859      0x0000095d      0x00000871
0xf6fff87c:     0x0000040d      0x00000006      0x00000ade      0x00000fa8
0xf6fff88c:     0x00000561      0x000009da      0x00000878      0x00000682
0xf6fff89c:     0x00000fa9      0x00000f5f      0x0000025e      0x00000db0
0xf6fff8ac:     0x00000fbf      0x00000bc6      0x00000d38      0x0000095d
0xf6fff8bc:     0x00000d09      0x000007ed      0x00000307      0x000001c0
0xf6fff8cc:     0x00000399      0x00000956      0x00000a45      0x00000292
0xf6fff8dc:     0x00000c8a      0x0000092f      0x0000004a      0x00000964
0xf6fff8ec:     0x00000194      0x000009da      0x0000011f      

After that, we can just recover the flag by writing some simple scripts.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env ruby

resp = `./test`.split("\n")
seed = []
ans =[0x00000cfe, 0x00000859, 0x0000095d, 0x00000871, 0x0000040d,0x00000006,0x00000ade, 0x00000fa8, 0x00000561,  0x000009da , 0x00000878, 0x00000682, 0x00000fa9 , 0x00000f5f, 0x0000025e, 0x00000db0, 0x00000fbf, 0x00000bc6 , 0x00000d38 , 0x0000095d, 0x00000d09, 0x000007ed , 0x00000307, 0x000001c0, 0x00000399, 0x00000956 , 0x00000a45 , 0x00000292, 0x00000c8a,0x0000092f , 0x0000004a , 0x00000964, 0x00000194,  0x000009da, 0x0000011f]
 
for s in resp
    seed << (s.to_i(16) & 0xfff)
end

flag = ""

for a,b in seed.zip(ans)
    flag += (a^b).chr
end

puts flag

test is a C program for generating the random seed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int i = 0;
    srand(0x1e61);
    for(i = 0 ; i < 35 ; i++)
    {
        printf("0x%x\n", rand());
    }
    return 0;
}

Result:

1
2
3
# root @ 9c51322c8256 in /mnt/files/hitcon-ctf-2016-qual/flame [8:42:43] C:126
$ ruby ./sol.rb 
hitcon{P0W3rPc_a223M8Ly_12_s0_345y}

flag: hitcon{P0W3rPc_a223M8Ly_12_s0_345y}

References of PowerPC:

This post is licensed under CC BY-SA 4.0 by the author.

HITCON CTF 2016 Quals -- Secret Holder

HITCON CTF 2016 Quals -- ROP

Comments powered by Disqus.