SECCON Beginners CTF 2024 Writeup

# SECCON Beginners CTF 2024
JNSAの交流会に参加した後、IPFactoryの皆さんと参加しました。
順位は99位/962位でした。

![rank.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982234/d8f0d98d-3c9a-1ea3-279a-d9b4d5511f3b.png)

# 目次
- welcome
- getRank
- simpleoverflow
- simpleoverwrite
- 感想

## welcome(welcome, 50pt)

CTF開始直後、Discordの``announcements``チャンネルにてflagが投稿されました。

![welcome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982234/6803e860-3ef4-8a56-189f-b13f270bdc35.png)

``ctf4b{Welcome_to_SECCON_Beginners_CTF_2024}``

## getRank(misc, 59pt)

1位になるとflagを獲得できるみたいです。

```typescript
const RANKING = [10 ** 255, 1000, 100, 10, 1, 0];
```

ここで``10の256乗``を入力すれば良いのですが以下のコードにより
``10の256乗``を入力するだけではflagを手に入れることはできません。

```typescript
function chall(input: string): Res {
  if (input.length > 300) {
    return {
      rank: -1,
      message: "Input too long",
    };
  }

  let score = parseInt(input);
  if (isNaN(score)) {
    return {
      rank: -1,
      message: "Invalid score",
    };
  }
  if (score > 10 ** 255) {
    // hmm...your score is too big?
    // you need a handicap!
    for (let i = 0; i < 100; i++) {
      score = Math.floor(score / 10);
    }
  }

  return ranking(score);
}
```

flagを手に入れるには3つの条件を満たす必要があります。

1. 入力されたものの長さが300以下であること
    (超えると``Input too long``と判定される)
2. 入力されたものがNaNでない
    (parseIntの処理後数字である必要がある)
3. 最終的に``10の255乗``より大きいこと
    (1位になれない)

また、``Get Rank``の部分で通信を途中で止めると以下のようなヘッダとボディが書かれています。

![getrank.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982234/f07766a6-fbf8-2661-67d6-c68762c13bbc.png)

したがって、通信を途中で止めて``10の355乗``より大きな値を入れればよいことがわかります。

1.は逆手にとればボディに以下のような配列を入力すると膨大な値でも長さは1と判定され、
flagを手に入れることができます。

``{"input":["1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"]}``

別解

16進数でもできるみたいです。

``{"input":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}``

``ctf4b{15_my_5c0r3_700000_b1g?}``

### おまけ

Q.1e356のようにeを使った表記でflagが取れないのはなぜですか?

A.この場合``1e356 → 1`` とparseIntの処理後最初の数字の部分のみ反映されるためです。

## simpleoverflow(pwnable, 50pt)

オーバーフローです。

```c

int main() {
  char buf[10] = {0};
  int is_admin = 0;
  printf("name:");
  read(0, buf, 0x10);
  printf("Hello, %s\n", buf);
  if (!is_admin) {
    puts("You are not admin. bye");
  } else {
    system("/bin/cat ./flag.txt");
  }
  return 0;
}

```

配列bufとread関数に注目すると、10文字以上の入力は対応していないことがわかります。
したがって10文字以上の文字列を入力するとflag獲得です。

![simpleoverflow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982234/e7dc7ebf-6b28-5bf1-6b04-005f686cade1.png)

``ctf4b{0n_y0ur_m4rk}``

### おまけ

read関数で文字列を読み取った後、終端文字を追加して配列bufに入れているため
本来収納できる10文字でも追加の終端文字によってオーバーフローし
flag獲得できてしまいます。

## simpleoverwrite(pwnable, 66pt)

returnでwin関数のアドレスを実行すればflagが手に入りそうです・・・

```c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void win() {
  char buf[100];
  FILE *f = fopen("./flag.txt", "r");
  fgets(buf, 100, f);
  puts(buf);
}

int main() {
  char buf[10] = {0};
  printf("input:");
  read(0, buf, 0x20);
  printf("Hello, %s\n", buf);
  printf("return to: 0x%lx\n", *(uint64_t *)(((void *)buf) + 18));
  return 0;
}

```

Ghidraでwin関数のアドレスを見てみると``0x00401186``であることがわかります。

![simpleoverwrite_Ghidra.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982234/3887ecf0-b996-60c0-1e00-6c56043b1c91.png)

ここでアドレスを入力する際、ncコマンドの後に入力すると
必ず文字列として判定されてしまうため入力できる範囲が``0x21～0x7e``と
制限がかかってしまいます。

そこで以下のようにechoコマンドとncコマンドを同時に使用します。
``echo -en "\x01" | nc simpleoverwrite.beginners.seccon.games 9001``
あとは``\x01``の部分をいろいろと試行錯誤してflag獲得です。

![simpleoverwrite_nc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982234/e09fb2eb-a780-0df0-d45f-43c20e1b5ca8.png)

``ctf4b{B3l13v3_4g41n}``

## 感想

自分で問題作っていろいろと試行錯誤してみるとより理解力が深まり、
実力が上がるかもしれない・・・