博多電光

blog.hkt.sh

TSG CTF 2020 開催記

※このエントリは TSG Advent Calendar 2020 の1日目の記事です。

博多市です。

今年の7月11日から12日にかけて、東京大学のサークルTSGの主催により、TSG CTF 2020 が開催されました。

昨年5月に行われた TSG CTF に次いで、TSGとして2度目のCTF開催となります。わたくし博多市は今回も全体の進行を司るなど、主催に際して実質的にリーダー的役割を果たしました。

なんとなく開催記を書くのを後回しにしてたんですが、前回の開催記が異様に好評なのもあってゆっくり思い出しながら Advent Calendar のタイミングで出すことにしました。よろしくお願いします。

CTFを開催するという点での大まかな知見は前回の記事に詳しく書いてあるので、今回は新たに得られた知見についてなるべく詳しく書いていこうと思います。

TL;DR

  • CTFを主催するのは、大変だけど、楽しい!

前回からのアップデート

開催時期について

前回の開催記にも書きましたが、CTFを計画する上で一番難しいのは日程選びです。我々のような弱小CTFにおいては他の大きなCTFと日程がかぶらないことはより多くの参加者を募るという点で非常に重要です。昨年の TSG CTF は5月上旬に開催しましたが、今年は作問メンバーが忙しくなる時期などを考慮して6月下旬をターゲットにしようと2月頃には決定していました。そこで当時CTFTime上で予定の空いていた6/27から6/28を開催予定日として、CTFTime上で予定登録し他のCTFとかぶらないように早めに予定登録をしていました。

ところが開催予定日1ヶ月前の5月下旬になって事件が発生します。世界的に有名なCTF大会である0CTFが TSG CTF とまるまるかぶる日程で予定を突っ込んできました。これはTSGとしてもかなり望ましくない事態なのでTSG一同大慌てしたのですが、最終的に0CTFやスポンサーともコンタクトを取った結果、TSG CTF の開催を2週間後の他のCTFがやや空いている時間帯に移動させることにしました。日程変更に伴う混乱は多少ありましたが、結果的に参加者数は前回よりも増えたので、移動させたこと自体は悪い判断ではなかったと思います。

ちなみにCTFTime上で開催時間を変更する操作ができないのが懸念点の一つでしたが、連絡したらすぐにデータを修正してくれました。神対応。(ちなみにCTFTimeはSECCONに対しては塩対応だったので結構気分によるんだと思います)

教訓として、どれだけ早めに予定を入れていても、スケジュールがかぶるときはかぶります。というか、近年は開催されるCTFの数自体が増えているのか、他とかぶらない日程を探すことがそもそも難しくなりつつあります。最近はそういうことをあまり気にしすぎるのも良くないのかなと思い始めています。

リリーススケジュールの事前公開

近年、「より良いCTFを設計するためにはどうすればいいのか」という議論がさまざまな場所で盛んです。このような取り組みの一つとして、GoogleCTFの Eduardo Vela さんが編集した CTF Design Guidelines (bit.ly/ctf-design) というドキュメントが昨年12月に公開され、大きな反響を呼びました。このドキュメントにはこれまでに議論されたCTFの設計に関するアドバイスが丁寧かつ網羅的にまとめられており、CTFを主催するすべての人に読んでほしい必読書となっています。

僕も2度目の TSG CTF を開催する上でこのドキュメントの内容には一通り目を通しました。このドキュメントには「問題のリリーススケジュール」というセクションがあり、様々なリリースパターンとそれぞれの長所短所について詳しく記述されています。特にCTFでよくあるパターンである「開始後、徐々に問題をリリースする」か「競技開始と同時にすべての問題を公開する」かの選択は非常に難しく、TSG CTF を開催する上でも非常に悩んだのですが、これに加えて、このドキュメントには以下のような提案がありました。

The ideal case is to be transparent with players and reveal, ahead of time, how many challenges are expected to be released and when. And create such a schedule taking into consideration timezone challenges.

個人的にもこのアイデアは非常に優れていると感じたので、第三の選択肢として、「事前にどの問題をいつリリースするかを公開しておき、そのスケジュール通りに少しずつ問題を公開する」というパターンを選びました。参加者間の公平性という面では「競技開始と同時にすべての問題を公開する」パターンが最も優れていますが、TSG CTF では1人あたりの問題作成数が多く、複数の問題のサポートを同時に行うのが難しいという点を考慮して行いませんでした (あと、個人的に少しずつ問題が増えていくのがCTFの楽しみの一つでもあると思っているのもあります)。

参加者の反応を見る限り、この試みは非常に好評だったので、次回以降もリリーススケジュールの公開は行うと思います。なお僕の知る限りこのパターンを採用したCTFは TSG CTF 以前では知りませんが、直後に開催された HacktivityCon CTF では TSG CTF 2020 と同様に問題のリリーススケジュールが事前に公開されていました。

Beginner問題の整備

前回の反省点として、開催後のフィードバックとして「全体的に問題が難しすぎて全く手がつけられなかった」という感想が多く寄せられたというものがあります。これを受けて今回の TSG CTF では、初心者向けの問題をすべてのジャンルに1問ずつ用意し、大会開始と同時にリリースすることにしました。

この「初心者向けの問題」を TSG CTF で出題するというのが非常に厄介な問題でした。前回の開催記でも述べたとおり、基本的には我々は「面白い問題は難しい」と思っているので、あまりに自明で解いていて面白くない問題は出題したくないという思いがありました。例えば、フラグ文字列をbase64で100回エンコードしたものをcryptoの初心者向け問題として出題するなどのことはしたくありませんでした。よって方針としては、初心者でも解けて、かつ「CTFのパズルとしての面白さ」を体験できる問題を作成しようと考えた結果、「セキュリティやCTFに特有の知識ベースや経験をほぼ必要とせず、プログラマーなら誰でも知っている知識のみで解けるが、解くにはある程度発想の転換やパズルを解く必要がある (=自明ではない) 問題」を「初心者向け問題」として定義し、出題することにしました。

例えば、Miscジャンルの初心者向け問題として用意した "Beginner's Misc" はこの意味で特によくできた問題だと思っています。この問題の配布ファイルは実質以下の3行だけで構成されています。

exploit = input('? ')
if eval(b64encode(exploit.encode('UTF-8'))) == math.pi:
  print(open('flag.txt').read())

ここで登場する技術はUTF-8Base64Pythonと、どれもセキュリティ的な文脈でなくても広く使われる基礎的な技術ばかりです。一方でこの問題を解くにはUTF-8Base64エンコーディング形式について詳しく調べ、かつ指定された条件を満たすような入力をスクリプトを書いて生成する必要があり、必ずしも「自明でつまらない問題」ではないと思います (ちなみに、Pythonstr.encodeのデフォルト値はUTF-8なので明示的に指定する必要はないのですが、UTF-8が使用されていることが分かりやすいようわざわざ引数で指定してあります)。

競技プログラミングに詳しい方には、同じ難易度でも「ABCのC問題」ではなく「AGCのA問題」を目指して作ったと言えばわかりやすいでしょうか。とにかく、知識勝負ではなく発想勝負になるような問題をBeginner問題として出題するようにしました。

加えて、TSG内部のCTF初心者からは「CTFの経験がないと、問題の内容から何をすればいいか・問題の目的が自明ではない」というフィードバックがあったため、全てのBeginner問題には、問題の目的がなにか、何を達成できればフラグが取得できるのかについて詳細に記述しました。これらの取り組みによって、CTF初心者であっても「何をすればいいか全くわからない」というようなことがなく、かつ「解くことによってCTFの面白さをわかってもらえる」問題を目指しました。

反響ですが、依然として「難しすぎる」「初心者向けと書いてあるのに全く解けなかった」という反応は多かったです。「初心者向け」と銘打って出題したことで問題に対する期待と実際の問題に乖離が生じてしまったのは反省するべき点かなと思います。特にCTFを「セキュリティ技術を競い合う大会」だと思って TSG CTF に参加しに来た人には「セキュリティに関する知識が要求されず、純粋なパズルのように見える問題」が出題されたことで戸惑わせてしまったのではないかと思われます。

次回どのような方針で作問するかについては未定ですが、できれば今回出題したような「特殊な知識はほぼ必要としないが、ひらめきを用いることで解ける問題」は (初心者向けというラベルを付けるかはともかく) 出題できたらいいなと思っています。

ちなみに、大会中にDiscordで問題の難易度について「beginners < easy < med < hard」という発言をしましたが、これはかなりミスディレクションな発言だったと反省しています。上に述べたとおりBeginners問題の本質は「解くのにCTFへの参加経験を必要としない」ことだと思っているので、難易度とは別軸の基準だと思っています。が、一方でCTF初心者から見たときの敷居の高さという面ではEasy問題よりもBeginners問題のほうが低いということも同時に言えるのではないかと思います。

動的スコアの計算式の見直し

前回の TSG CTF ではCTFdのデフォルトの動的スコアをそのまま使用して失敗してしまったため、今回は動的スコアの計算式を事前によく検討して「TSGが考える最高の動的スコア」になるように調整を行いました。

この過程で、他のいくつかのCTFの動的スコアの計算式を比較して参考にしました。プロットすると以下のようになります。

前回の TSG CTF ではCTFdのデフォルトの計算式を用いたので青色の線が該当します。改めて見ると本当にめちゃくちゃ頭の悪い動きをしていてびっくりしちゃいますね。

TSGでは、これまでのCTFへの参加経験から、以下のような特徴を持った動的スコアの計算式が優れていると考えました。

  • 序盤は多くのCTFと似たexponentialな動きをし、10solves程度で約半分の得点になる
  • 一方で関数の漸近が遅く、solve数が増えてもゆるやかに得点が減っていく

これらの条件を満たす計算式として、bit.ly/ctf-designで提案されている以下のlogモデルはかなり理想に近いものでした。

 \displaystyle
  p=500-K\log\left(\frac{S+V}{1+V}\right)

が、この計算式には一つ大きな問題があります。特定の収束値を持たないのでsolve数が増えるにつれて無限に得点が下がっていき、問題の得点がマイナスになる可能性があるということです。

そこで、TSGが誇る数学のプロフェッショナル@naan112358に依頼して、上の条件を満たすような計算式を新たに考案してもらいました。その結果が以下の式です。

 \displaystyle
  p=500\times\left(1+\frac{\log^{2}_{10}S}{a}\right)^{-b}

ここで、 {a,b}は調整のためのパラメーターです。TSG CTF 2020 では  {(S,p)=(100,100),(10,250\text{程度})} となるよう以下のパラメーターを使用しました。

  •  {a=2.079}
  •  {b=1.5}

これをプロットしたのが上のグラフの緑の線です。なめらかに動きゆるやかに0に収束していくことがわかります。

この計算式は実際の運用でも問題なく機能し、またこのあと開催された SECCON 2020 でも同じ計算式が採用されました。実際かなりよくできた式だと思うのでぜひ他のCTFでも積極的に採用してもらえればと思います。

Discordの運用体制

今回も TSG CTF の参加者同士のコミュニケーションにはDiscordを採用しました。近年IRCからDiscordに移行するCTFが増えてきているのを感じるので、これはかなり時代にも則った妥当な判断だと思います。

前回のDiscordでは、random, random_ja, general の3チャンネルによる運用でしたが、前回のDiscordの活動状況から日本語でのコミュニケーションが少なかったことを受けて、今回はrandom_jaチャンネルを使用しませんでした。その代わり他のCTFでよく見かけるように web, pwn, crypto などジャンルごとのチャンネルを設けてそれぞれの話題についての会話ができるようにしました。

結果として、コンテスト中はそれほどコミュニケーションが活発化されなかったこと、また問題の内容に抵触するような内容について話される危険が増すことからあまりいい措置ではなかったと思います。特に TSG CTF のような「真面目な」CTFではコンテスト中のDiscordは完全に参加者が運営に質問をする専用の場所として割り切ってしまうのが良いのかなと思います。

一方で、コンテスト終了後に問題の解法について話し合う際には、複数のチャンネルがあったほうが話題が散逸せず話し合いやすいという利点もあります。この問題を解決するため、SECCON 2020 の運営ではあらかじめ問題のジャンルごとにpost-mortemチャンネルを作成しておき、コンテスト終了と同時にアクセスを解禁するという手法を取りました。これはかなりうまく行ったと思っているので、次回があればこの手法を試そうと思っています。

セキュリティへの配慮

前回の開催記にも詳しく書いていますが、CTFのプレイヤーは一般にあらゆる意味で行儀が悪い (褒め言葉です) ので、CTFの問題サーバーを構成するにあたってセキュリティ的な配慮を行うことは通常にも増して重要だと考えています。前回に引き続き、今回も問題全体のデプロイにDockerを用いており、GCPの container-optimized OS を採用することにより全体的なセキュリティの向上を図っています。

加えて、一部の問題ではセキュリティに関して特別な配慮を行う必要がありました。今回出題したstd::vectorという問題では、ユーザーから与えられたプログラムをサンドボックス環境で実行する必要があるため、DinD (Docker in Docker) を用いた構成を採用しています。この構成ではユーザーからの接続を受けるマスタープログラムがDockerを自由に駆動できる必要があるため、外側のDockerコンテナにpriviledgedフラグを付与する必要があり、コンテナが実質的にサンドボックスの役割を果たしません。つまりマスタープログラムに深刻な脆弱性があった場合、即座にサーバー全体を乗っ取られる危険性があるということを意味します。

なので当然マスタープログラムの実装に際しては脆弱性が存在しないか細心のチェックを行いましたが、やはり人間によるチェックにはどうしても限界があります。そこで万一マスタープログラムがハックされた場合のセーフネットとして、この問題をデプロイするサーバーには他の問題を絶対に同居させず、サーバーに置くファイルも必要最小限のものとし、ネットワーク的にも他のサーバーと隔離することによって、被害の範囲が最大でもその問題のフラグの流出に留まるようにしました。

CTFにおいて大会全体の問題の解法やフラグが流出するのは考えうる限り最悪のシチュエーションなので、常にこのような可能性に気を配って大会を運用したいところです。というかそもそもこのようなタイプの問題をより安全に駆動するためのアイデアがないので、もしあればぜひ教えて下さい (?)。

トラブル・反省点

開始直後のアクセス過剰とサーバーダウン

これは前回の TSG CTF ではちゃんと回っていたけど、今回はうまく行かなかったことの一つです。

前回の TSG CTF では、コンテスト開始直後には大量のアクセスが見込まれることから、開始時刻にだけ強いインスタンスのサーバーを用意しておき、その後アクセスが落ち着くと同時にインスタンスを減らしていくという手法を取りました。今回も同じようにコンテスト開始までにインスタンスを増やしておこうという算段を立てていたんですが、あまり重要なタスクだと意識していなかったためコンテスト開始直前までこのことを完全に忘却しており、結局サーバーのスケールアップが間に合っていない状態でコンテスト開始時刻を迎えてしまいました。結果、コンテスト開始と同時に大量のアクセスに耐えきれずサーバーがダウンし、その後サーバーのスケールアップが完了するまでの十数分間程度、参加者がスコアサーバーにアクセスできない状況が続きました。TSG CTF のインフラ担当としてこれらのアクセスに対応できなかったのは深く反省するべき点だと思います。

コンテスト開始時に問題の内容にアクセスできないのはかなり参加者側の体験として良くない (Discordがかなり荒れました) 上に、場合によってはチーム間の公平性に関わってくるため、かなり避けたいことの一つです。

コンテスト開始直後は、やはりというか、通常よりもかなり多いアクセスが一気に流れ込んでくるので、甘く見ずにちゃんとインフラの対応を行おう、というのが教訓だと思います。

問題チェックの甘さとレビューの難しさ

CTFの問題の作問を行うたびに思いますが、やはりCTFの問題のレビューというのは難しいです。特に今回の TSG CTF 2020 では問題に対する非想定解がコンテスト中に多く発見されました。これは、より洗練された問題を作ろうとした場合、想定解より洗練されていない解をすべて通らないように設計しないといけない、という関係性があるため、より優れた問題を作ろうとする上ではどうしても避けて通れないジレンマだと思います。

これを踏まえても、やはりよりよいCTFを設計しようとするならば、決してレビューを軽んじず、1つの問題に対するレビュー人数と時間を増やすことが肝心です。ましてレビュー無しで問題をリリースするなどというのは絶対にありえないことだと思います。

今回ももちろんリリースした問題に対しては一通り他のメンバーによるレビューを行っていますが、作問陣営の人数がそこまで多くなかったこと、また後述するように結局ほとんどの問題が完成したのが開催直前だったというのもあり、決して「徹底したレビュー」とは言えなかったと思います。また例えば今夏のWeb問題 "Notes" における、Cache Probing を用いた非想定解などに関しては、そもそもこれを用いた非想定解の可能性について作問陣で一度は考慮したものの現実的に可能なexploitを構築できなかったという経緯があり、レビューメンバーの技術的な限界が露呈したとも言えると思います。

これらは本当に難しい問題ですし、簡単な解決策は無いと思います。あるとすれば、日頃から多くのCTFに参加してCTFプレイヤーとしても強くなり、非想定解に対する感度を高めるくらいしかないでしょう。チームTSGとしてもやはり精進は続けていくべきだと改めて思わされます。

クローラー系問題のキュー詰まり

CTFのWebジャンルの問題の典型的な構成として、adminアカウントを持ったブラウザに特定のURLを踏ませることによってXSSなどの攻撃を発火させるというものがあります。これをCTFの問題として実現するために、URLを送信することでヘッドレスなブラウザから自動でそのURLにアクセスしてくれるBOTが実装されることが多いです。これを指してクローラーなどと呼ばれることが多いですが、今回の TSG CTF では Notes (と Notes Revenge) という問題でこのクローラーを含む問題の実装を行いました。

クローラーの動作は、実際のブラウザをまるごと動かすだけあって非常に重いので、Notesではキューによる順次実行を行うことにより並列実行が行われないようにしています。が、問題を公開してからしばらく経った時点で、クローラーの処理能力を超えた数のURLが送信されることによりタスク実行が詰まり始め、URLを送信したにもかかわらずかなり長い間クローラーにアクセスされないという問い合わせが多発しました。

いちおう、このようなことが起きたときにちゃんとスケールできるよう、クローラーの実行は複数インスタンスに分散可能なように設計されていますし、URLの送信に対して Proof of Work を設ける準備もできていたのですが、肝心のキューが詰まっているという事実をちゃんと運営側で把握できていなかったため、対応が後手に回る結果となりました。

CTFが限られた時間で問題を解く競技であることを考えると、クローラーのアクセスが遅いことは競技終了までに問題が解けるかどうかという部分に直接的に関わってくるため、このような事態はなるべく避けたいところです。クローラーなど重たい処理を含む部分は、ちゃんとキューの待ちタスク数や負荷状況を監視対象とすることを忘れないようにしたいです。また現在のクローラーの状態がわかるよう、キューに積んだときに現在の待ちタスク数をユーザーに表示する、などの対策も有効だと考えられます。

最近ではFaaSなどを活用したスケーラブルなWebクローラー実装も見られるようなので、次回があればぜひ実装してみたいなと思っています。

リモート開催の難しさ

昨今の情勢は TSG CTF の運営にも大きな影響を与えました。昨年の TSG CTF では運営が一箇所に集まって泊りがけで24時間の運営作業を行いましたが、今年はやはり物理的に集まるのは良くない (加えて、場所が見つからなかった) という事情により、各々の自宅を音声通話でつないでリモートで運営を行いました。

感想ですが、やはりCTFの運営をリモートで行うのは難しいなと改めて感じさせられました。CTFという大会の特性上、参加者からの質問に対して作問者による対応が必要なことが多々あるのですが、オンサイトで集まっていれば寝ていても気軽に (?) 起こしに行けるのですが、リモートだと作問者による対応を確実に乞うことはどうやっても難しいです。CTFだとこういう場合参加者に「作問者が寝てるからちょっと待ってくれ」と言っても許される風潮がありますが、そのせいで最後まで問題が解けなかったりするとやはり体験としては非常に悪いです。これは通常の開催でもそうなのですが、なるべく作問者の他にも問題に対する質問に対応できる人間を増やしておく、具体的には問題の内容と解法をより多くの人間が理解し、サーバーなどのオペレーションに必要な認証情報が適切に共有されているということが、リモート開催に際してはより一層重要になってくるかなと思います。

ちなみに今回の TSG CTF 2020 ではわたくし博多市が終了数時間前に仮眠をとったあと不慮の事故により寝過ごしてしまい、本来僕がやる予定だったCTFの終了に際する告知作業を他の部員が代行して行う羽目になるというトラブルがありました。運営を担当した僕以外のTSGerがめちゃくちゃスマートだったのでこれは概ね恙なく完了した (ありがとう) のですが、WebPushの通知を送信するのに必要なOneSignalのトークンだけ事前に共有するのを忘れていたため片手落ちとなってしまいました。慢心、ダメ、絶対。

質問への対応作業の集約

TSG CTF 2020 では、問題の作問者がわかるとその情報から問題の解法がメタ読みされる危険があるという思想から、なるべく問題の作問者を秘匿するようにしました。それに伴う問題として、Discord上での質問に対する対応に作問者直々に回答することができなくなってしまうため、TSG CTF 2020 ではDiscord上で@tsgctf-adminという質問集約のためのアカウントを設け、問題への質問などは全部そこに送ってもらい、運営者なら誰でもそのDM内容を見て回答できるようにしました。これには作問者を秘匿することができることに加えて以下のような利点があると考えました。

  • 参加者は "who is admin for xxx?" → "please dm @xxx" という質問作業を行うことなく一発で問題に対する質問を送ることができる
  • 未対応の会話が放置されず、運営全員が気づいて対応することができる

で、実際にやってみた結果ですが、こちらもうまく回ったとは言い難いです。そもそも論として、問題の作問者を隠すことに関してかなり賛否両論でした。問題の作問者は、製品の生産者表示のように参加者に対して問題のクオリティを保証する意味を同時に持っているため、むしろ積極的に表示したほうがいいという意見も多かったです。また未対応の会話が放置されないという点ですが、実際に運用してみると膨大な量のメッセージを捌く必要があったため、対応済み・未対応の管理が難しく、ちゃんと漏れなく対応できていたとは言い難かったです。加えてDiscordが複数アカウントを切り替えるという操作を想定していない、メッセージの既読未読状況がブラウザごとではなくアカウントで共有されてしまうなどのプラットフォームの問題もあり、結果的にかなり運営の負担が増えてしまいました。たぶん次回は廃止すると思います。

これはCTFの伝統とコンフリクトするため僕も導入するのを渋っているのですが、やはり上のような問題を解決するためには競プロのようなclarシステムを設けるほうがいいのかもしれません。が、CTFの場合、参加者の問題を解決するために会話を何往復かさせなければいけないことも多いため、単純な問題でないのも事実です。

通知ボタン

前回に引き続き、今回もスコアサーバーから参加者のブラウザにWebPushで問題追加などの通知を送るようにしています。前回の反省点として「通知の許可ダイアログが出ると反射的に拒否してしまう」というものがあったため、今回は右下に通知ONボタンを表示し、ページロード時ではなく通知を許可したいときにボタンを押して貰う形式にしました (OneSignalの機能を使っています) が、通知をONにした人の数は前回よりも少なかったです。悲しいね。

プロジェクトマネジメント

これはもう半ば諦めているんですが、最終的に出題する問題が出揃ったのは今回もかなり開催直前になってからでした。余裕を持った作問スケジュールというのは存在しうるんでしょうか?

本来の開催予定日だった6月末の時点で、作問状況はこんな感じでした。

hakatashi以外の問題が6問 (うち1問未完成) しかない!

で、今回も直前の追い上げで問題を一気に完成させたわけですが、そうやって完成した問題にはやはり穴が多かったというか、非想定解とかトラブルが多かった印象があります。あまりに当然ですが、問題はやはり早めに完成しておくに越したことはないようです。

いろいろなCTFのオーガナイザーに話を聞いてみると、年1回のCTF開催を目標に、年間を通して問題を考えておき、1年分のストックをCTFで放出するというスタイルを取っているCTFが多いようです。そんなわけで、TSGでも次回の TSG CTF 開催に向けて今から作問作業を行っています⋯⋯が、やっぱり作問状況は芳しくないですね。安定してCTFの問題を供給するのはすごく難しいことだと思います。

完璧なプロジェクトマネジメントなどといったものは存在しない。完璧な絶望が存在しないようにね。

次回について

2021年の TSG CTF についてですが、大きな問題がなければ十中八九開催します。時期などは全く決まっていませんが、今回の反省内容を生かして、より面白くてクオリティの高い大会にできたらいいな〜と思っています。あと、新しい取り組みもなるべく取り入れたいです。

今回の開催記はこんな感じでしょうか。最後まで読んでいただきありがとうございます。また、スポンサー協力を頂いたFlatt社のみなさん、および TSG CTF 2020 に参加していただいたみなさんにも感謝の気持ちが尽きません。TSGだけではCTFは成り立たないんだということを改めて感じさせます。今後ともよろしくお願いします。

TSG CTF 2020 作問感想

博多市が作問した問題に関する雑記です。ちゃんとしたWriteupは後ほど投げます。

Beginner's Crypto (Crypto)

配布ファイルが単純だし解くのに複雑な式変形も必要ないのでBeginner。

レビュー中にsatos (@satos___jp) が下の桁から合わせていく面白い別解で解いていた。

Sweet like Apple Pie (Crypto)

「こんな問題できないかな~」って投げたらナン氏 (@naan112358) が秒で解いてくれた。すごい。sin(x) = sin(π - x) に関しては博多市も作問する際にめっちゃ悩んだのでみんなにもこの苦しみを味わってもらいたいと思いそのまま出題。

Rubikrypto (Crypto)

CTFにルービックキューブ問は何度も出てるけど、ちゃんと暗号暗号してる良問ってないな~と思っていたので、ちゃんと群と暗号に絡めた形の問題を作りたかった。想定解は Pohlig-Hellman ではなくルービックキューブ群の元の位数がたかだか1260であることを利用する。

Modulus Amittendus (Crypto)

原案はcookies (@kcz146) の「Nの代わりにΦ(N)が与えられたらpとqを復元できないかな~」という発言。その過程で「そういえばHITCONに Lost Modulus Againなんて問題があったね~」ってなって、試しにこの問題から係数を一つ落とした問題をナン氏 (@naan112358) に投げてみたら秒で解いてくれたので出題。

今年もRSA問が出せてよかった。係数一つ落としただけなのにもとの問題と解き方が全く違ってくるのが面白いですね~。

Beginner's Web (Web)

object[key](hoge, fuga) みたいなやつで key がいじれるシチュエーションなら、CTFerでなくても Object.prototype を疑うのが自然かな~と思います。

で、想定ムーブは Object.prototype 以下のメソッド一覧をドキュメントなりなんなりで確認し、(String, Function) という引数を取る関数が __defineSetter____defineGetter__ しか無いことを見つけ、で __defineGetter__ はフィルタの [FLAG] に引っかかるので __defineSetter__ 一択に絞れる。あとは一直線。

Slick Logger (Web)

いや~Side-channel問の作問って難しい。つばめ先生の Blind Regexp Injection の記事にあるとおり、Golangオートマトンを用いたいい感じのRegExpエンジンを実装しているのでexponentialな計算量のReDoSが難しいわけですね。ただオートマトンを用いるConsとしてオートマトンの状態数に比例した時間計算量が必要になり、GolangRegExpには状態数に対する制限がないので重めの線形ReDoSが構築できます。あとはApacheCGIのTimeout設定が1sなのを確認して status code が504か200かで判別できるようにパラメータを調整すれば勝ちです。

Beginner's Misc (Misc)

Base64UTF-8も全人類知ってるしソースコードが実質3行なのでBeginner。

Base64に変換したあと[0-9/+]だけで構成されるようなUTF-8文字列を手で構成するなり全探索するなりしたあと分数にして上の桁から合わせていく。

Poor Stego Guy (Misc)

めっちゃ頑張って作ったのに一行で解かれて泣いちゃった。想定解はJPEGのDCTの係数離散化がそのまま格子と見なせることを利用し、LLLを用いてCVP問題を解きます。

BlindCoder (Misc)

「こんな問題できないかな~」って投げたらCoiL氏 (@coil_kpc) が秒で解いてくれた。すごい。

博多市のアイコンが新しくなります

博多市です。今春の就職に合わせて、6月から渋谷のど真ん中で新生活を送っています。

これまでインターネット上で好んで用いてきたおなじみの黒と赤と青のアイコンですが、新生活にあたって心機一転し、全面的にリニューアルすることにしました。

これまで使ってきたアイコンはこちらです。

f:id:hakatashi:20200621072038p:plain
年季を感じさせる古臭さのぬぐえないクソダサ旧アイコン

そして、新しくなった博多市のアイコンはこちらになります。

f:id:hakatashi:20200621072355p:plain
新しい時代の到来を感じさせるスタイリッシュでエレガントな新アイコン

何が変わったか一目瞭然ですね!!!!

変更点

何が変わったかわからない人のために、旧アイコンと新アイコンを重ね合わせて比較してみました。

f:id:hakatashi:20200621073312p:plain
旧アイコンと新アイコンの比較

赤い部分が旧アイコン、青い部分が新アイコンです。各部の寸法が微妙に変更されています。

新しいアイコンの特徴

実は、これまで使用していたアイコンの寸法はあらゆる意味で「汚い」ものでした。今回のリニューアルはこの寸法の問題を一挙に解決するためのものです。このアイコンは以下のような特徴を持ちます。

1. 各頂点の座標を含め、あらゆる寸法を正確な整数比で表現することができる

画像全体の大きさを480x480としたとき、各図形の頂点の座標は以下の通り規定されます。

  • 黒: (130, 185), (146, 173), (286, 243), (286, 343), (270, 355), (130, 285)
  • 赤: (162, 161), (178, 149), (318, 219), (318, 319), (302, 331), (302, 231)
  • 青: (194, 137), (210, 125), (350, 195), (350, 295), (334, 307), (334, 207)

2. 図形全体を六角形としてみたとき、左右の対角線がその両端の角を正確に二等分する

f:id:hakatashi:20200621075541p:plain

3. 図形全体を回転させると左右対称かつ上下対称となる

f:id:hakatashi:20200621075657p:plain

4. 図形全体の中心が画像の中心と一致する

f:id:hakatashi:20200621081059p:plain

5. 図形を直方体としてみたとき、黒と赤と青のストライプが側面の幅を正確に五等分する

f:id:hakatashi:20200621081307p:plain

これらの特徴のうち、旧アイコンで満たされていたのは4.の「図形全体の中心が画像の中心と一致する」だけでした。

実はこの旧アイコンは、寸法とかそういう難しいことは考えずにIllustratorを使用して雑にデザインしたものだったので、よく見るとあちこちがガタガタです。「博多市」としてのソーシャルアイデンティティーを示すためのアイコンとして、こんな適当なアイコンでは良くないと以前から気になっていたのですが、今回このように改めて図形としての仕様や要件からアイコンをきっちりと定義し直したことにより、旧アイコンの「フリーハンド感」が薄れてデザインとしてもかなりスッキリしたと感じています。

これらの寸法の仕様や派生画像など、アイコンに関する情報は今後こちらのリポジトリに集約する予定です。

github.com

よく見ないと気づかないマイナーチェンジかもしれませんが、スッキリと生まれ変わったアイコンで、どうか今後ともよろしくお願いいたします。

博多市アイコン略史

せっかくなので、このアイコンに至るまでの博多市のアイコンの遷移と歴史について軽く紹介しておきます。

博多市が自分のアイコンというか、アバターについて初めて意識したのは、2010年にTwitterのアカウントを作成した頃です。

当時の博多市のTwitterアイコンは、こんな感じでした。

f:id:hakatashi:20200621083414p:plain
第1世代アイコン (再現)

今となっては懐かしい、「伯方の塩」の非公式擬人化キャラクター「伯方さん」です。

「博多市」というハンドルネームは当時から使っていたので、アイコンもそれにちなんだものにしようと考え、当時流行していた名前の似ているキャラクターをそのまま引っ張ってきました。キャッチフレーズの「!の!!お!」が「はかたし」の4文字を含むこともあって、このキャラクターには比較的愛着があったのを覚えています。

そしてそれから2年ほど経った頃、Twitter「箱ドット」なるアイコンが流行し始めました。こういうやつです。

f:id:hakatashi:20200621084037p:plainf:id:hakatashi:20200621084219p:plain (素体素材: 花屋(oat)さん)

借り物のキャラクターじゃなくて自分オリジナルのアイコンが欲しいと思っていた博多市は、自分でもこれを作ってみることにしました。素体とかよくわからなかったので、ペイントで一から描いてみました。

f:id:hakatashi:20200621084616p:plain

そして頭だけ描いて諦めました。

このままで終わるのもなんなので、元のアイコンの伯方さんのアクセントである赤と青のストライプと、「博多」という文字を前面にあしらってなんとなくそれっぽい感じにしました。これが第2世代のアイコンです。

f:id:hakatashi:20200621084935p:plain
第2世代アイコン

このアイコンはけっこう長く使われましたし、派生アイコンも多く誕生しています。たとえばクリスマスだけ使われた1日限定のアイコンはこんな感じで、

f:id:hakatashi:20200621085138p:plain
第2世代アイコン (クリスマス限定)

Tumblr用に使われたのはこんなアイコンでした。

f:id:hakatashi:20200621085227p:plain
第2世代アイコン (Tumblr)

そして大学に進学したあとの2014年ごろ、Webプログラミングなどを通してSNS以外でのアイコンの使用が増えてくるのにつれて、ドット絵のアイコンというのが古臭く見えるようになってきました。まあ素人が適当に打ったドット絵ですし実際見栄えが悪い。当時フラットデザインというのが流行し始めていたのもあり、デザインの意匠は変更せず、シンプルな図形で構成されたアイコンにリニューアルしようと考えました。このとき作成されたのが第3世代のアイコンです。

f:id:hakatashi:20200621085928p:plain
第3世代アイコン

というわけで、博多市のトレードマークでもあるこの赤と青のストライプは、もとを辿れば伯方さんの衣装、さらに辿れば伯方の塩のパッケージに描かれている赤と青の線に行き着きます (ちなみにこのパッケージの線が何を表現しているのかは未だにわからないままです。誰か教えて)。

ちなみに、このリニューアルしたアイコンを、以前のアイコンを知る当時のNPCAの後輩や同輩に見せたら口を揃えて「え⋯⋯ダサw」って言われたのをよく覚えています。絶対に許さん。

f:id:hakatashi:20200621072355p:plain
第4世代アイコン

以来、実に長い間このアイコンは使われました。目が痛くなるこの原色の黒赤青の配色がもはや自分にとっての「博多市配色」です。リファインされ第4世代となったアイコンにさらに愛着が湧くよう、これからも精進してまいります。

2020年3月のトリビア

この記事は博多市がその月に「へえ~」と思ったことをまとめたものである。詳しくは初回の記事を参照。

2020年2月のトリビア - 博多電光

というわけで、2020年3月に収集した事項を掲載する。

注意事項

  • あくまで博多市が面白いと思ったかどうかなので、いわゆるトリビアっぽくないものも含まれている。
  • 原則として自分の中である程度調査して裏付けが取れたものだけを掲載しているが、ちゃんとソースをまとめるのがめんどくさいので正しい情報かどうかの判断は各位でお願いしたい。

トリビア

  • 1970年代を境に伝書鳩の帰巣能力は世界的に低下している。世界レベルの磁気の変化や生態系の変化などが仮説として挙げられている。
  • 日本において、大麻は所持のみが違法であり使用は処罰されない。そもそも大麻の成熟した実である芥子の実は七味の材料などとして日常的に用いられており、これにも微量の大麻成分が含まれているため現在の検査能力では真に大麻として使用したか立証困難だからである。
  • 軍事用語において「全滅」とは部隊の構成要員の約三割が損耗した状態を指す。
  • 2011年までロシアではビールが酒類として扱われていなかった。当然未成年でも合法に飲むことができた。
  • 「めったに起こらないが、起こらないと思って油断していると壊滅的な被害を与える事象」を Black Swan と呼ぶ。17世紀に「黒い白鳥」が発見されるまで白鳥はすべて白いと考えられてきたことに由来する。
  • フリーマーケットサイトなどで行われるクレジットカード現金化が取り締まられる際の主要な法的根拠は「出資法違反」である。取引を通して紙幣の額面以上の金額を受け取る行為を融資とみなせば超高金利の違法融資となるためである。
  • 海藻類の多くは、陸上に棲息する多くの植物とは反対に、晩秋に芽吹いて、冬から春にかけて成長し、夏になると枯死するというサイクルを経るものが多い。
  • 一般には宇宙は無重力ではない。例えば国際宇宙ステーションの高度は地表から約400kmであり、この距離では地表と比較して約90%もの重力が地球に対して働いている。
  • 「ポン酢」の「ポン」は「フルーツポンチ」の「ポン」と同根である。
  • よく知られている「上を向いて歩こう」の英題「Sukiyaki」の由来は原作の内容と全く関係がなく、一説にはこの曲を欧州に知らしめたイギリスのトランペッター、ケニー・ボールがCDリリースの際、彼が知っていた日本語の単語が「Sukiyaki」と「Sayonara」くらいしかなかったためSukiyakiとプリンティングしたことに由来するという。
  • 人間の毛髪には健康な状態でも数ppm程度の水銀が含まれており、一般的に毛髪水銀と呼ばれる。この濃度は魚介類を多く摂取するほど高くなる。

5年間アルバイトを勤めたピクシブ株式会社を退職します

誰?

博多市 (@hakatashi) です。

主にフロントエンドを好むエンジニアです。

大学4年生です。たぶん今日で学生終了です。

何してた?

アルバイト入社以来いろんなチームを転々としながら、フロントエンドを中心にWeb開発を行っていました。

  • pixiv
  • pixiv小説
  • pixiv小説モバイル
  • pixivコミック
  • pixivノベル
  • pixiv Sketch
  • pixiv Sketch Live

エンジニアとして社のブログもいくつか書きが、こうして並べて見ると何してるエンジニアなのかかなり不明です。

ピクシブはどうだった?

アルバイトとしてのpixivの5年間は、ひとえに「幸福」の一語に尽きます。

5年前アルバイトとして入社したとき、自分は謙遜抜きで未熟なプログラマーでした。当時書いていたコードを今見返してみるとまさに物の道理を知らないプログラマーの児戯といった風情で全く恥じ入るばかりです。

それから5年、pixivでのアルバイトの経験は僕に様々なことを教えてくれました。開発における心構え、プロダクトを作るということの如何を学びました。1人で開発しているだけでは決して得られないイベントの経験を何度も得ました。技術に秀でた貴重な知人を得ました。pixivは僕を育ててくれました。

もしも今の自分を、すごい、優秀だと褒め称してくださる方がいるなら、おそらくその半分以上がpixivという環境のおかげです。感謝が尽きることがありません。

加えて、「創作文化を尊重する」という点にかけても、pixivは自分にとって非常に心地よい空間でした。

「創作活動がもっと楽しくなる場所を創る」という企業理念に違わず、pixivはインターネットの創作文化を支援するにかけて実利的に日本で最も寄与する企業であると考えています。のみならずそれを構成する社員の一人ひとりも一次創作、二次創作を問わず創作文化を愛する同士であり、その証左に「お絵かきブートキャンプ」などの取り組みも積極的かつ自主的に行われています。自分もまたpixivのヘビーな閲覧ユーザーで創作を愛する自負があり、かるが故にpixivでの業務は常に指針として己の欲するところを為すものでした。巷では創作者を理解する心なきようにpixivの運営主体を扱い謗る風評も時として罷り通っていますが、その内実からすればとんでもない話です。

なぜやめる?

大学卒業につき他社に就職を決めたためです。

就職先については大いに悩みました。もちろんピクシブからもオファーを受けていました (それも好条件で) が、最終的に他社を選択したのは、それがより自分にとって厳しいものであると思えたためです。

思えばこれまでの人生の岐路において、自分は常に厳しい道を選択したと思います。大学を選ぶにおいても内部競争の激しいところを選び、学科選択においても一度内定した学科を蹴りわざわざブラックと名高い学科に進学したりしました。

狭き門より入れ、艱難汝を珠にす――とは、敢えて言いません。実際僕はこの選択によって多くの「不利益」を蒙りました。大学の学科振り分けに際しては幾度となく躓き最終的に3回も留年を重ねる羽目になり、一時は真面目に退学を考えました。先日の報告のとおり無事卒業を迎えることができましたが、自己研鑽の意味においても本学を選んだことは必ずしも妥当な選択ではなかったと、今ではそう思います。

しかしそれでも、事ここに至って再度「過ち」を繰り返そうとするのは、自分が安寧な環境において何かを成せる人間だと思えないからです。僕は人より幾分か長い大学生活の中で、深い絶望の底でしか紡げない文学があることを知りました。劈く耳鳴りの中でしか鳴らせない音楽があることを知りました。これはエンジニアリングの道においても同じだと思います。大業成すには死狂いなるべし。水の流れていればこそ鯉が滝を登るように、とりわけ自分のような白面郎には常に自らをタフな環境に置くことこそ重要だと考えました。

創作の庭からは、一旦離れることになります。先に述べたとおり自分にとって創作文化とは著しい傾慕の情を惹起させるところ、その一助に自分も加われないことに後ろ髪を強く引かれる思いですが、またいつかと割り切って差し当たり道を分かつことにします。もしかしたらとんぼ返りなんてことになるかもしれないですし。

今後は?

今春から Google Japan にソフトウェアエンジニアとして就職する予定です。チームは Google Maps 関連になる予定です。と言っても、コロナウイルスの影響でまだしばらく仕事がないようですが⋯⋯。

CTFや同人誌の執筆などの個人活動は継続する予定です。大学は卒業しますが TSG CTF 2 もたぶん開催します。

最後に改めて、pixivでお世話になった多くの方々に感謝いたします。狭い業界、またどこかで会いましょう。ありがとうございました。

2020年2月のトリビア

日常生きているといろんなことに感心したり、なるほどと思ったりする。しかしそんな小さななるほど感は1ヶ月も経てば忘れて知識としては全く定着しないことが多い。そこで、日頃から「へぇ~」と思ったことをメモしておいて、1ヶ月に1回くらいの周期でまとめようと思った。

とりあえず、2020年2月に収集した事項を掲載する。あくまで博多市が面白いと思ったかどうかなので、いわゆるトリビアっぽくないものも含まれている。

原則として自分の中である程度調査して裏付けが取れたものだけ掲載しているが、ちゃんとソースをまとめるのがめんどくさいので正しい情報かどうかの判断は各位でお願いしたい。

トリビア

  • 日本の有効な記念硬貨には10万円の金貨が存在する
  • 「裸の王様」の原案では衣服は「不義の子には見えない」という設定だった
  • 「凶」の部首「凵」は凵繞(かんにょう)である
  • 兜割りは技を極めた剣術家の最高峰の妙技である。明治の天覧兜割りでは榊原鍵吉なる当代一の剣術家が実際に挑戦し鉄兜の頭頂を3寸ほど切り込むことに成功したという
  • e+πが無理数であることは2020年現在証明も反証もされていない
  • 渋谷109の名前の由来は「東急」→「10・9」の語呂合わせである
  • 日本語「かわいい」と漢語「可愛」の関連はよくわかっていない。日本語の語源は「顔映ゆい」とする説が有力だが、表記は漢語に依るという説もある。少なくとも現代中国語で「可愛」は通じる。
  • 人間の脳が仲間と認識できるのは150人が限界である
  • 酒類のCMは自主規制により「25歳以下の出演」「喉元アップの描写」などが禁じられている
  • 1900年前後まで地球の年齢は1億年前後と見積もられていた
  • 酒気帯び運転とは血中アルコール濃度が基準以上の状態による運転、酒酔い運転とは飲酒により判断能力が低下した状態での運転である。後者のほうが重い。
  • 三大ブルーチーズの一つゴルゴンゾーラの由来はイタリアのゴルゴンゾーラなる街名が由来だが、現在ここでチーズは製造されていない
  • ロシア名産とされるマトリョーシカ人形の歴史は意外と浅く、1900年のパリ万博以前の歴史には登場しない。
  • パプリカは唐辛子である
  • どて焼きの名前の由来は最初に鉄鍋の縁に土手状に味噌を盛ることからである
  • キヤノンキユーピーアロンアルフアシヤチハタオンキヨー
  • 予防医学の研究の進展により、現在ではうがいによる感染症の予防効果は認められていない。実際、新型インフルエンザ流行の際の厚生労働省のQ&Aにはうがいの記述があるが、新型肝炎のQ&Aではうがい関連の記述が削除されている。
  • 陸上で最も海から遠い点、ないし海上で最も陸から遠い点のことを到達不能極と呼ぶ。
  • 「ファストフードによくついてくる、ジャムとマーガリンを同時に出せるあの容器」の正式名称は「ディスペンパック」……ではなく、「パキッテ」である。2019年名称変更。
  • 衛生および美容の観点からは、洗髪時のシャンプーはほぼ不要である
  • expectは「当然そうすべきと期待した」、You'd better は「絶対に~するべき」、どちらも強いニュアンスを持つ単語なので注意して使うべきである