WaniCTF2020 開催記
概要
大阪大学CTFチームWani Hackaseは初心者向けCTF大会WaniCTFを11/21(土) 10:00 ~ 11/23(月) 20:00の日程で開催しました。
187名の方に問題を解いていただき、6名の方が全完を達成されました。ランキングページ
経緯
Wani Hackaseとして活動し始めてから2年が経ち、「そろそろ外部公開CTFを開催したい」という感じで自分が中心となって9月末から動き始めました。また、チームの内部事情として現在B1とB2のメンバーがいないので在学生にも接点を持つことができる学園祭(まちかね祭)に出展して開催することに決めました。
まちかね祭は高校生&大学1~2年生がメイン層なので、初心者向けの問題を中心に中級者向けの問題をいくつか加え、Wani Hackaseメンバーが時間を掛ければ解き切れるレベルの問題セットを提供することにしました。
しかし、11月2日に大会開催の告知を行ってから9日後の11月11日にまちかね祭の延期が発表されました。私達はまちかね祭の延期日程の見通しが立っていないこと、すでに大会開催の告知を行っていることなどから単独のイベントとしてまちかね祭の開催日程に合わせていた日程から1日短縮した上で開催することに決定し、11月12日に告知を行いました。
直前での開催日程の変更を行い申し訳ありませんでした。この場を借りてお詫び申し上げます。
作問
Wani Hackaseでは毎週2人が作問を行ってメンバー全員で解く勉強会を行っており、そこそこの問題ストックがあります。この中から「初心者の人に知ってほしい脆弱性や攻撃手法」が含まれる問題をピックアップしたり、新規に作問した問題をチーム内スコアサーバーに登録して全員で解きながら
- 適正な難易度か
- 初心者が問題に対してどう感じたか
- 解法の方向性に気付けるような動線があるか
- 解くのに必要な技術力は高すぎたり低すぎたりしないか
- リアルにハッキングしてる感があるか & 面白いか
- 非想定解があるか
- 問題サーバーを潰されるような脆弱性がないか
を確認して修正していきました。
初心者の方だと「どこから手を付けたらいいかわからない」「ツールが必要な問題なのにツールの存在を知らない」ことが多々あると考え、このハードルを取り除くために多くのヒントを掲載し、解法の方向性に気付けるような動線を整備しました。ただここで提供するヒントは問題の解法に直接結びつくものではなく、周辺知識を補うための内容に絞るよう気をつけました。
そして、問題のソースコードも出来得る限り公開するようにしました。pwn問題でC言語のソースコードを添付していたり、Web問題でdocker-composeでローカル環境を立てることができるようにしていました。
結果、参加者の方から「初心者にも優しい問題が出ている」「問題文も親切」というお声をいただきとても嬉しく思っております。
また「非想定解は必ず存在する」というスタンスで問題チェックを行いましたが、Web問題及びCrypto問題で難易度が大きく下がる非想定解がありました。今後は問題検証用のファジングツールの開発なども行い、想定している難易度から大きく下振れするような非想定解を減らす工夫を行いたいと思います。
インフラ
インフラ構成は
- スコアサーバー
- 監視サーバー
- netcat 問題サーバー
- pwn 問題サーバー
- web 問題サーバー
- 運営サーバーへの外部監視クローラー (AWS Lambda)
- 問題サーバーへの外部監視クローラー (AWS Lambda)
- Discord(外部向け)
- Slack(内部向け)
- AWS IoT(MQTT問題)
となっています。
今回はローカルな大会であり開催時間も長いことからサーバーに大きな負荷はかからないだろうということでスコアサーバー・監視サーバー・問題サーバーはEC2にdocker-composeで立てました。大会中は合計54個のコンテナが5台の仮想マシン上で動作していました。
スコアサーバー
Golang APIとNuxt.js SPAのオリジナルスコアサーバーWaniCTFdを使用しました。これはチーム内での勉強会のためにずっと開発してきて、さらに今回のCTFのためにreCAPTCHA v3を導入したり、内部エラー発生時の詳細ログ保存&運営Slackへのエラー通知機能を追加したものです。
一般的にはCTFdが採用されることが多いですが、適切な設定をしないと重たくなったりするらしい&あまり使ったことがないので今回は見送りました。(CTFdではなくrCTFを採用する大会も増えてきているようです)
そしてCTFのスコアサーバーとしては珍しく運営問い合わせ機能を付けました。今回の大会は学祭に出展する予定であったため、Discordに参加するというCTFの習慣を受け入れられない参加者がいるかもという理由で実装したものです。ここで参加者からの問い合わせがあったときには運営Slackへ通知が来るようになっています。ただ、今大会では1度もここからの問い合わせはありませんでした。問い合わせ機能の説明が足りなかったかと思いますので今後改善していきます。
監視サーバー
次の監視機構の章で紹介する「内部監視」と「問題サーバーへの外部監視の結果の収容及びステータスバッジの配信」を行っています。
また、内部監視と外部監視を可視化するGrafanaを保護するためにoauth2-proxyを導入し、GitHubアカウントで認証を行いました。
問題サーバー
それぞれの作問者にDockerで問題を作ってもらい、カテゴリごとにまとめてEC2上でホストしました。
インスタンスのメトリクスはnode-exporter、各問題コンテナのメトリクスはcadvisor・ログはpromtailによって監視サーバーに集約されます。
Web問題はプロキシとしてCaddyを設置してHTTPSで提供しました。このときSSLのCertificate Transparency(証明書の透明性)の仕組みによって使用したドメインが記録されてしまうので、Web問題サーバーは大会開始後に起動しました。
大会開始後の対応に手間取り、Web問題の公開まで30分ほどお待たせして申し訳ありませんでした。次のCTFでは詳細なマニュアルを用意し役割分担を明確にして迅速に問題公開ができるようにします。
監視クローラー
次の監視機構の章で紹介する「外部監視」を行うPythonクローラーをAWS CDKを使用してAWS Lambda + CloudWatch Eventsをデプロイして定期実行しました。
監視機構
@atponsさんのSECCON Beginners CTF 2020の監視・オペレーションを支える技術を参考にしてPrometheus・Loki・独自開発の外部監視クローラーで監視機構を作りました。
以下は簡易的なサービス配置図でアクセスの流れが表されています。(監視サーバー内のpromtail, node-exporter, cadvisorは省略しています)
内部監視
すべてのインスタンスからPrometheus(node-exporter, cadvisor)でメトリクス収集、Grafana Loki(promtail)でログ収集を行いGrafanaで可視化しました。そしてPrometheusの監視対象のインスタンスが応答しなくなった場合はAlertmanagerによって運営Slackへ通知するようにしました。
大会開催中は暇さえあればGrafanaの画面を眺めて、CPU・メモリ・ネットワーク負荷が急激に上昇していないか確認していました。
運営サーバーへの外部監視クローラー
スコアサーバー・監視サーバーへHTTPリクエストを送って応答を確認し、もし応答がない場合は運営Slackへ通知するPythonスクリプトをAWS Lambda上で5分おきに定期実行しました。
監視クローラーはAWS CDKでデプロイしています。いくつかのコマンドだけでコードからCloudFormationスタックを作成してくれるのでとても便利です。
問題サーバーへの外部監視クローラー
各問題サービスに対してTCPかHTTPで接続確認をし、作問者が作成したソルバーを実行して正しくフラグが取得できるか確認するPythonスクリプトをAWS Lambda上で15分おきに定期実行しました。
接続確認やフラグの確認の結果は監視サーバーへ収集し、問題ごとにステータスバッジを配信し、プレイヤーがスコアサーバーで問題を見るときにそのサーバーが生きているか確認できるようにしました。
ここでも同様に問題サーバーへの接続やソルバーが正しく通らなかった場合には運営Slackへ通知が来ます。
大会運営のあれこれ
宣伝方法
Twitterで宣伝を行っていました。開催前・開催後の告知ツイートや全完された方の報告をしたり参加されている方のツイートを探していいねを押していました。
まちかね祭への出展が無かったことにより参加者がとても少なくなってしまうのではと心配していましたが多くの方にリツイート&いいねで拡散していただいた上にWaniCTFのことを話題にしてくださって想定以上の方にWaniCTFの存在を知っていただけました。本当にありがとうございました。
開催中の運営の様子
ZoomでGrafanaの画面を共有しながらたまに喋ったりしていました。やはり外部監視クローラーが定期的にサーバーの状態をチェックしているという安心感は大きく、のんびりとしていました。結果としては特に大きな障害もなく安堵しています。
問題の点数減少について
各問題は初期点数500pt・最低点数100ptで正答者数が増えるごとに点数が減少していくdynamic scoring形式を採用しています。参加者は50から60人程度だと予想しさらに余裕を持って減少速度のパラメータを設定していましたが、大会開始後に想定以上の参加者の増加がありBegginer-Hardラベルの問題での点数傾斜が非常に小さくなってしまいました。
ルールに「各問題の正答数による点数差が適切でないと運営が判断した場合、競技時間中に点数の算出パラメータを変更します」という一文を入れていたものの、パラメータ変更によって大きな順位変動が起こる可能性を考え変更しませんでした。
今回の大会運営でユーザー数の増加モデルを取得できたので今後の大会では開催前にある程度の最終参加者数の予測ができるはずです。次は理想的な点数傾斜が付くようにしたいと思います。
作問者writeupの公開について
大会終了後にすぐに作問者writeupを公開しました。事前にwriteupを作成することで作問者が問題の内容を再確認でき、また終了後すぐに公開することで参加者の方へのフォローアップにもなりました。
普段参加しているCTFでもwriteupが見つからず迷宮入りすることが何度かあったので、作問者writeupを公開する習慣が根付いてほしいなと個人的に思っています。
アンケートについて
CTF大会ではアンケート回答後にフラグを表示するいわゆる「アンケート問題」でアンケートを募ることがありますが、今回は問題セットが全完を想定しており、アンケート問題の回答状況でトップ層で不公平な順位の変動が起きることが予想されたため、アンケート問題は用意せず大会終了後にアンケートのアナウンスを行いました。
アンケートにて頂いたご意見はメンバーと共有し改善して参ります。またご感想も大変励みになります。協力いただいた皆様ありがとうございました。
理想の初心者向けCTF大会を目指して
日本トップクラスのCTFerの方たちの参戦もあり、全完を目指す方たちのスコアボードのグラフでの抜きつ抜かれつの戦いがかなり盛り上がっていました。ただ、このグラフは見やすさの観点から上位10名程度のスコア推移しか表示できないため、獲得ポイントの少ない方たちがグラフに掲載されません。
「頑張ってもポイントの数値が増えて順位が少し上がるだけ…そしてグラフに載っている人たちには到底追いつけそうにない…」という感じで特に初心者の方に周りの参加者との戦いの盛り上がりを感じてもらえないのではないかとチーム内で話していました。
もしまた初心者向け大会を開催することがあれば「自分と獲得点数が近いプレイヤー」のみのスコア推移をグラフとして見えるようにしたり、初心者の方のみのスコアボードを用意するなど工夫をしたいと思います。
最後に
いろいろと運営にミスがあり参加者の方にご迷惑をかけてしまい、申し訳ありませんでした。自分はここまで多くの方が参加していただけるイベントを企画、運用したのは初めてであり色々と反省すべきところ・改善すべきところを認識できました。
また、初めてCTFに参加された方やあまりCTFの参加経験がない方から「楽しく解けました」と感想を頂きとても嬉しかったです。(ベテランのCTFerの方からもお褒めの言葉があり、ほっとしています)
参加者の皆様、一緒にWaniCTFを作り上げてくれたチームメンバーに感謝を申し上げます。今回の経験を生かして次のCTFを開催する暁にはまた参加して楽しんでいただければ幸いです。ありがとうございました。