ゼロから始めるCapture The Flag
2019年07月29日 月曜日
CONTENTS
はじめに
皆さんは、情報セキュリティの知識や技術力を競い合うゲームCTF (Capture The Flag)をご存じでしょうか。CTF は、全世界で開かれており、近年日本でも知名度が上がってきています。本記事では、CTF の競技形式や問題カテゴリについてご紹介します。また、サンプルの問題を用いて、実際の解く流れも解説いたします。本記事を通して、CTF を知り、参加するきっかけになれば幸いです。
CTF とは
CTF は、与えられた問題を解いてFLAG{Example_FLAG} のようなフラグと呼ばれる答えの文字列をスコアサーバに提出することで得点を重ねていき、開催期間内の総合得点で順位を競います。フラグの形式は、開催されるCTF によって異なっています。多くの場合CTF の大会名や略称などがフラグフォーマットとして利用されます。例えば、もし弊社がCTFを開催するとしたらフラグ形式は、iijctf{Example_FLAG} などでしょうか。
CTF の開催形態として最も多いのは、インターネット上での開催です。不定期で開催されており、1回のCTF の競技時間はおよそ24時間から48時間です。また、インターネット上でアクセスできる形でいつでも解答することができる常設型のCTF もあります。開催されているCTFの情報などは、CTFTime と呼ばれるポータルサイトで確認することができます。近年は、企業などがCTF を開催する事例もよくあります。また、いくつかの有名なCTF の中には、予選と決勝に分かれており、予選はインターネット上で開催され決勝は海外の現地に集まって行うものもあります。
CTF には、主に2つの競技形式があります。1つ目は、Jeopardy 形式で、インターネット上で開催されているCTF では、最も定番の競技形式です。Jeopardy 形式では、参加者が複数ある問題から自由に選んで解答する形式です。難易度に応じて得れる得点が変わりますが、最近ではダイナミックスコアリングと呼ばれる問題の解かれた数に応じて動的にスコアが変動する形式もよく見られます。この方式を用いることで、問題の作問者が意図した難易度と競技者が感じる難易度の差による配点の違和感を軽減し、結果的に適切な配点になることが多いです。2つ目は、Attack & Defense と呼ばれる形式です。Attack & Defense では、各チームに脆弱性のあるサービスが稼働しているサーバが与えられます。競技者は、他チームのサービスを攻撃してフラグを手に入れることができます。また、脆弱性を修正して他のチームから攻撃されないようにするといったことも重要です。Attack & Defense は主に決勝大会で行われることが多いです。
CTF は、主にチームで参加することが多いですが、1人で参加することも可能です。多くのCTF の場合、1人のチームで登録することは可能です。1人で参加することのメリットとしては、幅広く問題に触れることができるという点があります。チームで参加している場合は、他のチームメンバーに解かれてしまった問題を競技時間内に解くことはしないので、多くの問題にチャレンジしたい場合は、1人での参加をお勧めします。CTF プレイヤーの中には、難しいCTF にはチームで、比較的簡単なCTF には1人で参加している人もいるようです。一方で、チームで参加することのメリットは、1人で解けない問題をチームメンバーと協力して解くことができる点です。各問題カテゴリ毎に担当する人を分けているチームもよくあります。この構成を取ることで、各問題カテゴリに特化した専門的な力を伸ばすことができたり、自分の苦手分野を補って協力し効率良く問題を解くことができるというメリットがあります。
もう少し具体的な問題のカテゴリについてご説明していきます。CTF では、主に以下の問題カテゴリに分けられて出題されます。ただし、開催元によってカテゴリの分け方に違いがある場合もあります。
- Web
- Web アプリケーションの脆弱性を突いて、フラグを取得する
- Crypto
- 古典暗号や現代暗号を解いて、フラグを取得する
- Forensics
- パケットやイメージファイルなどを解析して、フラグを取得する
- Rev(Reversing, Binary)
- バイナリ(主に実行ファイル)を解析して、フラグを取得する
- Pwn(Exploit, Pwnable)
- サーバで動作している脆弱なプログラムを攻撃し、サーバの権限を奪取してフラグを取得する
- Misc
- 他の問題ジャンルに属さない問題が集められるジャンル
この他にも画像に隠されたフラグを見つけ出すSteganography や競技プログラミングのように与えられたお題をプログラミングで解くスキルを問われるPPCなどもあります。次の節では、この中のRev カテゴリの実際の問題とその解き方を解説していきます。
リバースエンジニアリング問題の解説
今回は、CTF のRev カテゴリにおいてよく出題されるcrackme と呼ばれる問題を解説していきます。crackme とは、与えられた実行ファイルを実行するとパスワードの入力を求められ、正しいパスワードを入力できるととフラグが手に入るという問題です。
今回の問題ファイルは、baby_rev という実行ファイルです。まずは、与えられた実行ファイルがどんな形式のファイルであるかを特定しましょう。ここでは、file コマンドを使って調べてみます。
$ file baby_rev file baby_rev baby_rev: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=1aec4240335b368a72fc357570a602340d99d192, not stripped
上記の結果から、64bitのELF(Executable Linkage Format)ファイルだと分かりました。ELF ファイルは、主にUnix やLinux で利用される実行ファイルの形式です。また、ダウンロードしてきた実行ファイルには、実行権限が無いので、chmod コマンドで実行権限を付与しましょう。
$ chmod u+x baby_rev
では、実際に実行してみましょう。
$ ./baby_rev Usage: ./baby_rev <password>
上記の結果から、実行時にコマンドライン引数としてパスワードを設定する必要があることがわかりました。そこで、何かしらの値を入力してみましょう。ここでは、iij と入力してみました。
$ ./baby_rev iij Invalid password...
上記の結果から、フラグは得れておらず、適切なパスワードを入力する必要があるとわかります。しかしながら、パスワードの長さもわからず闇雲に実行しても正解にはたどり着けません。そこで、リバースエンジニアリングして内部の動作を明らかにしていきます。
一般的に、ELF などの実行ファイルの中に、CPU(Central Processing Unit)が理解できる機械語と呼ばれる命令群が格納されています。しかしながら、機械語は一般の人間には、解読するのが難しいため、アセンブリ言語と呼ばれる機械語とほぼ一対一に対応した人間にも解釈することができる文字列に変換して解析していきます。この機械語からアセンブリ言語に変換することを、逆アセンブルと言います。今回は、デバッガを用いて、逆アセンブルした結果を確認しながら、解析していきます。
Linux の代表的なデバッガとしては、gdb が挙げられますが、今回はgdb のPython 拡張でより機能を豊富にしたgdb-peda と呼ばれるツールを用いて解析していきます。gdb-peda の利点として、実行中のレジスタの値や実行している命令の付近の逆アセンブル情報、スタックやヒープ領域といったメモリの情報を俯瞰しながら解析することができます。
下の図は、gdb-peda で当該ファイルを実行させ、いくつか命令を進めた後の画面です。
画面中央の矢印がある行が次に実行される命令です。この命令がどんな動きをするのか考えてみましょう。cmp 命令は、与えられた2つの引数を比較して、EFLAGS レジスタと呼ばれる専用のレジスタを更新します。この時点のDWORD PTR [rbp-0x4] は、コマンドライン引数の個数を示しており、それを2と比較しようとしています。その次の行では、je 命令があります。je 命令はcmp 命令と組み合わせて利用されることが多く、EFLAGSレジスタの値に応じて、引数が指すアドレスに制御を移す命令です。今回の場合は、コマンドライン引数の個数が2と等しかったら制御を0x40061f に移します。一方で、等しくなかった場合、次の行に進んでいきます。つまり、一般的なプログラミング言語のif 文に相当する処理だと考えられます。また、今回はコマンドライン引数を与えて実行するので、ここは制御が移り先に進みます。その後、さらに少し進んだ際の画面が以下の図です。
ここでは、次にstrlen関数が実行されようとしています。strlen 関数は、引数の文字配列の長さを取得する関数です。今回は、iij という3バイトの値を入力しているため、この関数の戻り値は3になります。戻り値は、raxレジスタに格納されるため、次の行で0x16つまり10進数で22と比較して、先ほどと同じようにje命令で制御を移すかどうかの処理が実行されます。そこで、22バイトの連続したAAAA…A という文字列をコマンドライン引数で与えて実行しなおして、さらに進めた画面が以下の図です。
ここでは、strncmp 関数が実行されようとしています。また、Gueesed arguments の部分にiijctf から始まるフラグと思われる文字列があることが確認できます。strncmp 関数は、与えられた2つの引数の先頭からnバイトが等しいかを判定する関数です。1つ目の引数に実行時に入力した値があり、2つ目の引数にフラグがあるため、実行時にフラグを入力すればよいと分かります。そこで、実際に入力してみましょう。
./baby_rev iijctf{Are_you_CTFer?} Congratulations!! You got a FLAG :)
上記の結果から適切なパスワードであることがわかり、フラグを手に入れることができました!
本番のCTF では、このフラグをスコアサーバなどに提出することで得点を得ることができます。CTFに参加した際は、この記事のような問題の解き方をまとめたWriteup をブログなどで公開する文化があります。競技時間内に解けなかった問題などは、他のチームのWriteup を読んで勉強することでより強くなることができます。
おわりに
本記事では、CTF の概要や実際の問題例、解き方などをご紹介しました。CTF とは、主に情報セキュリティの知識や技術力を競い合う競技です。様々な問題カテゴリがあり、Webアプリケーションの脆弱性を突くものや、リバースエンジニアリングをする問題、パケットやイメージファイルの解析など問題の種類は多岐にわたります。求められる知識やスキルがとても幅広く難しい競技ですが、問題を解けたときには、とても達成感を得られると思います。また、CTF を通して様々な技術分野に触れるきっかけにもなると思いますので、自分の技術力の腕試しをしてみたい方など、ぜひCTF に参加してみてください!