SECCON 2017 Online writeup

yharima チームで参加。 84th、1900点でした。

自分がフラグを提出した問題だけまとめる。

Vigenere3d

vigenere 暗号だけれど、表が二次元ではなく三次元になっているという問題。

三次元になってはいるが、s として定義されている文字列上で何文字ずらしたかさえ結局分かればいい。 鍵は14文字だが、その鍵で暗号化したあとに、反転した文字列を鍵として暗号化するのと同じことをやっている。 つまり、最初7文字で何文字ずらせばよいかわかれば、それを反転させて次の7文字に適用すればよい。 最初7文字が SECCON{ になることを利用すれば、最初の7文字何文字ずらせばいいかわかる。

テキストでうまく説明できなかったので、ソースコードをそのまま貼り付けておく。

#!/usr/bin/env python

s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz_{}"
e = "POR4dnyTLHBfwbxAAZhe}}ocZR3Cxcftw9"
hint = "SECCON{"

key_half = []
for i in range(len(hint)):
    c1 = hint[i]
    c2 = e[i]
    k1 = s.find(c1)
    k2 = s.find(c2)
    d = (k2 - k1) % len(s)
    key_half.append(d)
key = key_half + key_half[::-1]

text = ''
for i in range(len(e)):
    c2 = e[i]
    d = key[i % len(key)]
    k2 = s.find(c2)
    k1 = (k2 - d) % len(s)
    c1 = s[k1]
    text += c1
print text

フラグは SECCON{Welc0me_to_SECCON_CTF_2017}

Qubic Rube

QR コードの問題は毎年担当しているので、勇んで解いた。

ブラウザ上でQRコードが各面に貼られたルービックキューブが回転する様が描画されるので、QRコードを読み込んで次のURLを見つけてフラグまでたどり着くという問題。 50問ある上、ルービックキューブなので回転を加えられてQRコード部分がぐちゃぐちゃになるので、人手で解くのは厳しい。 はじめルービックキューブ的に回転するとは思っていなくて2問目に飛んだ時にショックを受けたのはいい思い出。

手順としては次のように解いた。

ブラウザ上で描画しており、各面のテクスチャはページのソースの中で見つかるので、まずはそれをダウンロードしてくる。

画像を3x3の9領域に相当するピースに分割し、各面の色ごとに角4つ、辺4つ、中心1つの9つのピースをまとめる。

次のことを考慮して、ありえる組み合わせを列挙していく

  • 角にあったピースが辺や中心にいったり、中心にあったピースが角や辺にいくことはありえない
  • 角のピースでもともと画像の角に位置していた点は、復元時も画像の角に位置していなければならない
  • 辺のピースでもともと画像の辺に位置していた辺は、復元時も画像の辺に位置しなければならない

中心のピース以外は位置が決まれば向きが決まることになる。 そこで、中心のピースは固定し、角と辺のピースの位置を適当に決める。 1面当たり 4! * 4! = 576 通りの候補が出てくる。

あとは適当な QR コード解読用のライブラリに画像を食わせて、QRコードとして読み取れるまで走らせるだけ。

まじめに回転とかを考慮するともっと速くできたのだろうけど、1時間近く走らせれば終わるペースだったのでそのまま放置した。

フラグは SECCON{Thanks to Denso Wave for inventing the QR code}

SHA-1 is dead

sha1 が一致する 2017KB ~ 2018KB のサイズのファイルをアップロードすればよいという問題。 かの有名な SHAttered の pdf を https://shattered.it/ からお借りした。 ファイルの最初の方でハッシュ値を計算に用いられる状態が一致して、あとは同じバイト列が続いているので、必要なサイズになるまで適当な文字列をつなげればよい。

$ python -c 'print "A" * 1643000' > tail.txt
$ cat shattered-1.pdf tail.txt > hoge1.bin
$ cat shattered-2.pdf tail.txt > hoge2.bin
$ sha1sum hoge1.bin 
d05003cacf7e3c4f4dc01997f57cae7d5efb8485  hoge1.bin
$ sha1sum hoge2.bin 
d05003cacf7e3c4f4dc01997f57cae7d5efb8485  hoge2.bin

フラグは SECCON{SHA-1_1995-2017?}

Log Search

ログを検索するページが与えられて、フラグを探す問題。

最初SQLインジェクションでもするのかと思ったが、クエリをいろいろ投げているうちに、Lucene ベースの検索エンジンであることに気づく。 フィールド名はテーブルにそのまま書いてあったので、フィールド指定したり、OR/AND 検索などもできた。

問題公開前のログを見ればいいかと思い、次のようなクエリを投げた。

timestamp:"09/Dec/2017" AND -(timestamp:"09/Dec/2017:15" OR timestamp:"09/Dec/2017:16" OR timestamp:"09/Dec/2017:17" OR timestamp:"09/Dec/2017:18" OR timestamp:"09/Dec/2017:19") AND request:flag AND response:200

12/08以前のログはなさそうだったので、12/09 に絞っている(必要なかった気もする)。 このクエリをなげたとき19時代だったので、15~19時のログがヒットしないようにした。 さらに、flag もどきのページにアクセスして 404 が返ってきているようだったので、それを除くために response:200 を付けた。 ちなみに timestamp が日付型だと思ってレンジクエリを投げてうまくいかずはまった。

このクエリで検索するとログが一件だけひっかかり、そこにアクセスするとフラグが見える。
フラグは SECCON{N0SQL_1njection_for_Elasticsearch!}

Ps and Qs

RSA の公開鍵二つと暗号文が渡されるので、それを解読する問題。

公開鍵が二つあったので、とりあえず modulus の gcd をとったら素因数分解できた。 あとは rsatool に秘密鍵を作ってもらって openssl で解読。

フラグは SECCON{1234567890ABCDEF}

Man-in-the-middle on SECP384R1

楕円曲線暗号を使ったやりとりに、中間者攻撃をしかける問題。
これで後半の時間をすべて溶かした。 解けたからよかったものの他の問題に手を出すべきだったかもしれない。

何もしないと dev0 と dev1 の間のやり取りを模したデータが送られてくるだけ。 ただし、データを受信してから一定時間内にペイロードを送ると、中間者としてデータをすりかえたことになるらしい。

初めは 楕円曲線暗号用の公開鍵(DER形式)をやりとりしているので、適当に鍵ペアを作って公開鍵を dev0 と dev1 に送り付ける。
秘密鍵と公開鍵は次のようにして openssl で作成した。 楕円曲線としては SECP384R1 を選択する。

$ openssl ecparam -name secp384r1 -genkey > my.key
$ openssl ec -pubout < my.key > my.pub

正しい形式で送ると OK とレスポンスが返ってくるので、これで公開鍵は正しく受け渡せた。

次に、共通鍵を作成する。
ECDH で鍵共有が行われているようだったので、まず共有する秘密(楕円曲線上の座標)を作成する。 楕円曲線暗号では、秘密鍵は適当な整数値が1つであり、公開鍵はベースポイントG * 秘密鍵の整数値に相当する座標となっている。 相手からもらった公開鍵中の座標に自分の秘密鍵に相当する値をかけることで、共有する秘密であるところの楕円曲線上の座標が求められる。 楕円曲線上でのスカラー倍算の実行には python の fastecdsa パッケージを利用した。
さらに、[KBKDF: SHA256, Encryption: AES] と送られてくるので、SHA256 でハッシュ化して鍵を作成して AES で暗号化/復号化を行えばいいらしい。 SHA256 に得られた楕円曲線上の座標のx座標を食わせてdev0, dev1 それぞれの共通鍵を作成した。

そして、dev0 用の共通鍵でAES-CBC で dev0 から送られてきた暗号文を復号すると 0000....0000 と出てきた。 このとき、先頭16バイトが iv だと思って復号したが、これが大きな過ちだった。 適当な iv で 0000...0000 (240文字) を暗号化して送っても、dev1 から返答はなかった。

dev0 から送られてくるデータ 256 byte は全て暗号文で、iv は送られてくるデータに入ってきていなかった。 後半 240 バイトは iv にかかわらず復号できるので、その 240 バイト (0000....0000) をもとに暗号文の最初 16 バイトから本当の iv が復元できた。 ちなみに test message0000 だった。

これを iv として、0000...0000(256バイト) を dev1 用の共通鍵で暗号化し、dev1 に送ると無事レスポンスが得られた。 さらに、得られたデータを dev1 用の共通鍵で復号すると、だいたいうまく復号できたが、iv がわからないので最初16バイトが不明となった。 復号文の最後の文字が } だったのでこれがフラグだろうということで、初め7バイトがSECCON{ になるよう iv を予想したところ、iv の先頭7バイトは '0000000' となった。 残りも 0 だろうということで iv を 0000...0000(16バイト)にしたところ、フラグが出た。

フラグは SECCON{FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFCB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF00000000000000000000000000000000000000000000000000000000}
(長い)

反省

man in the middle で時間を溶かしすぎた。
z80 と Very smooth は面白そうだったのでやりたかった。
pwn に手が出なかったので基礎力をつけたい。

関連

チーム yharima のメンバの writeup

yuta1024.hateblo.jp