exits

勉強記録

ksnctf C92 WriteUp

この記事は Harekaze Advent Calendar 2017 の15日目の記事です。
チームHarekazeでは、2018年2月10日にHarekaze CTF 2018を開催します。

ksnctf C92 WriteUp

最近全然CTF系のことをできていないのですが、Advent Calendarに何を書くか考えていて、夏に隣の席の人からある本を手に入れたことを思い出しました。
f:id:yue82:20171214233718j:plain:w300
ksnctf C92 です。
ksnctfのコミケC92向けスピンオフだそうです。
上記の写真の本はコミケ当日に頒布された解説本です。

コンテスト自体については早めにサインアップしたもののその後放置という感じでした。
f:id:yue82:20171214233913p:plain
今回は夏に買ったこの解説本を読みながら、冬になった今ちょっとだけ自分でやってみたよーという内容です。

E1 - Mysterious Light


flag.pngファイルに謎の光に隠されたQRコードが載っています。
QRコードのカドの位置検出パターンを塗るとQRコードが完成し、QRコードリーダーで読むことができるようになります。
f:id:yue82:20171214234035p:plain:w400

FLAG{QyDYkTQNd1lb1IDH}

flag2.pngに巨大なQRコードが載っていて、コードの大部分は隠されていませんが、ほんの少しだけ謎の光で隠されています。
この状態ではQRコードリーダーで読めません。隅の位置検出パターンを見つけているような気配はありますが、一向に読み取りは進みません。

位置検出パターンの周囲には以下の2つの形式情報が載っています。

  • QRコードの白黒がまんべんなく散らばるようにかけるマスクとして8種類のうちどのマスクを使用したか
  • 4種類の誤り訂正レベル

この2つが欠けると読み取りを進めることができません。
形式情報は3つの隅に分散・多重化して記述されていますが、謎の光はちょうど形式情報が配置されているところを隠してしまっています。

マスクと誤り訂正レベルの組み合わせはそんなに多くないので総当りができます。
その2つをオプションとして渡せる強力なQRコードデコーダーがstrong-qr-decoderです。

QRコードのデータは明暗を表す記号(と不明を表す?)で記述したファイルで渡す必要があります。
画像から記号表記に直すコードはこんな感じ。

XXXXXXX_??_XX__XX_XXX__X___XX__XX__X_XX_X__X_XXXXXX_XX_X__X_X__XXX_X_XX__X_X_____XXX______XX______XXXXX__XX__XXXX_X_X__XXXX___XX__XXX____XXX_X_XX__X_XXX___X_X__X___X_X___XXXXXXX
X_____X_??XXXX__XXXXXX_X___XX_XX_XXXX__X_X_X__X_____X__X_XXX___XXXX_____X____X_XXXXXXX_X_X___XX_X_X___XXX__X_XXXXX_XX_____X________XXXX__XX__XXXX__X_XXXX_XX_X_X_X_XX_X_X_X_____X
X_XXX_X_??___XXXXX_X__X_X_XXX__XX_XX_XXX_XXX_X__X__X____X____XXXXXX_X__X___XXX_XX__X_X_X___XXX__XX__XXX__XXX______X_XXX__XX_XX__X___X__X_X_X____X___XX____X____X_X__XXX___X_XXX_X
X_XXX_X_??XX_X____X_________X_XX___XXX_X_X_XXX____X_XX_X_X__X____X__XX_XX_X_XX_X__X__X_X_XXXXXXX_XXX__X_XX__X_XXXXX_X____X___XXXX__X_X__XXX____XXX_XXX__X_XX_X____XX___XX_X_XXX_X
X_XXX_X_??XXXXXXX_XX_XXXX__XXXXXX_XXX_XXXXXX_X____X___XXXXXXX_X___X_XX__X______XX_XXXXXXX___XX_X____X__XX_X_X_X_XXXXX_X__X_X_XXXX_XX___X_XXXXXXXX_X_X___X___X____XX_______X_XXX_X
X_____X_??XX_XXXX_X_XXXXX___X___XX__X__XX_X__XXX_X__XXXXX___X___XX_X__XX__X_XX__XX_XX___X___X___XX_XX_XX_XX___XXX___XXXX_XXX_X_X_XX_XXX___XXX___XX_XX__________XX_____X_X_X_____X
XXXXXXX_??X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_XXXXXXX
_______???_X_XXXXXXX_XX_XX__X___X___X_XX____X_XXX_X__X__X___X_X__XXXXXXXXXX______X__X___X__X___XX__X__XX___X___XX___X__X__X__X_XX_XXX__X_X_XX___X_XXX____X_XXXXX_____X___________
??????????X___XXX_XX____X___XXXXXX___XXXXX_X_X_____X_X_XXXXXX_XXXX_X___XX_XXX_______XXXXXXXXX_XX_XX_X_X__XXXXXX_XXXXXXX_X____X_X_XX____X___XXXXXX_XXXXX______XXXXXX__X_XX????????
??????????__X_XXXX_______XX__XX__XX_XXXX__X_____XXXXXXXXX__X_____X_X__XX__XX___________X____XXXX____XXX_XX__XXXX_X_XX__XX_X___XX_X_XX__X_X_____XX__XXXX__XXXX__XX_XXX__XX????????
XX____XXXX____XXX_XXX_X_X__X__X__X_XX_X_XX_X__X__X_X___XX__XX____X_X_XX_X_______XXXX_____XX_XXXX___XX_XXX__XX_XX__XXXX_X__XXX____X__X__X_X_XX__XXXXX_XXXXX___XXX__XXX_X_X_____X_X
XX_X_X____X__XX__XXX___X_X_X__XX__XXXX_XXX____X_X_XX_X_____X_____X_X_XXXX_X_XX____X_X____X_X_X____XX_X___X_X_X_X__XX___X___XX___XXX_XXXXXX____XXX___XX_XXXX____X_X_X_XX__X_X_X__X
_XXX__XX_____XX_XXXX_XX___XX_X______XX_X_X_X___X_XXXXXX_X___XXXXX__X_X_X_XXX_X_XX__XX__X__X_XXXXXX__X__X_X_X_XXXXX_X___X_X__XXX_X__X_X_XXXX___X_X__XXXXX___XXXX_X___XX____XX___X_
_X__X__X_____X_XX__X_XXXX_X______X___XXXX_XXXXXXX__X_XXXXXXXX__XXXXXX_X_XXX__XXXX_XX___XXX__X_XX_X_X__X_XXX__XXXXX__X__X_X_X_X_XX______X___XXX____X_____XX_X___XXX_X_XX_XXX__XXX_
いかりゃく

これをstrong-qr-decoderに、パラメータを変えて投げます。

$ for ((i = 0; i < 8; i++))
do
for ((j = 0; j < 4; j++))
do
python2 sqrd.py -e $i -m $j flag2.txt
done
done

error: 未対応のモード指示子です
error: 未対応のモード指示子です
Traceback (most recent call last):
  File "sqrd.py", line 1056, in <module>
    data.extend([ord(alphanumeric_table[num // 45]), ord(alphanumeric_table[num % 45])])
IndexError: list index out of range
Traceback (most recent call last):
  File "sqrd.py", line 1072, in <module>
    data.append(int(data_bits[:8], 2))
ValueError: invalid literal for int() with base 2: ''
error: 未対応のモード指示子です
error: 未対応のモード指示子です
Traceback (most recent call last):
  File "sqrd.py", line 1056, in <module>
    data.extend([ord(alphanumeric_table[num // 45]), ord(alphanumeric_table[num % 45])])
IndexError: list index out of range
error: 誤り訂正に失敗しました。--no-correctionオプションの仕様を検討してください
error: 誤り訂正に失敗しました。--no-correctionオプションの仕様を検討してください
error: 未対応のモード指示子です
Traceback (most recent call last):
  File "sqrd.py", line 1056, in <module>
    data.extend([ord(alphanumeric_table[num // 45]), ord(alphanumeric_table[num % 45])])
IndexError: list index out of range
FLAG{sttzFKrw6NY1VCd6zZrUL7f7bmba86f7B3f63XbZDAacbNfopNJpuyAsu0rHZ9KIrIBtYe0jg9jg4ke2rXg2dDWsoreTpTR31tSbFBVtxnzVIWyQIjfRQNswaYwG1tnM06MmtweK4fxMCrIKjB7Id5Zo7g4Xe0VFJtXpS5LoIFifp7Zby3j2a4LAnaK3buZBc95WcaAzERfGX1aba70cYYA2WCVS36w5rFDYtC8YVqGfAbrM6nO5bU4AFupjmB5WOiToscqK38a3KQHNvNvN4I8YePjeEbxPBBHO3g141EqskqODlOvoL7ZvWxwACyEJE62iaW18JTXd8ZvPupcVX1fc2pSyY68vTarClvEFX2WV3GMmK8rZUnhCWjfEuqxdfWnteD9FxzDedqwn3bvTUP8Mf2oGM0fxsa8R56n5jVP6o45U1tLTaWGjHiSHg4NEC1y1aLLvqgdCfHJTUY2aRcfFUauR5NiJMYkqiKNe7h3FWt9PYEJTYeV8v93Iarv8sKLvELrVnxjbLB6QKoIYrSYgox71DLRw5Pipah68GPkKYjUKnYESxSfhtqI72hRZ3sIbi2O3g8W7jfK900lZVKXQG3vqpRgynKcMQvIvOaryfwfiTq26g7SnuEvRpNxZbx0OOk3uws1tnYuKU9NyYnVI7RHU5tIPkFFvCFRI9ggBIAXDeIGa93vmQu01VoaKslimfE3e85A94F8EGmu7iQtVntUdq7EdAX7wJiZ3Z26aBjZxrLOQiCh5WL6mcDfIB8W9z5tCCf1aKbRrchTXMeN24bmextbzcp1MVs4gX9XTziS7oKxorCHSy7pNZsj39z4Y96lCkBFErtPxlSN9s5s9ZNVJK1LASOowu3Sea6IXA2SJsbqLT6j7tbVwpIb7TuhrXzL6gLdquh57TgCX3ZBBQrFTCAUUu0xDakU7DTWOI2Rm2YoKxo4IFKQhin546VjG6MKLJsBWi7fvhnUNtrQM5fChIALMovG7uQMqhFbRSdUcRSayBzKQTjZP9sZGXc0ZiaXCo5OQMbJoKglrdJshqYepRfjVpvXtLfvqOIuj85mKHJz2Yn3v1fcYO7hJ3k4phDDDyPVHcb4ZtcxfhEjH3XuSCcG2kfy1WMFRvnmbjGr6K7In3JTULqQxpxCuM4aFJy2AlS23jclhFbhGkCDMVHu0CGzmnOM56IouIsC29LTqdsAj5W1ts8zbBiIDNCNW8xxXUSAZN52tUc2jXK29sxZaz65HDLjGlphb5wavmq6t4Ez}
error: 未対応のモード指示子です
いかりゃく

FLAG{sttzFKrw6NY1VCd6zZrUL7f7bmba86f7B3f63XbZDAacbNfopNJpuyAsu0rHZ9KIrIBtYe0jg9jg4ke2rXg2dDWsoreTpTR31tSbFBVtxnzVIWyQIjfRQNswaYwG1tnM06MmtweK4fxMCrIKjB7Id5Zo7g4Xe0VFJtXpS5LoIFifp7Zby3j2a4LAnaK3buZBc95WcaAzERfGX1aba70cYYA2WCVS36w5rFDYtC8YVqGfAbrM6nO5bU4AFupjmB5WOiToscqK38a3KQHNvNvN4I8YePjeEbxPBBHO3g141EqskqODlOvoL7ZvWxwACyEJE62iaW18JTXd8ZvPupcVX1fc2pSyY68vTarClvEFX2WV3GMmK8rZUnhCWjfEuqxdfWnteD9FxzDedqwn3bvTUP8Mf2oGM0fxsa8R56n5jVP6o45U1tLTaWGjHiSHg4NEC1y1aLLvqgdCfHJTUY2aRcfFUauR5NiJMYkqiKNe7h3FWt9PYEJTYeV8v93Iarv8sKLvELrVnxjbLB6QKoIYrSYgox71DLRw5Pipah68GPkKYjUKnYESxSfhtqI72hRZ3sIbi2O3g8W7jfK900lZVKXQG3vqpRgynKcMQvIvOaryfwfiTq26g7SnuEvRpNxZbx0OOk3uws1tnYuKU9NyYnVI7RHU5tIPkFFvCFRI9ggBIAXDeIGa93vmQu01VoaKslimfE3e85A94F8EGmu7iQtVntUdq7EdAX7wJiZ3Z26aBjZxrLOQiCh5WL6mcDfIB8W9z5tCCf1aKbRrchTXMeN24bmextbzcp1MVs4gX9XTziS7oKxorCHSy7pNZsj39z4Y96lCkBFErtPxlSN9s5s9ZNVJK1LASOowu3Sea6IXA2SJsbqLT6j7tbVwpIb7TuhrXzL6gLdquh57TgCX3ZBBQrFTCAUUu0xDakU7DTWOI2Rm2YoKxo4IFKQhin546VjG6MKLJsBWi7fvhnUNtrQM5fChIALMovG7uQMqhFbRSdUcRSayBzKQTjZP9sZGXc0ZiaXCo5OQMbJoKglrdJshqYepRfjVpvXtLfvqOIuj85mKHJz2Yn3v1fcYO7hJ3k4phDDDyPVHcb4ZtcxfhEjH3XuSCcG2kfy1WMFRvnmbjGr6K7In3JTULqQxpxCuM4aFJy2AlS23jclhFbhGkCDMVHu0CGzmnOM56IouIsC29LTqdsAj5W1ts8zbBiIDNCNW8xxXUSAZN52tUc2jXK29sxZaz65HDLjGlphb5wavmq6t4Ez}

E5 - Not Guilty?

pcapファイルなのでWireSharkで開きます。
プロトコルの列を見ると802.11と書いてあります。Wi-Fiですね。 photo.jpgによるとWPA2らしいのでまぁ暗号化してあるんだな―という感じです。

WPA2のパスワードはphoto.jpg内のカードに書いてあり、WireSharkではパスワードを使って復号ができるそうです。便利。
Edit/Preferences/Protocols/IEEE802 f:id:yue82:20171214235004p:plain:w400

Enable Decriptionにチェックを入れ、DecryptionKeysのEditからWPAのパスワード認証でパスワードを設定します。

そうすると暗号化で真っ白だったストリームが複合されてHTTPの混ざったTCPストリームになりました。
f:id:yue82:20171214235018p:plain:w400

WireSharkは便利なものでHTTPストリームの中に含まれるコンテンツをエクスポートできるそうです。

File/Export Objects/HTTP
f:id:yue82:20171214235028p:plain

flag.jpgが見つかりました。
f:id:yue82:20171214235038j:plain:w400

FLAG{bBVqBLqTxy3pR4F8}

E6 - flower

1

StegSolveでポチポチします。
Blue plane 1で発見。
f:id:yue82:20171214235051j:plain:w400

FLAG{ZPf1dTqXQcbD0ygl}

2

1問めで見つけたヒントに従って解きます。 IDATはpngのチャンクのうち、画像本体を含むチャンクだそうです。 チャンク毎の表示、編集にはTweakPNGが便利とのことで見てみました。

f:id:yue82:20171214235107p:plain:w400

最後のIDATだけやけに大きく、それまでのIDATのlengthは最大49~125くらい・・・

$ ipython
Python 3.6.1 (default, Mar 24 2017, 12:50:34)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: ''.join([chr(i) for i in [70,76,65,71,123,49,106,109,109,90,83,76,52,65,55,70,83,86,121,66,122,125]])
Out[1]: 'FLAG{1jmmZSL4A7FSVyBz}'

FLAG{1jmmZSL4A7FSVyBz}

3

Hint2は 下を見ろと言っています。 TweakPNGはpngのデータ編集もできるのでIHDRチャンクからEditで
f:id:yue82:20171214235146p:plain

べんり。

FLAG{ozwPlyihMxgnxey0}

所感

色々やってみるより前に本を手に入れた(そして放置していた)ので、ネタバレというか先に読んでから解いてしまった問題が多かったです。反省。 あとだいたいSteganographyとか画像っぽいのとかpcapとかMiscばっかり見てしまうのでそろそろ他のもちゃんと勉強したいなとは思いました。やるとは言っていません。

2018年2月10日にHarekaze CTF 2018が開催されます。

せっかくメンバーに入れてもらっているので雑用を色々お手伝いしてます。

作問もする予定・・・
ネタというか方向性だけは一案あるんですがまだ全然考えてません。がんばります。
僕以外のみなさんは作問たくさんしててすごーいな感じです。勉強します。