kstm 光芒祭CTF 2025 回答速報
本日は信州大学工学部光芒祭にお越しくださりありがとうございました!
今回の開催では、延べ42のグループの皆様に参加していただきました。私達のブースまで足を運んでいただき、ありがとうございます。
この記事では、kstmの出展のCTFに登場した問題について、簡単な解説をします。
問題解説
アノニマス大学の受難【Medium 500pts】
この問題は、
- ブルートフォース攻撃(総当たり攻撃)
- IDOR脆弱性を利用した攻撃
この2つの攻撃方法を駆使することを主題とした問題です。
まず、ブルートフォース攻撃を行うJavaScriptのプログラムファイルが添付されています。これを小改造してロッカーシステムのログインページで実行することで、総当たりでパスワードの突破を行い(実際は結構な時間お待ちいただきました)量子コーヒー研究部(ID:qcrd)のロッカーに入ります。
次にURLの
mypage?user-id=qcrd
のid部分をukkrに変更することで、「ものつくらないサークルukkr」のロッカーに入り込むことができ、フラッグを得ることが出来ます。IDOR(Insecure Direct Object Reference / 不安全な直接オブジェクト参照)脆弱性の見落としがあったようです。
なお、現実のサイトではこのような攻撃が用意に成立しないように設計されています。ukkrがうっかりしていたのか、非常に脆弱で危険なシステムとなっていたのです。
北陸のロマン【Medium 400pts】
この問題は、JPEGファイルに保存されたExifデータに何が保存されているかを主題とした問題です。
どの電車に乗っていたかを特定するには画像の撮影時刻を知ることが重要です。しかし、この画像には時計やその類のものは写っていません。そこで、利用するのがJPEGのExifにある撮影時刻のデータです。画像をダウンロードして、このデータを見ると、撮影時刻が
2025/09/28 13:21
とわかります。
あとは適当な乗り換え検索サービスで大阪から北陸新幹線経由で長野まで検索すると、13:21に長野駅に到着できる列車が「サンダーバード11号から、はくたか562号への乗り継ぎ」と分かります。
(当面の間はYahoo!路線情報をご覧いただくことで、実際にサンダーバード11号が正解として妥当であることが分かるかと思います)
ところで、文章中にて「列車番号」と表記したことで、答えを4011(M)と誤解させてしまった事例が発生しました。「号数」というのが正しかったですね。
消えたデータを追え!【Hard 700pts】
この問題は、Webフロントエンドの基本的な技術(HTML, CSS, JavaScript)に関する知識を問い、開発者ツールをいかに使いこなせるかを試すものです。フラグは3つのパーツに分割され、それぞれ異なる場所に隠されています。
ステップ1: ヒントを下にした取っ掛かり
まず、問題文のヒントにブラウザの開発者ツールを使用が不可欠と書かれています。
ステップ2: コンソールの確認 - 第1のキー
指示通りに開発者ツールのコンソールを開くと、script.js によって出力された以下のメッセージが表示されています。
最初のキーのかけら: ZmxhZ3t3ZWJf
ヒント: この文字列は特殊な方法でエンコードされているようだ。'atob()'関数が役に立つかもしれない。
ここで第1のキーのかけら ZmxhZ3t3ZWJf を入手できます。ヒントにある通り、これはBase64でエンコードされた文字列です。コンソールで atob() 関数を使い、デコードします。
atob('ZmxhZ3t3ZWJf')
// 実行結果: "flag{web_"
これにより、フラグの先頭部分 flag{web_ を得ることができます。
ステップ3: CSSの調査 - 第2のキー
次に、HTMLやCSSに隠された情報を探します。index.html には .secret-message というクラス名を持つpタグが存在します。
<p class="secret-message">見た目だけが全てではない。</p>
しかし、このテキストはブラウザ上では見えません。style.css を確認すると、その理由がわかります。
.secret-message {
color: #16213e; /* 背景色と同じにして見えなくしている */
}
.secret-message::after {
content: "X2tleV9pc19k"; /* キーの一部 */
}
テキストの色が背景色と同じに設定されているため、不可視になっていました。さらに重要なのは、::after 擬似要素の content プロパティに、第2のキーのかけら X2tleV9pc19k が設定されていることです。
これも同様にBase64デコードを行います。
atob('X2tleV9pc19k')
// 実行結果: "_key_is_d"
これにより、フラグの中間部分 _key_is_d を得ることができます。
ステップ4: JavaScriptの解析 - 第3のキー
最後に script.js を詳細に調査します。ファイルの下部に、どこからも呼び出されていない(未使用の)関数 getThirdKeyPart() が存在します。
// 誰も気づかない関数...
// この関数はどこからも呼び出されていない。
function getThirdKeyPart() {
// 16進数から文字列へ
const hex = 'MzA2ZWN9';
return hex;
}
なお、コメントが実態を表しておらず実際はBase64エンコードで、atobがそのまま使えます。
この関数をコンソールで実行してみます。
getThirdKeyPart()
// 実行結果: "MzA2ZWN9"
これで第3のキーのかけら MzA2ZWN9 を入手できました。これもBase64デコードします。
atob('MzA2ZWN9')
// 実行結果: "306ec}"
これにより、フラグの末尾部分 306ec} が得られます。
ステップ5: フラグの組み立て
ステップ2、3、4で入手した3つのパーツを発見した順に結合します。
flag{web__key_is_d306ec}
結合した結果、最終的なフラグが完成します。
flag{web__key_is_d306ec}
暗号化メール事件【Easy 200pts】
この問題は、シーザー暗号をテーマとした問題です。
シーザー暗号は、アルファベットを指定の回数ずらすことで暗号化する手法です。問題では回転数は13と指定されていたので、x→kになるように13個ずらすと解答が得られました。
問題文に与えられていたメール
Uryyb, V'z gur frperg cebwrpg bs bhe sbyybjvat:
xfgz{PGS_jvgu_ebg13!}
は復号することで、
Hello, I'm the secret project of our following:
kstm{CTF_with_rot13!}
となり、フラッグkstm{CTF_with_rot13!}が得られます。
灯台下暗し【Easy 100pts】
この問題は、JavaScript(Webブラウザ)のblurイベントを利用した問題です。
イベントというのは、ユーザーの操作や、ダウンロードの完了など、いつ来るかわからない出来事が発生したあとにプログラムを実行するという仕組みです。
blurイベントは、ブラウザのタブが非アクティブになったときに発生するイベントです。これに呼応させて、タブのタイトルを動的に変更するようにしています。これを利用して、ヒントボタンを押して別のタブが開いたタイミングで、問題ページのタブタイトルがフラッグになるようになっています。
機関車を走らせろ!【Easy 100pts】
この問題は、Unix系OSのジョークコマンド、slコマンドを改造した問題です。
ターミナル上にてslコマンドを利用して、表示される機関車にフラッグが表示されるようになっています。poppo-!
感想
- 「たくさん来てくれて、ありがとう!」というお気持ちです。本当にここまで来てくれるとは思いませんでした。今年とったアンケートを元に来年は更に良いものを作りたいですね!一年後、また光芒祭でお会いできることを楽しみにしています!(ochika・「北陸のロマン」作者・解説者)
- 昨年の反省を生かし、いつでもスタート可能、大きな机、インターネット接続の大改革の上で、問題の数を少なくするようにしました。「難しいけど面白い」と言っていただけることが多くて、この企画を開始して本当によかったと思っています。来年もさらにパワーアップして皆様とお会いできることを楽しみにしております!
この場をお借りして、本企画のために機材や環境をセットアップしてお貸しいただいた信州大学の関係者の皆様にお礼申し上げます。ありがとうございました。(高原のな・解説の確認者)