SECCON 2018 Writeup & Review

#### はじめに

先日、人生初の[SECCON](https://score-quals.seccon.jp/)に参加しました。

特に勉強もしてなかったので、学校の友人とチームを組んで軽いのりで始めたものの、１問しか解けず圧倒的惨敗でした。仲間の1人は開始2時間でリタイアしました。

反省の意を込めて、開始5分で爆速でできた問題と、その他残りの23時間55分かけても解けなかった問題たちの解法を、強者のWriteupを見ながらまとめていきます。


#### Writeup

##### Unzip (Forensics)

これが唯一解けた問題ですね。

与えられたzipを解答すると、```makefile.sh```と```flag.zip```が入っています。```flag.zip```はパスワードがかかっていて開きません。

まずは```makefile.sh```を見てみましょう。

```sh
echo 'SECCON{'`cat key`'}' > flag.txt
zip -e --password=`perl -e "print time()"` flag.zip flag.txt
```

1行目で「flag.txt」にflagを書き込み、2行目で現在時刻のUNIX timeをパスワードとして、zipファイル化していることがわかります。

```bash
$ zipinfo -v flag.zip
Archive:  flag.zip
There is no zipfile comment.

End-of-central-directory record:
-------------------------------

  Zip archive file size:                       225 (00000000000000E1h)
  Actual end-cent-dir record offset:           203 (00000000000000CBh)
  Expected end-cent-dir record offset:         203 (00000000000000CBh)
  (based on the length of the central directory and its expected offset)

  This zipfile constitutes the sole disk of a single-part archive; its
  central directory contains 1 entry.
  The central directory is 78 (000000000000004Eh) bytes long,
  and its (expected) offset in bytes from the beginning of the zipfile
  is 125 (000000000000007Dh).


Central directory entry #1:
---------------------------

  flag.txt

  offset of local header from start of archive:   0
                                                  (0000000000000000h) bytes
  file system or operating system of origin:      Unix
  version of encoding software:                   3.0
  minimum file system compatibility required:     MS-DOS, OS/2 or NT FAT
  minimum software version required to extract:   2.0
  compression method:                             deflated
  compression sub-type (deflation):               normal
  file security status:                           encrypted
  extended local header:                          yes
  file last modified on (DOS date/time):          2018 Oct 27 00:10:42
  file last modified on (UT extra field modtime): 2018 Oct 27 00:10:41 local
  file last modified on (UT extra field modtime): 2018 Oct 26 15:10:41 UTC
  32-bit CRC value (hex):                         571b1069
  compressed size:                                43 bytes
  uncompressed size:                              32 bytes
  length of filename:                             8 characters
  length of extra field:                          24 bytes
  length of file comment:                         0 characters
  disk number on which file begins:               disk 1
  apparent file type:                             text
  Unix file attributes (100644 octal):            -rw-r--r--
  MS-DOS file attributes (00 hex):                none

  The central-directory extra field contains:
  - A subfield with ID 0x5455 (universal time) and 5 data bytes.
    The local extra field has UTC/GMT modification/access times.
  - A subfield with ID 0x7875 (Unix UID/GID (any size)) and 11 data bytes:
    01 04 f6 01 00 00 04 14 00 00 00.

  There is no file comment.
```

```zipinfo```コマンドで、```flag.zip```の情報を見てみると、10月27日の00:10:41に修正されたことがわかります。

UNIX time で以上の時間は1540566641なので、これを入力すると、、、。

```bash
$ unzip
Archive:  flag.zip
[flag.zip] flag.txt password: 1540566641
replace flag.txt? [y]es, [n]o, [A]ll, [N]one, [r]ename: n
```

解けました。

```bash
SECCON{We1c0me_2_SECCONCTF2o18}
```

##### QR checker (Forensics)

次はQRコードに関する問題です。以下のサイトに、QRコードを読み込ませるためのwebサイトと、ソースコードが公開されています。

http://qrchecker.pwn.seccon.jp/cgi-bin/qr.cgi

```python
#!/usr/bin/env python3
import sys, io, cgi, os
from PIL import Image
import zbarlight
print("Content-Type: text/html")
print("")
codes = set()
sizes = [500, 250, 100, 50]
print('<html><body>')
print('<form action="' + os.path.basename(__file__) + '" method="post" enctype="multipart/form-data">')
print('<input type="file" name="uploadFile"/>')
print('<input type="submit" value="submit"/>')
print('</form>')
print('<pre>')
try:
	form = cgi.FieldStorage()
	data = form["uploadFile"].file.read(1024 * 256)
	image= Image.open(io.BytesIO(data))
	for sz in sizes:
		image = image.resize((sz, sz))
		result= zbarlight.scan_codes('qrcode', image)
		if result == None:
			break
		if 1 < len(result):
			break
		codes.add(result[0])
	for c in sorted(list(codes)):
		print(c.decode())
	if 1 < len(codes):
		print("SECCON{" + open("flag").read().rstrip() + "}")
except:
	pass
print('</pre>')
print('</body></html>')
```

ソースコードを見た感じ、1つのQR画像を500x500 250x250 100x100 50x50とリサイズし、そこから2つ以上のデータが読み込まれた場合のみflagが表示されるようになっています。

リサイズにより画像が荒くなったり潰れたりすることを利用しろということでしょうか。。。

とりあえず、ローカル環境で再現できるようセットアップを行います。

```python
!/usr/bin/env python3
import sys, io, cgi, os
from PIL import Image
import zbarlight
print("Content-Type: text/html")
print("")
codes = set()
sizes = [500, 250, 100, 50]
print('<html><body>')
print('<form action="' + os.path.basename(__file__) + '" method="post" enctype="multipart/form-data">')
print('<input type="file" name="uploadFile"/>')
print('<input type="submit" value="submit"/>')
print('</form>')
print('<pre>')
try:
	form = cgi.FieldStorage()
	data = form["uploadFile"].file.read(1024 * 256)
	image= Image.open(io.BytesIO(data))
	for sz in sizes:
		image = image.resize((sz, sz))
		result= zbarlight.scan_codes('qrcode', image)
		print(result)
		if result == None:
			break
		if 1 < len(result):
			break
		codes.add(result[0])
		print(codes)
	for c in sorted(list(codes)):
		print(c.decode())
	print(len(codes))
	if 1 < len(codes):
		print("SECCON{" + open("flag").read().rstrip() + "}")
except:
	pass
print('</pre>')
print('</body></html>')
```
まずはデバックできるように、```print(c.decode())```を追加しました。しかし、実行してみると```zbarlight```がないと怒られます。```zbarlight```をインストールしましょう。

https://github.com/Polyconseil/zbarlight

```sh
$ brew install zbar
$ export LDFLAGS="-L$(brew --prefix zbar)/lib"
$ export CFLAGS="-I$(brew --prefix zbar)/include"
$ pip install zbarlight
```

ここから、cgiサーバーを立ち上げて、ローカル環境で実行できるようにします。
具体的には、cgi-binフォルダを作成し、先程のファイルをその中に移動します。そして、以下のコマンドで簡易サーバを立ち上げます。

```sh
$ python -m http.server --cgi
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
127.0.0.1 - - [01/Nov/2018 16:59:45] "GET /cgi-bin/qr.python HTTP/1.1" 200 -
```

http://localhost:8000/cgi-bin/ファイル名 にアクセスするとローカル環境で動かせる事がわかります。

ローカル環境のセットアップはこれで完了です。

ここから、サイズを変えたり、QRコードを重ねたりいろいろしたのですが間に合わなかったです、、、。

ということで、こちらの方の[Writeup](https://graneed.hatenablog.com/entry/2018/10/28/150839)を拝見したところ["気まぐれコード"](https://news.yahoo.co.jp/byline/moriimasakatsu/20180629-00087130/)と呼ばれる、千人に一人、一万人に一人はフィッシングサイトに誘導されてしまうようなQRコードの存在を知りました。

上記のサイトに有るように、QRコードにおける[誤り訂正符号](http://www.qrcode.com/about/error_correction.html)部分が白と認識されるか黒と認識されるかで読み込まれるサイトが変わるような仕様になっています。

実際に、以下のように塗りつぶし、

![20181028023931.png](https://qiita-image-store.s3.amazonaws.com/0/182922/b41c928b-618b-4e44-2bdb-2c2f8e766e39.png)


これをローカル環境で読み込むと、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/182922/2b389143-ef79-fa4d-9bb1-f89c11979d37.png)


このように50x50にリサイズされた時、labがlobとして認識されることがわかりました。

これを実際の[サイト](http://qrchecker.pwn.seccon.jp/cgi-bin/qr.cgi)で読み込ませると、、、。

```
SECCON{50d7bc7542b5837a7c5b94cf2446b848}
```

flagが獲得できました。


明日以降も随時更新してきます！