SECCON 2018 Online CTF Writeup

この大会は2018/10/27 15:00(JST)~2018/10/28 15:00(JST)に開催されました。
今回もチームで参戦。結果は 4245点で653チーム中7位でした。
自分で解けた問題をWriteupとして書いておきます。

mnemonic (Crypto)

$ cat mnemonic.txt
{
    "japanese": [
	[
	    "d3a02b9706507552f0e70709f1d4921275204365b4995feae1d949fb59c663cc",
	    "ふじみ あさひ みのう いっち いがく とない はづき ますく いせえび たれんと おとしもの おどろかす ことし おくりがな ちょうし ちきゅう さんきゃく こんとん せつだん ちしき ぬいくぎ まんなか たんい そっと",
	    "338c161dbdb47c570d5d75d5936e6a32178adde370b6774d40d97a51835d7fec88f859e0a6660891fc7758d451d744d5d3b1a1ebd1123e41d62d5a1550156b1f"
	],
	[
	    "dfc9708ac4b4e7f67be6b8e33486482cb363e81967a1569c6fd888b088046f7c",
	    "ほんやく ごうきゅう おさめる たこやき ごかん れいぎ やせる ふるい まんなか てんない だんろ さうな きぼう よくぼう しのぐ よけい こんき みうち らくご いわかん いこく あたためる のはら たぶん",
	    "bdadda5bbff97eb4fda0f11c7141bc3ce3de0fef0b2e4c47900858cec639c10187aee4695b1ba462b1dd34b170b62801e68c270b93af62629f4964947a620ed9"
	],
	[
	    "c0f...",
	    "??? とかす なおす よけい ちいさい さんらん けむり ていど かがく とかす そあく きあい ぶどう こうどう ねみみ にあう ねんぐ ひねる おまいり いちじ ぎゅうにく みりょく ろしゅつ あつめる",
	    "e9a..."
	],
    ],
    "flag": "SECCON{md5(c0f...)}"
}

日本語の名称やタイトルで調べると、タイトル通り、mnemonicの問題。
日本語のリストは以下にあった。

https://github.com/bitcoin/bips/blob/master/bip-0039/japanese.txt

さらに何かよいライブラリがないか調べると、python-mnemonicがあったので、試しにこれを使ってみる。

https://github.com/trezor/python-mnemonic/tree/master/mnemonic

ここにもワードリストがある。試すときに、コード中の最後の方にある部分をenglishからjapaneseに変える。

m = Mnemonic('english')
    ↓
m = Mnemonic('japanese')
$ python mnemonic.py d3a02b9706507552f0e70709f1d4921275204365b4995feae1d949fb59c663cc
ふじみ あさひ みのう いっち いがく とない はづき ますく いせえび たれんと おとしもの おどろかす ことし おくりがな ちょうし ちきゅう さんきゃく こんとん せつだん ちしき ぬいくぎ まんなか たんい そっと
$ python mnemonic.py dfc9708ac4b4e7f67be6b8e33486482cb363e81967a1569c6fd888b088046f7c
ほんやく ごうきゅう おさめる たこやき ごかん れいぎ やせる ふるい まんなか てんない だんろ さうな きぼう よくぼう しのぐ よけい こんき みうち らくご いわかん いこく あたためる のはら たぶん

問題の結果と一致する。この中のコードを見て、逆算する。ただし、1つ目のワードがわからないので、そこはブルートフォースする。
日本語の処理が面倒だったので、リストを見ながら、インデックス(何行目か)を抽出する。その結果を含めたコードは以下の通り。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import hashlib

def decrypt(val):
    idxes = [val, 1333, 1376, 1953, 1173, 777, 570, 1262, 337, 1333, 995, 375,
        1706, 616, 1485, 1404, 1495, 1644, 297, 91, 444, 1844, 2030, 24]

    b = ''
    for idx in idxes:
        b += bin(idx)[2:].zfill(11)
    b = b[:-8]

    data = ''
    for i in range(0, len(b), 8):
        data += chr(int(b[i:i+8], 2))

    return data.encode('hex')

pre = 'c0f'

for i in range(2**11):
    h = decrypt(i)
    if h.startswith(pre):
        print 'val =', str(i)
        print 'h =', h
        break

flag = 'SECCON{' + hashlib.md5(h).hexdigest() + '}'
print flag
SECCON{cda2cb1742d1b6fc21d05c879c263eec}

でもよく考えたら、ブルートフォースする必要はなかった。11bitごとに最初の単語がきまるので、適当な単語で復号して、先頭3文字をc0fにするだけでよかった。