ログイン
編集不可のページディスカッション情報添付ファイル
"Shinkan2016/CTF"の差分

MMA
1と2のリビジョン間の差分
2016-03-31 01:59:30時点のリビジョン1
サイズ: 48
編集者: nomeaning
コメント:
2016-04-04 23:03:48時点のリビジョン2
サイズ: 11299
編集者: nomeaning
コメント: MathJaxや定義リスト、問題を配置するサーバーなどが完了していないが、ひとまずページの内容の作成
削除された箇所はこのように表示されます。 追加された箇所はこのように表示されます。
行 2: 行 2:
#format gfm
# 2016年度MMA新歓CTF
CTF(Capture The Flag)は情報セキュリティなどのコンピュータ技術を競う競技のことです。CTFには主に次の2つの形式があります。フラグを見つけ解答するジェパディ形式のものと、他のチームのサーバーに対して攻撃を行いフラグを取る攻防戦形式のものです。

MMAでは2013年より積極的に各種CTFに参加しており、毎年SECCONやDEFCON予選といったコンテストに参加しています。

## ルール
 * 問題はウェブサイト(https://wiki.mma.club.uec.ac.jp/Shinkan2016/CTF)及び新歓教室で配布されます。
 * 回答期限は2016/4/19 17:00(JST)です。
 * 回答期限終了までに問題の解法を公開することを禁止します。
 * 問題が解けたら、新歓教室あるいはMMA部室(サークル棟2階)に居るMMA部員に確認してもらってください。解けた問題に応じて賞品があります。
 * 問題の修正がある場合、ウェブサイトにて告知します。

## 問題

### a quiz in floating island

難易度
: ★

ジャンル
: Misc

出題者
: nomeaning

次のCプログラムが、`shinkan2016.mmactf.link`のポート2000で動いています。フラグを入手してください。
```c
#include <stdio.h>

int main(int argc, char **argv) {
  float a, b;
  scanf("%f %f", &a, &b);
  if(a == b && *(int*)&a != *(int*)&b) {
    puts("Congratulations. Flag is here.");
    puts(argv[1]);
  } else {
    puts("Wrong");
  }
}
```

## Secure Password Rules

難易度
: ★(問題1, 問題2)

ジャンル
: Programming

出題者
: nomeaning

MMA(Modern Mobile Application)社では、新たなログイン機能を持つアプリケーションを開発するにあたり、安全なパスワードをユーザーに強制させるため次のようなパスワードのルールを定めました。

 * パスワードには半角アルファベット小文字のみを用いることが出来る(記号とか使われるとSQL Injectionとかされるかもしれないのです)。
 * パスワードの長さは\\(1\\)文字以上 \\(N\\) 文字まで(長すぎると容量をたくさん使ってしまいます)。
 * 辞書に含まれている単語が部分文字列として3度以上現れてはならない(辞書攻撃対策です)。 部分文字列は同じ位置にある同じ長さのものを除いて重複して数えます。例えば辞書には"lol", "ol", "lo", "l"の4つが含まれていた場合、"lol"には辞書に含まれている単語が5つ含まれていることになります。

ところが、このルールを定めた後に、辞書が強力すぎるとパスワードに使える文字列が少なくなりすぎてしまうことがあるという指摘が入りました。それに対応する必要があるかを検証するためには、まずパスワード使える文字列の種類を数え上げる必要があります。あなたの仕事は\\(N\\)と辞書が与えられた時に、ルールを満たすパスワードの個数を求めることです。

例えば、\\(N = 3\\)、すなわちパスワードが3文字まであり、辞書には"a", "b", "c"の3つの文字列が含まれているとき、
 * 1文字のパスワードは全て条件を満たす(\\(26\\)通り)
 * 2文字のパスワードは全て条件を満たす(\\(26^2\\) 通り)
 * 3文字のパスワードは"a","b","c"のみからなるものを除いて全て条件を満たす(\\(26^3 - 3 ^ 3\\)通り)

となるので、\\(18251\\)通りのパスワードがありえます。また、\\(N = 5\\)で辞書には"aaa"のみが含まれる時は"aaaaa"以外の全ての5文字以下のパスワードが許容されるため、\\(26 + 26^2 + 26^3+ 26^4 + 26^5 - 1 = 12356629\\)通りのパスワードが考えられます。

#### 例題
 1. \\(N = 6\\)で、辞書ファイル[attachment:sample3.txt]に対するパスワードのパターン数は\\(21089426\\)です。
 2. \\(N = 150\\)で、次の辞書ファイル[attachment:sample4.txt]に対するパスワードのパターン数を\\(10^9+9\\)で割った余りは\\(275144701\\)です。

#### 問題
 1. \\(N = 5\\)で、次の辞書ファイル[attachment:input1.txt]に対するパスワードのパターン数を答えてください。辞書ファイルは1行に1単語が書かれています。パスワードのパターン数の10進表記がそのままフラグになります。
 2. \\(N = 10000\\)で、次の辞書ファイル[attachment:input2.txt]に対するパスワードのパターン数を答えてください。ただし、パターン数は非常に大きくなるため\\(10^9+9\\)で割った時の余りを取った数を10進表記したものがフラグになります。

### Money Game Returned

難易度
: ★

ジャンル
: Pwn

出題者
: nomeaning

次のCプログラムがサーバー`ccc`のポート27506で動いています。フラグを入手してください。
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define N 3
// 各銘柄の値段
int price[N];
// 各銘柄の個数
int count[N];
// 現在の所持金
int money;
// 現在の週
int week;

int check_array_bound(int pos) {
  return pos >= 0 && pos < N;
}

// ゲームを初期化する
void init() {
  int i;
  for(i = 0; i < N; i++) {
    price[i] = 100;
  }
  money = 100;
}

// 現在の状態をユーザに伝える
void show() {
  int i;
  printf("\033[2J");
  if(week == 53) {
    printf("Week #%d(Final Week): \n", week + 1);
  } else {
    printf("Week #%d: \n", week + 1);
  }
  printf("You have %d YEN\n", money);
  for(i = 0; i < N; i++) {
    printf(" Stock #%d: %d YEN (You have %d.)\n", i + 1, price[i], count[i]);
  }
}

// ランダムに銘柄の値段を変化させる
void change() {
  int i;
  for(i = 0; i < N; i++) {
    int d = rand() % 21 - 10;
    price[i] += d;
    if(price[i] < 50) price[i] = 50;
    if(price[i] > 150) price[i] = 150;
  }
}

// 数字入力を受け取る
int get_number() {
  char buf[256] = {};
  int i, ret;

  fgets(buf, 255, stdin);
  if(*buf != '\0') buf[strlen(buf) - 1] = '\0';

  for(i = 0; i < strlen(buf); i++) {
    if(!isdigit(buf[i])) {
      puts("Invalid Number.");
      return get_number();
    }
  }

  ret = atoi(buf);
  return ret;
}

int main() {
  int action, n, m;
  srand(time(NULL));
  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stdout, NULL, _IONBF, 0);
  init();
  for(week = 0; week < 54; week++) {
    show();
    printf("Your action (1:Buy, 2:Sell, 3:Rest) : ");
    action = get_number();
    switch(action) {
    case 1:
      // 買う
      printf("Which stock do you want to buy? (%d-%d) : ", 1, N);
      n = get_number() - 1;
      if(!check_array_bound(n)) {
        puts("Invalid stock");
        break;
      }
      printf("How many stocks do you want to buy? (%d-%d): ", 0, money / price[n]);
      m = get_number();
      if(m > money / price[n]) {
        puts("Too much");
        break;
      }
      count[n] += m;
      money -= price[n] * m;
      break;
    case 2:
      // 売る
      printf("Which stock do you want to sell? (%d-%d) : ", 1, N);
      n = get_number() - 1;
      if(!check_array_bound(n)) {
        puts("Invalid stock");
        break;
      }
      printf("How many stocks do you want to sell? (%d-%d): ", 0, count[n]);
      m = get_number();
      if(m > count[n]) {
        puts("Too much");
        break;
      }
      count[n] -= m;
      money += price[n] * m;
      break;
    case 3:
      // 休む
      break;
    default:
      puts("Invalid action");
      break;
    }
    change();
    puts("\n");
  }
  printf("Your final money: %d YEN\n\n", money);
  sleep(1);
  if(money > 1000000) { // Millionaire!
    puts("Congratz! The flag is here.");
    printf("%s\n", getenv("FLAG"));
  } else {
    puts("You have little money to get the flag.");
  }
  return 0;
}
```

### unskipyc
難易度
: ★★

ジャンル
: Forensics

出題者
: ytoku

```
0000020: 0064 0100 6402 0014 6403 0064 0400 6405 .d..d...d..d..d.
0000030: 0085 0300 196a 0000 6406 0064 0700 6404 .....j..d..d..d.
0000040: 0064 0400 6408 0085 0300 1917 8301 0017 .d..d...........
0000050: 4748 6404 0053 2809 0000 0073 0800 0000 GHd..S(....s....
0000060: 466c 6167 2069 7320 731f 0000 000f 39f9 Flag is s.....9.
0000070: 459c 8445 790a c9a9 b900 7310 f129 fdcc E..Ey.....s..)..
0000080: 65b9 9c8c cece a978 aba9 8933 690a 0000 e......x...3i...
0000090: 0069 1a00 0000 4e69 0900 0000 7401 0000 .i....Ni....t...
00000a0: 007a 7403 0000 0062 696c 69ff ffff ff28 .zt....bili....(
00000b0: 0100 0000 7406 0000 0064 6563 6f64 6528 ....t....decode(
00000c0: 0000 0000 2800 0000 0028 0000 0000 7307 ....(....(....s.
00000d0: 0000 0066 6c61 672e 7079 7408 0000 003c ...flag.pyt....<
00000e0: 6d6f 6475 6c65 3e03 0000 0073 0000 0000 module>....s....
```

### Ultra Efficient Cipher
難易度
: ★★

ジャンル
: Crypto

出題者
: ytoku

Because XorShift is the efficient random number generator, this cipher is very fast.

Flag format is `/^MMA\{[a-z0-9_]+\}$/`. `flag.txt` contains the flag only without any whitespace characters.
```c
#include <stdio.h>

unsigned int s[4];
unsigned int xor_shift() {
  unsigned int t;
  t = s[0] ^ (s[0] << 11);
  s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
  return s[3] = (s[3] ^ (s[3] >> 19)) ^ (t ^ (t >> 8));

}

int main(int argc, char **argv) {
  if(argc != 3) {
    fprintf(stderr, "Usage: %s key-file input-file\n", argv[0]);
    return 1;
  }
  FILE *fp = fopen(argv[1], "r");
  fread(s, sizeof(unsigned int), 4, fp);
  fclose(fp);

  fp = fopen(argv[2], "r");
  unsigned int buf = 0;
  int i;
  while(fread(&buf, 1, sizeof(buf), fp) > 0) {
    buf ^= xor_shift();
    for(i = 0; i < 4; i++) {
      putchar(buf & 255);
      buf >>= 8;
    }
  }
  fclose(fp);
  return 0;
}
```
```
$ ./uec key.dat flag.txt | xxd
00000000: a1e7 05f2 5634 a44f 4732 6ac8 038a 4638 ....V4.OG2j...F8
00000010: 5f0c c880 d669 f3ac 61b5 154a f160 be8d _....i..a..J.`..
00000020: 7853 764b a605 e09f 5fe3 1a3e 7afe b1b1 xSvK...._..>z...
00000030: 183c de66 1be0 d855 .<.f...U
```

## 各ジャンルの説明
|ジャンル名|説明|
|---|---|
|Crypto|暗号に関する問題が該当します。|
|Forensics|与えられた情報を分析し、必要な情報を抽出します。例えば、USBメモリーのイメージが与えられて、そこからファイルを抽出するなどが該当します。|
|Programming|主にプログラミング/アルゴリズムの知識を用いて解く問題が出題されます。|
|Pwn|与えられたプログラムの脆弱性(プログラムのミスなどによって生じるセキュリティホール)を攻撃し、必要な情報を得る問題が出題されます。|
|Misc|その他の問題が該当します。|

CTF(Capture The Flag)は情報セキュリティなどのコンピュータ技術を競う競技のことです。CTFには主に次の2つの形式があります。フラグを見つけ解答するジェパディ形式のものと、他のチームのサーバーに対して攻撃を行いフラグを取る攻防戦形式のものです。

MMAでは2013年より積極的に各種CTFに参加しており、毎年SECCONやDEFCON予選といったコンテストに参加しています。

ルール

  • 問題はウェブサイト(https://wiki.mma.club.uec.ac.jp/Shinkan2016/CTF)及び新歓教室で配布されます。
  • 回答期限は2016/4/19 17:00(JST)です。
  • 回答期限終了までに問題の解法を公開することを禁止します。
  • 問題が解けたら、新歓教室あるいはMMA部室(サークル棟2階)に居るMMA部員に確認してもらってください。解けた問題に応じて賞品があります。
  • 問題の修正がある場合、ウェブサイトにて告知します。

問題

a quiz in floating island

難易度
: ★

ジャンル
: Misc

出題者
: nomeaning

次のCプログラムが、shinkan2016.mmactf.linkのポート2000で動いています。フラグを入手してください。

#include <stdio.h>

int main(int argc, char **argv) {
  float a, b;
  scanf("%f %f", &a, &b);
  if(a == b && *(int*)&a != *(int*)&b) {
    puts("Congratulations. Flag is here.");
    puts(argv[1]);
  } else {
    puts("Wrong");
  }
}

Secure Password Rules

難易度
: ★(問題1, 問題2)

ジャンル
: Programming

出題者
: nomeaning

MMA(Modern Mobile Application)社では、新たなログイン機能を持つアプリケーションを開発するにあたり、安全なパスワードをユーザーに強制させるため次のようなパスワードのルールを定めました。

  • パスワードには半角アルファベット小文字のみを用いることが出来る(記号とか使われるとSQL Injectionとかされるかもしれないのです)。
  • パスワードの長さは\(1\)文字以上 \(N\) 文字まで(長すぎると容量をたくさん使ってしまいます)。
  • 辞書に含まれている単語が部分文字列として3度以上現れてはならない(辞書攻撃対策です)。 部分文字列は同じ位置にある同じ長さのものを除いて重複して数えます。例えば辞書には"lol", "ol", "lo", "l"の4つが含まれていた場合、"lol"には辞書に含まれている単語が5つ含まれていることになります。

ところが、このルールを定めた後に、辞書が強力すぎるとパスワードに使える文字列が少なくなりすぎてしまうことがあるという指摘が入りました。それに対応する必要があるかを検証するためには、まずパスワード使える文字列の種類を数え上げる必要があります。あなたの仕事は\(N\)と辞書が与えられた時に、ルールを満たすパスワードの個数を求めることです。

例えば、\(N = 3\)、すなわちパスワードが3文字まであり、辞書には"a", "b", "c"の3つの文字列が含まれているとき、
* 1文字のパスワードは全て条件を満たす(\(26\)通り)
* 2文字のパスワードは全て条件を満たす(\(26^2\) 通り)
* 3文字のパスワードは"a","b","c"のみからなるものを除いて全て条件を満たす(\(26^3 - 3 ^ 3\)通り)

となるので、\(18251\)通りのパスワードがありえます。また、\(N = 5\)で辞書には"aaa"のみが含まれる時は"aaaaa"以外の全ての5文字以下のパスワードが許容されるため、\(26 + 26^2 + 26^3+ 26^4 + 26^5 - 1 = 12356629\)通りのパスワードが考えられます。

例題

  1. \(N = 6\)で、辞書ファイル[attachment:sample3.txt]に対するパスワードのパターン数は\(21089426\)です。
  2. \(N = 150\)で、次の辞書ファイル[attachment:sample4.txt]に対するパスワードのパターン数を\(10^9+9\)で割った余りは\(275144701\)です。

問題

  1. \(N = 5\)で、次の辞書ファイル[attachment:input1.txt]に対するパスワードのパターン数を答えてください。辞書ファイルは1行に1単語が書かれています。パスワードのパターン数の10進表記がそのままフラグになります。
  2. \(N = 10000\)で、次の辞書ファイル[attachment:input2.txt]に対するパスワードのパターン数を答えてください。ただし、パターン数は非常に大きくなるため\(10^9+9\)で割った時の余りを取った数を10進表記したものがフラグになります。

Money Game Returned

難易度
: ★

ジャンル
: Pwn

出題者
: nomeaning

次のCプログラムがサーバーcccのポート27506で動いています。フラグを入手してください。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define N 3
// 各銘柄の値段
int price[N];
// 各銘柄の個数
int count[N];
// 現在の所持金
int money;
// 現在の週
int week;

int check_array_bound(int pos) {
  return pos >= 0 && pos < N;
}

// ゲームを初期化する
void init() {
  int i;
  for(i = 0; i < N; i++) {
    price[i] = 100;
  }
  money = 100;
}

// 現在の状態をユーザに伝える
void show() {
  int i;
  printf("\033[2J");
  if(week == 53) {
    printf("Week #%d(Final Week): \n", week + 1);
  } else {
    printf("Week #%d: \n", week + 1);
  }
  printf("You have %d YEN\n", money);
  for(i = 0; i < N; i++) {
    printf("    Stock #%d: %d YEN (You have %d.)\n", i + 1, price[i], count[i]);
  }
}

// ランダムに銘柄の値段を変化させる
void change() {
  int i;
  for(i = 0; i < N; i++) {
    int d = rand() % 21 - 10;
    price[i] += d;
    if(price[i] < 50) price[i] = 50;
    if(price[i] > 150) price[i] = 150;
  }
}

// 数字入力を受け取る
int get_number() {
  char buf[256] = {};
  int i, ret;

  fgets(buf, 255, stdin);
  if(*buf != '\0') buf[strlen(buf) - 1] = '\0';

  for(i = 0; i < strlen(buf); i++) {
    if(!isdigit(buf[i])) {
      puts("Invalid Number.");
      return get_number();
    }
  }

  ret = atoi(buf);
  return ret;
}

int main() {
  int action, n, m;
  srand(time(NULL));
  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stdout, NULL, _IONBF, 0);
  init();
  for(week = 0; week < 54; week++) {
    show();
    printf("Your action (1:Buy, 2:Sell, 3:Rest) : ");
    action = get_number();
    switch(action) {
    case 1:
      // 買う
      printf("Which stock do you want to buy? (%d-%d) : ", 1, N);
      n = get_number() - 1;
      if(!check_array_bound(n)) {
        puts("Invalid stock");
        break;
      }
      printf("How many stocks do you want to buy? (%d-%d): ", 0, money / price[n]);
      m = get_number();
      if(m > money / price[n]) {
        puts("Too much");
        break;
      }
      count[n] += m;
      money -= price[n] * m;
      break;
    case 2:
      // 売る
      printf("Which stock do you want to sell? (%d-%d) : ", 1, N);
      n = get_number() - 1;
      if(!check_array_bound(n)) {
        puts("Invalid stock");
        break;
      }
      printf("How many stocks do you want to sell? (%d-%d): ", 0, count[n]);
      m = get_number();
      if(m > count[n]) {
        puts("Too much");
        break;
      }
      count[n] -= m;
      money += price[n] * m;
      break;
    case 3:
      // 休む
      break;
    default:
      puts("Invalid action");
      break;
    }
    change();
    puts("\n");
  }
  printf("Your final money: %d YEN\n\n", money);
  sleep(1);
  if(money > 1000000) { // Millionaire!
    puts("Congratz! The flag is here.");
    printf("%s\n", getenv("FLAG"));
  } else {
    puts("You have little money to get the flag.");
  }
  return 0;
}

unskipyc

難易度
: ★★

ジャンル
: Forensics

出題者
: ytoku

0000020: 0064 0100 6402 0014 6403 0064 0400 6405  .d..d...d..d..d.
0000030: 0085 0300 196a 0000 6406 0064 0700 6404  .....j..d..d..d.
0000040: 0064 0400 6408 0085 0300 1917 8301 0017  .d..d...........
0000050: 4748 6404 0053 2809 0000 0073 0800 0000  GHd..S(....s....
0000060: 466c 6167 2069 7320 731f 0000 000f 39f9  Flag is s.....9.
0000070: 459c 8445 790a c9a9 b900 7310 f129 fdcc  E..Ey.....s..)..
0000080: 65b9 9c8c cece a978 aba9 8933 690a 0000  e......x...3i...
0000090: 0069 1a00 0000 4e69 0900 0000 7401 0000  .i....Ni....t...
00000a0: 007a 7403 0000 0062 696c 69ff ffff ff28  .zt....bili....(
00000b0: 0100 0000 7406 0000 0064 6563 6f64 6528  ....t....decode(
00000c0: 0000 0000 2800 0000 0028 0000 0000 7307  ....(....(....s.
00000d0: 0000 0066 6c61 672e 7079 7408 0000 003c  ...flag.pyt....<
00000e0: 6d6f 6475 6c65 3e03 0000 0073 0000 0000  module>....s....

Ultra Efficient Cipher

難易度
: ★★

ジャンル
: Crypto

出題者
: ytoku

Because XorShift is the efficient random number generator, this cipher is very fast.

Flag format is /^MMA\{[a-z0-9_]+\}$/. flag.txt contains the flag only without any whitespace characters.

#include <stdio.h>

unsigned int s[4];
unsigned int xor_shift() {
  unsigned int t;
  t = s[0] ^ (s[0] << 11);
  s[0] = s[1]; s[1] = s[2]; s[2] = s[3];
  return s[3] = (s[3] ^ (s[3] >> 19)) ^ (t ^ (t >> 8));

}

int main(int argc, char **argv) {
  if(argc != 3) { 
    fprintf(stderr, "Usage: %s key-file input-file\n", argv[0]);
    return 1;
  }
  FILE *fp = fopen(argv[1], "r");
  fread(s, sizeof(unsigned int), 4, fp);
  fclose(fp);

  fp = fopen(argv[2], "r");
  unsigned int buf = 0;
  int i;
  while(fread(&buf, 1, sizeof(buf), fp) > 0) {
    buf ^= xor_shift();
    for(i = 0; i < 4; i++) {
      putchar(buf & 255);
      buf >>= 8;
    }
  }
  fclose(fp);
  return 0;
}
$ ./uec key.dat flag.txt | xxd
00000000: a1e7 05f2 5634 a44f 4732 6ac8 038a 4638  ....V4.OG2j...F8
00000010: 5f0c c880 d669 f3ac 61b5 154a f160 be8d  _....i..a..J.`..
00000020: 7853 764b a605 e09f 5fe3 1a3e 7afe b1b1  xSvK...._..>z...
00000030: 183c de66 1be0 d855                      .<.f...U

各ジャンルの説明

ジャンル名 説明
Crypto 暗号に関する問題が該当します。
Forensics 与えられた情報を分析し、必要な情報を抽出します。例えば、USBメモリーのイメージが与えられて、そこからファイルを抽出するなどが該当します。
Programming 主にプログラミング/アルゴリズムの知識を用いて解く問題が出題されます。
Pwn 与えられたプログラムの脆弱性(プログラムのミスなどによって生じるセキュリティホール)を攻撃し、必要な情報を得る問題が出題されます。
Misc その他の問題が該当します。

Shinkan2016/CTF (最終更新日時 2016-04-05 20:03:30 更新者 ytoku)