好奇心の足跡

飽きっぽくすぐ他のことをしてしまうので、忘れないため・形にして頭に残すための備忘録。

SECCON 2018 Online CTF write-up

この記事は SECCON 2018 Online CTF に参加した際の記録になります。

  • 簡単な問題しか解けていませんのでご了承ください
  • しかも力技が多いです
  • ネタバレなので閲覧の際はご注意ください
  • write-upの記事に後追いの記録を徐々に追加更新予定

今回は一人チーム。前回2人チームで参加したけど簡単な問題しか解けない人&専門が同じ人同士でチーム参戦だと解ける問題かぶっちゃってそんなにメリットなかったので。
あと初日は殆ど参加できないのがわかってたので、一人でのんびり参加したかったのもあります。どうせ決勝とかはいけないしね。
3問解いて、425 point。173位ということは、2017より大分上がったかな?解いた問題数は大分少ないけど。

f:id:kusuwada:20181028151016p:plain

基本装備

  • MacBookAir(OS X 10.9くらい) 2011モデル
  • python3
  • GIMP 2.8.10
  • Wireshark
  • Hopper Disassembler
  • etc...

Unzip [Forensics]

問題

Unzip flag.zip.

zipファイルがDLできる。

解答

DLしたファイルは、長いのでunzip.zipにリネーム。
ファイル形式を調べてみる。zipだ。

$ file unzip.zip 
unzip.zip: Zip archive data, at least v1.0 to extract
$ unzip unzip.zip 
Archive:  unzip.zip
 extracting: flag.zip                
  inflating: makefile.sh             

flag.zipをunzipせよとのことだったので

$ unzip flag.zip 
Archive:  flag.zip
[flag.zip] flag.txt password: 
password incorrect--reenter: 
   skipping: flag.txt                incorrect password

passwordを聞かれた。
一緒に入ってた makefile.sh を見てみる。

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

ふむ、パスワードが perl -e "print time()" のようだ。試しに今の時刻をこのコマンドで取得してみる。

$ perl -e "print time()"
1540631715

UnixTimeが出てくるのね。 ということで、flag.zipが作成された日時(秒まで)を調べてUnixTimeに変換、これをパスワードにしてflag.zipを解凍すると、無事flagの書かれた平文txtが入手できました。

Runme [Reversing]

問題

Run me.

解答

配布されたファイルを確認。

$ file runme.exe_b834d0ce1d709affeedb1ee4c2f9c5d8ca4aac68 
runme.exe_b834d0ce1d709affeedb1ee4c2f9c5d8ca4aac68: PE32 executable (console) Intel 80386, for MS Windows

Windows exe ファイル!!!
取り急ぎ Wine on Mac で動かしてみると、 The environment is not correct と怒られた。
中身を見てみる。どうやら起動に成功したら、 Congratz You know the flag! と出てくるらしい。

Reverseなのでとりあえず、Macに入れていた "Hopper Disassembler" の Demo mode(ライセンス買ってない)で開いてみる。
やたらとProcess(関数)が多いが、中身はよく見てみるとほとんど一緒。全ての関数の中で、ascii文字列になりそうなレンジのhexがpushされている。
Reverse全然わからんので、50個あったけど力技で全部のhexを写経。asciiにするコードを組むと、flag出てきた!

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

list = [
    0x43,
    0x3a,
    0x5c,
    0x54,
    0x65,
    0x6d,
    0x70,
    0x5c,
    0x53,
    0x45,
    0x43,
    0x43,
    0x4f,
    0x4e,
    0x32,
    0x30,
    0x31,
    0x12,
    0x4f,
    0x6e,
    0x6c,
    0x69,
    0x6e,
    0x65,
    0x2e,
    0x65,
    0x78,
    0x65,
    0x22,
    0x20,
    0x53,
    0x45,
    0x43,
    0x43,
    0x4f,
    0x4e,
    0x7b,
    0x52,
    0x75,
    0x6e,
    0x6e,
    0x31,
    0x6e,
    0x36,
    0x5f,
    0x50,
    0x34,
    0x37,
    0x68,
    0x7d
]

answer_list = []
for h in list:
    answer_list.append(chr(h))
print(''.join(answer_list))

もっとスマートなやり方は絶対あるはず。Reverseはいつもスキップしてるから、解いただけ偉い!

QRChecker [QR]

問題

http://qrchecker.pwn.seccon.jp/

解答

cgiファイルが配布されているので、問題文のサイト上で動いているものと推測。
実際上記URLに行くと、

  • page
  • script

というリンクが有り、scriptの方のリンクはこのファイルと同等の内容でした。なぜ同じファイルも配布されたのかは謎。
最後の方を見ると、

for c in sorted(list(codes)):
        print(c.decode())
    if 1 < len(codes):
        print("SECCON{" + open("flag").read().rstrip() + "}")

とあるように、一定条件を満たすとflagが表示されるようだ。

試しに、下記サイトで "SECCON" というテキストを入れたQRコードを作成してpng形式でDLし、突っ込んでみた。

QR Code Generator

f:id:kusuwada:20181028150708p:plain

SECCON、とQRコードがデコードされて表示されている。

動きを理解するために、このコードをLocalで動かしてみる。
どうやらsizeが 500, 250, 100, 50 の全てでQRコード解読を試みており、いずれかのサイズで異なるコードが検出された場合、Flagが表示されるらしい!でも1つのサイズで2つ以上検出されるのもNG。
そんなんできる?

いろいろ試しに作ってみた。QRコードの文字数を多くすれば複雑性がまして細かいパターンになるので、小さめのresizeだと見えなくなりそうなことを利用して、まず1パターン作成。
もう一つは、解像度が高いとだめだけど小さくして細かいノイズが取れると読めるよになるようなコードを作成。2つのパターンを入れ子にして一つのQRコードを作成。
ノイズ具合を試行錯誤するのに時間を費やしつつ、完成したQRコードがこちら。

f:id:kusuwada:20181028150727p:plain

なげたらQRコードのテキストともにFlag出現。

f:id:kusuwada:20181028150746p:plain

こちらも、もっとスマートなやり方はあった気がする。