CTFことはじめ


はじめに

寒くなってきましたね。皆さんいかがお過ごしでしょうか。私は風邪をひいています。
はじめまして。今年から新入社員として入社したピッカピカの1年目です。
先日賞与を会社からいただき、アウトレットモールに買い物をしに行きました。調子に乗って6万円のスウェットを購入しました。60%オフで6万円だったので、後悔はしておりません。

さて今回、アドベントカレンダーの場をお借りして初めて記事を書きます。今まで書く勇気とかモチベーションとかがありませんでしたが、同期に誘われたのと、ちょうど投稿日の12月10日で私の誕生日だったので、こりゃ縁起がいい!と思い立って書くことにしました。
何の記事を書こうかなーと悩んでいたのですが、先人の方々の記事を見て思いつきました。

私が所属する事業部では、上司の方が3日目の記事で書かれたように、CTF(Capture The Flag)のような社内SE技術者向けコンテストが開催されているようです。優勝チームは表彰もされており、すごくいいイベントだなと思いました。
そこで、新入社員の方向け(特に今まであまりITに触れてこなかった人)にも参加してもらいたいと思い、CTFって楽しいんだよ!!記事を書くことにしました。ちなみに私もCTFの問題を解くのは初めてです。やってみて面白かったし、この記事を見て興味を持ってもらえたら嬉しいです。

CTF is 何

前述の通り、CTFとはCapture The Flagの略称であり、いわゆる宝探しのようなものです。FPSをやっておられる方であれば、容易に想像がつくかもしれませんね。Wikipediaでも同じページに載ってます。

CTFの形式は2パターンあり、与えられた問題を解いていく形式と、同一環境内で攻撃する側と防御する側に分かれて競技する形式があります。本記事では主に問題を解いていく形式について説明します。
問題の形は様々で、与えられた暗号文を解読したり、大量のデータから情報を抽出したり、ソフトウェアの脆弱性を突いて閲覧できないページを閲覧できるようにするといった方法を用いて回答を見つけ出します。この回答をCTFではフラグと呼んでおり、参加者がフラグを探し求める、といった感じです。なんかハッカーになった気分ですよね。

コンテストでは上記のような問題が複数出題され、フラグを見つけると得点を獲得できます。そして得点の高さを競い合い、最終的に得点の高いチームが勝つといった流れになります。ジャンルは多岐に渡り、どの問題から解くこともできます。チーム戦であればメンバーを得意分野ごとに分けて挑戦するというのもアリですね。

先ほどジャンルは多岐に渡るとお話ししましたが、主なジャンルは以下の通りです。
- pwn
- Crypto
- Reversing
- Web
- Network
- misc (その他問題)

上5つに関してReversing以外のジャンルを問題を交えながら説明をしていきたいと思います(Reversingは私自身も理解できていないし、初心者にはとっつきにくいかと...)。また、私もCTF入門者であり、今回は是非初心者に触れてほしいという気持ちもあるため、簡単な問題を解くことにしています。全てマスターできなくても、チーム戦であれば力を発揮できることもあるかと思うので、興味のある分野があれば是非ご自身でもやってみてください。

CTFを実践する前に

日本では不正アクセスを実施した場合に法律によって罰せられることがあります。不正アクセスとは、本来アクセス権限を持たない人がシステムに対して攻撃を行い、不当にアクセスを行う行為等のことを指します。世間では無闇やたらにアクセスすると法律に引っかかるので、注意してください。

pwn

pwnとは元々「own」の打ち間違いから派生したスラングで、「pwnable」と呼ばれることもあるそうです。pwnは、プログラムの脆弱性を突いてフラグを見つけるといったジャンルとなっています。

スタックバッファオーバーフロー

本記事では、pwn問題でも最もシンプルなスタックバッファオーバーフローの解き方について説明します。

overflow.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void overflow(char *input){
    char buf[16];
    int secret = 0;
    strcpy(buf, input);

    if(secret == 0x45444342){
        system("cat answer.txt");
    }else{
        printf("Secret code is %x\n", secret);
    }
}

int main(int argc, char **argv){
    if(argc > 1){
        overflow(argv[1]);
    }
    return 0;
}

secretの値が0x45444342の場合、answer.txtから回答が出力されるみたいです。しかしsecretの値には0が格納されており、変更もされません。ひとまず実行してみると、

$ ./overflow "A"
Secret code is 0

上記のような出力がされます。じゃあどうやってsecretの値を書き換えるのでしょうか。ここでスタックバッファオーバーフローを利用します。バッファオーバーフローとは、予め確保されたメモリ領域を超えたデータを書き換えるといったものです。今回であればinputbufよりも大きい場合、超えた分のメモリは書き換わってしまいます。例えば、

$ ./overflow "AAAAAAAAAAAAAAAA" # Aが16個
Secret code is 0
$ ./overflow "AAAAAAAAAAAAAAAAA" # Aが17個
Secret code is 41

Aを16個渡した場合、secretの値は変わっておりませんが、17個渡した場合、バッファオーバーフローが発生し、secretの値が41に書き換わってます。ここで41は、AのASCIIコードの値です。つまり、16バイト以下の値はbufに格納され、17バイト以上の値はオーバフローが発生し、secretが書き換わるようになっています。
いま、secretの値が0x45444342になればよく、勘のいい方であればBのASCIIコードが42、CのASCIIコードが43...と気づくかもしれません。エンディアンというアドレス格納方式に気をつけながら入力すると、

$ ./overflow "AAAAAAAAAAAAAAAABCDE"
ctf{It's overflow.}

フラグを抽出できました。
pwnははじめは難しいと感じるかもしれませんが、慣れてくるとシェルを奪取してハッカー気分(?)になれるので、やりこみ要素はあると思います。

Crypto

Cryptoとは暗号を意味し、その名の通り暗号技術に関する問題が出題されます。身近なものであればシーザー暗号というものがあります。以下で説明しながら問題を解きたいと思います。

Caesar

以下のような暗号文があったとします。
synt_jrypbzr_gb_pgs
これは、元の平文がシーザー暗号を用いて暗号化されているものです。シーザー暗号とは、平文をアルファベット順にn文字ずらして暗号文を生成する手法です。一般的にはn=13ですので、今回は13文字ずらしたことになります。つまり、普通のアルファベットが
ABCDEFGHIJKLMNOPQRSTUVWXYZ
に対して、シーザー暗号を使うと
NOPQRSTUVWXYZABCDEFGHIJKLM
ということになります。したがって、与えられた文字を13文字元に戻してあげれば回答が手に入れられます。とりあえず13文字元に戻せるコードを以下に記載します(すごい適当ですが...)。

Caesar.py

crypto = 'synt_jrypbzr_gb_pgs'
caesar = 13
plain = ''

for char in list(crypto):
    ASCII = ord(char)
    if char != '_':
        num = ASCII - 97
        num = (num - caesar) % 26
        ASCII = num + 97
    plain += chr(ASCII)

print(plain)

結果はflag_welcome_to_ctfでした。それっぽいのが出ましたね。

Reversing

別名バイナリ解析とも呼ばれています。バイナリとはコンピュータが認識できるように「0」と「1」で表された形式のことを指します。流石に0と1だけで表わされたものを我々は読むことができないため、逆アセンブルと言う、より人間にわかりやすい言語に変換する手法があります。これを使ってコンピュータの動作を解析し、条件を満たす入力をすることによってフラグを獲得できます。一般的にReversingはpwnができるようになったら多少は勝手に力がつくそうです。ちょっと私も勉強してみます。

Web

Web問題は様々な種類があり、SQLインジェクションと呼ばれる、Webページの入力フォームでちょっとひねった入力を行うことでログインする問題や、RCE(Remote Code Execution)と呼ばれる、遠隔からコードを実行することでフラグを獲得するような問題があります。

SQLインジェクション

ここではSQLインジェクションを用いて問題を解こうと思います。問題はksnctfという、CTFの練習ができるサイトから引用したいと思います。ここではLoginという問題の導入部分のみを解きます。ページに遷移してみると、何やらadminでログインしろと書かれています。

普通であればadminに対応したパスワードなど分かるはずがないのですが、SQLインジェクションという脆弱性がこのサイトにある場合、パスワードを知らなくてもログインできてしまいます。
SQLインジェクションについて少しだけ説明します。例えば今回のSQL文が
SELECT * FROM user WHERE id='$id' AND pass='$pass'
であったとします。このときの$id$passの入力をするのですが、$idadmin$pass ' or 1=1 --と入力するとadminユーザの情報が抽出できるのです。上記のSQL文に当てはめてみると、
SELECT * FROM user WHERE id='admin' AND pass=' ' or 1=1 --'
ということですね。pass部分に注目していただきたいのですが、空白文字とクォーテーション 'とすることでpass文字列を決定しています。この条件式自体にはあまり意味はありません。理由は、後の1=1が全て真となるからです。そして、pass=' ' or 1=1と論理演算子orを用いることで、passadminのパスワードと一致しなくても、全て真となります。最後の--はコメントアウトを意味し、それ以降のSQL文をなかったことにします。

では上記のように入力フォームに入力してみると...

次の画面に遷移します。一応問題自体は続くのですが、今回はここまでとしておきます。
続きに関してはこちらの記事で詳しく説明している方がいるので参考にしてみてください。ちなみに、

これでもログインできます。理由は考えてみてください。

Network

CTFにおけるネットワーク問題は主に2種類あります。
1つはパケットと呼ばれる、ネットワーク通信を行うときに流れるものが含まれているファイルを解析する問題です。パケットが含まれているファイルをpcapファイルと呼び、これを解析することでフラグを獲得します。
もう1つはサーバにアクセスしてフラグを獲得する問題です。予め与えられたIPアドレスやパケットをもとにサーバへアクセスし、フラグを獲得します。
今回は1つ目のパケット解析の問題を紹介しようと思います。

パケット解析

今回はCTF for Beginners 2015で使用されたpcapファイルを解析します。問題ファイルはこちらです。
まず、Wiresharkというソフトウェアを用いてパケットを解析します。Wiresharkはパケットを解析できるソフトウェアで、いつ誰がどのようなプロトコルで通信を行ったのか、といった内容が記録されています。ここではWiresharkのインストール方法については省略します。
Wiresharkをインストールしたら、Wiresharkで先ほどのpcapファイルを開きましょう。開いてみると、以下のような状態になるかと思います。

パケットのリスト、パケットの詳細情報、そしてパケット内のバイナリデータが表示されると思います。ここから怪しいところを探していくのですが、プロトコルを見てみると、FTPというファイル転送プロトコルが使われているのがわかります。実はこの問題のタイトルが「File Transfer Protocol」なので、一応ヒントとしてそこを見てねって感じですかね。
FTPに着目しようと決まったので、とりあえずFTP通信以外はフィルタリングしましょう。ここで、以下の画像に示すように、ウィンドウ内の「表示フィルタを適用」という箇所に「ftp or ftp-data」と入力しエンターキーを押してください。するとFTP通信で使われる通信のみが画面に出力されます。

詳しくは説明しませんが、「ftp」が実際にFTP通信を行うための制御通信、「ftp-data」がデータ通信を表します。

次にパケットリスト中の一番右、Infoという欄を見てみてください。この箇所を全て読むのは難しいかもしれませんが、FTPサーバにログインし、その後「1.txtと2.zipと3.txtのデータをちょうだい」とリクエストを送り、それに対してデータを転送しているということがわかります。

したがって、この1.txt、2.zip、3.txtの中身を見てみると答えもしくはヒントが書かれてると推察します。
1.txtと3.txtの中身の見方は簡単です。パケット詳細情報のところに「Line-based text data(1 lines)」とあります。これを開いてみると...

何やらフラグっぽいものが出てきましたね。1.txtでは「ctf4b{This_communication_is」、3.txtでは「_encrypted.}」と記載されていました(3.txtは実際にご自身で確認してみてください)。
それでは最後に2.zipを確認してみましょう。しかし2.zip内に中身は記載されていません。そこでこのzipファイルをローカルにダウンロードしてみましょう。2.zipが含まれるFTP-DATAを右クリックし、「追跡→TCPストリーム」をクリックしてください。これは、指定したパケットに関連する通信内容(今回であればTCPセッション)のみ表示する機能です。ここで以下画像にあるように、ASCII形式からRaw形式に変換し、ローカルに保存してみてください。ASCII形式のまま保存すると展開したファイルがバイナリ型となってしまいます。保存後、zipファイルを展開し、中身を見てみると「_not」と記載されています。したがって、この問題の正解は「ctf4b{This_communication_is_not_encrypted.}」となります。確かに暗号化されていませんでしたね。

最後に

いかがでしたか。本記事で投稿した問題はどちらかといえば易しい問題であったため、今年から入社した新入社員でも多少なりとは理解できたかと思います。世の中にはこれ以上に難解な問題が多く存在し、そういった問題が解けるようになればお偉いさんの目に留まるかもしれません。少しでもコンテストに対する障壁が低くなれたら幸いです。
一応、CTFの問題が解けるサイトや、参考になる文献を以下に記載しておきます。

練習サイト

参考

また、writeupと呼ばれる解説もネット上に散らばっているのでそれを見てみるのも力になると思います。
今年も弊社ではコンテストが開催されると思うので、僕も出たいなーと思ってます。皆さんも参加してみてはいかがですか?