書籍「セキュリティコンテストのためのCTF問題集」を読んだ - 土日の勉強ノート

土日の勉強ノート

AI、機械学習、最適化、Pythonなどについて、技術調査、技術書の理解した内容、ソフトウェア/ツール作成について書いていきます

書籍「セキュリティコンテストのためのCTF問題集」を読んだ

前回 は、picoCTF の picoCTF 2023 の Binary Exploitation をやってみました。だいぶ時間がかかりましたが、解ける問題が増えてきた気がします。

今回は、[asin:B074888F88:title] という書籍を読む機会がありましたので、簡単な内容の紹介と、それぞれの問題の解説で使われていたツールの紹介と、実際にそのツールを使ってみたいと思います。

なお、この書籍は、以前の記事(セキュリティコンテストチャレンジブックの「Part2 pwn」を読んだ - 土日の勉強ノート)で読んだ セキュリティコンテストチャレンジブック CTFで学ぼう!情報を守るための戦い方 の続編のような感じでした。

それでは、やっていきます。

参考文献

今回、題材にさせて頂いた「セキュリティコンテストのためのCTF問題集」です。

[asin:B074888F88:detail]

USB開発に関する古い書籍です(2005年8月)。USB規格については、第1章に「これだけは知っておきたいUSBの基礎知識」に簡潔に10ページ程度にまとまっているだけです。必要に応じて、こちらを参照したいと思います。

こちらも、USB開発に関する古い書籍です(2005年12月)。USB規格については、第1章に「USBデータ転送プロトコルの基礎知識」に、24ページ程度でまとまっています。上の 10ページで不足した場合にこちらを活用したいと思います。

はじめに

「セキュリティ」の記事一覧です。良かったら参考にしてください。

セキュリティの記事一覧
・第1回:Ghidraで始めるリバースエンジニアリング(環境構築編)
・第2回:Ghidraで始めるリバースエンジニアリング(使い方編)
・第3回:VirtualBoxにParrotOS(OVA)をインストールする
・第4回:tcpdumpを理解して出力を正しく見れるようにする
・第5回:nginx(エンジンエックス)を理解する
・第6回:Python+Flask(WSGI+Werkzeug+Jinja2)を動かしてみる
・第7回:Python+FlaskのファイルをCython化してみる
・第8回:shadowファイルを理解してパスワードを解読してみる
・第9回:安全なWebアプリケーションの作り方(徳丸本)の環境構築
・第10回:Vue.jsの2.xと3.xをVue CLIを使って動かしてみる(ビルドも行う)
・第11回:Vue.jsのソースコードを確認する(ビルド後のソースも見てみる)
・第12回:徳丸本:OWASP ZAPの自動脆弱性スキャンをやってみる
・第13回:徳丸本:セッション管理を理解してセッションID漏洩で成りすましを試す
・第14回:OWASP ZAPの自動スキャン結果の分析と対策:パストラバーサル
・第15回:OWASP ZAPの自動スキャン結果の分析と対策:クロスサイトスクリプティング(XSS)
・第16回:OWASP ZAPの自動スキャン結果の分析と対策:SQLインジェクション
・第17回:OWASP ZAPの自動スキャン結果の分析と対策:オープンリダイレクト
・第18回:OWASP ZAPの自動スキャン結果の分析と対策:リスク中すべて
・第19回:CTF初心者向けのCpawCTFをやってみた
・第20回:hashcatの使い方(GPU実行時間の見積りとパスワード付きZIPファイル)
・第21回:Scapyの環境構築とネットワークプログラミング
・第22回:CpawCTF2にチャレンジします(クリア状況は随時更新します)
・第23回:K&Rのmalloc関数とfree関数を理解する
・第24回:C言語、アセンブラでシェルを起動するプログラムを作る(ARM64)
・第25回:機械語でシェルを起動するプログラムを作る(ARM64)
・第26回:入門セキュリhttps://github.com/SECCON/SECCON2017_online_CTF.gitティコンテスト(CTFを解きながら学ぶ実践技術)を読んだ
・第27回:x86-64 ELF(Linux)のアセンブラをGDBでデバッグしながら理解する(GDBコマンド、関連ツールもまとめておく)
・第28回:入門セキュリティコンテスト(CTFを解きながら学ぶ実践技術)のPwnable問題をやってみる
・第29回:実行ファイルのセキュリティ機構を調べるツール「checksec」のまとめ
・第30回:setodaNote CTF Exhibitionにチャレンジします(クリア状況は随時更新します)
・第31回:常設CTFのksnctfにチャレンジします(クリア状況は随時更新します)
・第32回:セキュリティコンテストチャレンジブックの「Part2 pwn」を読んだ
・第33回:セキュリティコンテストチャレンジブックの「付録」を読んでx86とx64のシェルコードを作った
・第34回:TryHackMeを始めてみたけどハードルが高かった話
・第35回:picoCTFを始めてみた(Beginner picoMini 2022:全13問完了)
・第36回:picoCTF 2024:Binary Exploitationの全10問をやってみた(Hardの1問は後日やります)
・第37回:picoCTF 2024:Reverse Engineeringの全7問をやってみた(Windowsプログラムの3問は後日やります)
・第38回:picoCTF 2024:General Skillsの全10問をやってみた
・第39回:picoCTF 2024:Web Exploitationの全6問をやってみた(最後の2問は解けず)
・第40回:picoCTF 2024:Forensicsの全8問をやってみた(最後の2問は解けず)
・第41回:picoCTF 2024:Cryptographyの全5問をやってみた(最後の2問は手つかず)
・第42回:picoCTF 2023:General Skillsの全6問をやってみた
・第43回:picoCTF 2023:Reverse Engineeringの全9問をやってみた
・第44回:picoCTF 2023:Binary Exploitationの全7問をやってみた(最後の1問は後日やります)
・第45回:書籍「セキュリティコンテストのためのCTF問題集」を読んだ ← 今回

この書籍「asin:B074888F88:title」を読んでみたいと思ったのは、以下の記事で、setodaNote CTF Exhibition の Logger という問題が解けなかったので、writeup を見てみると、この書籍で解説がされているということを知ったためです。

daisuke20240310.hatenablog.com

以下は「asin:B074888F88:title」のサポートサイトです。問題ファイルをダウンロードすることが出来ます。

book.mynavi.jp

では、書籍の章を参考に書き進めていきます。

Part1 バイナリ解析の問題

SelfReference

配布されるプログラム「SelfReference」に対してバイナリ解析を進める問題です。

いろんな角度から解析したところ、このプログラムは、コマンドライン引数で指定した文字列を暗号化してくれるものでした。暗号化アルゴリズムを読み解き、復号プログラムを実装して、暗号化されたフラグを復号してフラグを得る問題でした。

このプログラムを静的解析するツールとして、「radare2」が使われていました。「radare2」は、Ghidra や、IDA と同系統のツールで、バイナリを逆アセンブリ、C言語で表示する機能があります。名前はよく聞くツールですが、今は Ghidra の使いこなしを進めているので、ふーん、という感じで眺めて読み終わりました。

Simultaneous

配布されるプログラム「simltaneous」に対してバイナリ解析を進める問題です。

ltrace を使ってプログラムの動作を概要を把握していました。ltrace とは、Linux で使用されるデバッグツールで、プログラムが実行中に呼び出すライブラリ関数を出力してくれます。

その後は、gdb-peda を使って動的解析を行い、gdb、gdb-peda で使えるコマンドを解説しながら進めていき、このプログラムは、連立方程式を解く必要があることをつきとめます。

Python で、連立方程式を解くツールとして、z3 を紹介しています。z3 を使って 12変数の連立方程式を解き、フラグを得ています。機会があれば、z3 を使ってみたいと思います。

Part2 Pwnの問題

SECCONxコロッセオ 2017 サーバ弐

この問題は、CTF の問題形式として、King of the Hill(KoH)形式の問題です。

これまでやってきたのは、フラグを探して、見つけたらサーバに登録して、得点が得られる形式は、Jeopardy(ジョパディ)形式と言います。

KoH形式とは、Attack Point と Defense Point という 2種類のポイントがあり、Attack Point は、普通の問題と同じように、各問題サーバの問題を解いて、フラグを得たら、サーバに送ると得点になるというものです。Defense Point は、対象のサーバの問題を解くと、そのサーバに割り当てられているポイント(例えば、300ポイント)は一定時間ごとに、解いたチーム数(例えば、3チーム)で割ったポイントが得られます(3チームは 100ポイントずつ得られる)。一度、解いたサーバをずっと占有できるタイプだと、一定時間ごとにポイントが得られますし、一定時間で変化するサーバは、他チームにもチャンスが与えられたりと、いろいろな条件があるようです。

まず、Attack Point の問題が 3つ紹介されています(keyword1.txt、keyword2.txt、keyword3.txt に格納されているフラグを読む)。

1つ目の keyword1.txt は、catコマンドでフラグが格納されているファイルを読むだけの問題です。

2つ目の keyword2.txt は、ログインしたユーザとは別の、特定のユーザだけしか読めないパーミッションになっています。同じディレクトリにスティッキービットのセットされたプログラムとソースコードが置かれていて、そのプログラムをエクスプロイトすることで、keyword2.txt を読み出すという問題です。

そのソースコードは、バイナリファイルを読んで、その内容を実行するという内容でした。そこで、keyword2.txt を読み出すシェルコードを作って、それをバイナリファイルに格納して、スティッキービットが設定されたプログラムを実行することでフラグが得られる問題でした。

3つ目の keyword3.txt は、2つ目と同様に、また別のユーザだけしか読めないファイルとなっており、また別のスティッキービットの設定されたプログラムを使ってフラグを読み出すという内容です。

このプログラムのソースコードも置かれていて、scanf関数に脆弱性があるプログラムでした。scanf関数の場合、シェルコードを作る際、0x00(NULL)、0x20(スペース)、0x0A(改行)の文字が含まれると、終端文字として認識されるので使えません。これらの文字を避けてシェルコードを作るか、stagerという手法が紹介されています。とても興味深いので、これについては実践したいと思います。

以前、setodaNote CTF Exhibition の Shellcode という問題のためにシェルコードを作成しました。

daisuke20240310.hatenablog.com

この記事を読み返したところ、同じく、scanf関数の脆弱性へのエクスプロイトだったんですが、NULL などの文字を意識せずにシェルコードを作成していました。それでもなぜかフラグは取れたのですが、なぜでしょう。。気になるので、この時作成したシェルコードの動作も見ていきたいと思います。

時間がかかりそうなので、後でやります。

※2024/12/30:追記

上の記事で、NULL文字を含んだシェルコードにもかかわらず、うまく動作してい理由が分かりました。scanf関数に "%[^\n]" が指定されていたため、改行文字以外は NULL文字であっても入力として認識される、という理由でした。お騒がせでした。

cheer_msg

プログラムバイナリと libc が与えられる問題です。また、サーバアドレスとポート番号が与えられて、そこでフラグを得る問題です。

表層解析が行われ、GOT Overwrite可能、スタックカナリア有効、メモリ実行不可の状態でした。

その後、静的解析、動的解析が行われ、プログラムの動作を確認して、プログラムの脆弱性を探していきます。プログラムでは入力するメッセージの長さを入力する手順があります。この長さの入力のチェックが不十分で、負の値の入力が可能であることをつきとめられています。これにより、本来なら書き換えることが出来ないリターンアドレスを変更できる攻撃が可能となります。

次は攻撃方法についてです。シェルを取りに行きたいのですが、メモリ実行不可なので、シェルコードを動作させることは難しい状況です。

次に、このプログラム内で system関数や execve関数を使っているなら、リターンアドレスをそれらの PLT に変更し、シェルを起動する(ret2plt)ことが出来そうですが、今回のプログラムではこれらの関数は使われていないようです。

次に、libc の関数を使うことを考えます。ASLR が有効なので、事前に libc の関数のアドレスは取得できませんが、プログラム実行中に GOT を参照することで、libc 内の関数のアドレスが取得できます。libc 内の相対的なアドレスは変化しないので、その相対的なアドレスを調べることで、別の libc の関数のアドレスを計算することが出来ます。これにより、system関数や execve関数を実行してシェルを起動する(ret2libc)ことが出来るようになります。

書籍では、pwntools を使い、エクスプロイトコードを実装してフラグを得ていました。

また、書籍では、ret2plt や ret2libc についての詳細な技術内容は、前著の セキュリティコンテストチャレンジブック CTFで学ぼう!情報を守るための戦い方 を参照してください、としていました。

checker

プログラムバイナリが与えられる問題です。また、サーバアドレスとポート番号が与えられて、そこでフラグを得る問題です。

表層解析が行われ、Full RELRO により GOT Overwrite不可、スタックカナリア有効、メモリ実行不可の状態でした。

プログラムを実行して動作を確認しています。先ほどの問題と同じように、メッセージのやり取りを行うプログラムでした。

次に、静的解析を行います。逆アセンブラを読んで、プログラムの詳細な動きを確認していってます。ある程度、理解が進んだところで、次は、脆弱性を探していきます。

ユーザの入力を取得する関数を詳細に解析していくと、bss領域に確保した変数(配列)に、ユーザの入力を保存していることが分かります。この入力により、バッファオーバーフローを起こすことが出来るようです。これまでのバッファオーバーフローは、スタック、ヒープ領域が多かったのですが、bss領域のケースは珍しいです。また、フラグは、このユーザ入力を格納する領域の、次のアドレスから始まる領域に格納されているようです。

脆弱性は分かったので、次は攻撃方法を考えていきます。今回はフラグがこのプログラムの扱う領域内に格納されているため、シェルを取る必要はなく、リークでフラグの内容を読み出せればいいことになります。

スタックのバッファオーバーフローなら、リターンアドレスを書き換えることを目指します。ヒープ領域のバッファオーバーフローの場合は、同じヒープ領域の別の領域を書き換えることを目指したり、Use After Free で、解放された領域を参照してしまう脆弱性を使って、同じ領域を確保して書き換えて、プログラムの流れを操作することを目指す、などです。

今回、書籍で紹介されている攻撃方法は、なかなか興味深いです。スタックカナリアはスタックのバッファオーバーフローなどにより、スタックを書き換えられたことを検知して、プログラムを誤動作させないためのセキュリティ機構です。今回紹介されている攻撃方法は、スタックバッファオーバーフローで、カナリアの領域を書き換えて、スタックカナリアの書き換えを検知したことをログ出力するところで、フラグを出力させます。

具体的には、スタックカナリアはスタックの書き換えを検知すると、stack_chk_fail関数をコールします。その後、*** stack smashing detected *** というメッセージとともに、スタックに格納されたプログラム名をログ出力します。このプログラム名が格納されたスタックを書き換えて、プログラム名が出力される代わりに、フラグをログで出力させるという攻撃を行います。

スタックカナリアが有効になっていると、スタックバッファオーバーフローは使えないと考えてしまいがちですが、それを逆手にとって、スタックカナリアの準備した関数を使って、フラグをリークさせるという攻撃を行う問題でした。

Part3 ネットワークの問題

File Transfer Protocol

FTP の通信を記録した pcapファイルを解析する問題です。

ここでは、Wireshark の使い方を解説しながら、FTP の通信ログの中に、3つのファイルをダウンロードする部分があり、そのファイルの内容を連結するとフラグが得られていました。

Re:Build

2つの JPGファイルが与えられて解析する問題です。

見た目は同じ画像ファイルでしたが、バイナリで確認すると、片方には、末尾に pcapファイルが付いているようです。その pcapファイルを解析していき、HTTP通信であることが分かり、その通信の中で、分割された画像ファイルを取得しています。順番にファイルを連結したところ、フラグが書かれた画像ファイルが得られました。

What do you type?

USB通信が記録された pcapファイルを解析する問題です。今回、この書籍を読みたかったのは、この問題の解説が読みたかったからでした。

サポートサイトから問題ファイルの「mondai8.zip」をダウンロードします。解凍すると、problem.pcap が得られます。Wireshark で開きます。

まず、USBパケットのキャプチャ方法には 2通りあるそうです。

  • Linux環境で usbmon を有効にして、tcpdump や Wireshark でキャプチャする方法
  • Windows で、USBpcap をインストールして、USBpcap のコマンドラインツールや Wireshark でキャプチャする方法

どちらの方法かは、下図の Frame の先頭を見て、USB packets with USBPcap Header となっていれば、USBpcap を使ったキャプチャであることが分かります。

problem.pcap
problem.pcap

フォーマットについては、usbmon の場合は、https://github.com/the-tcpdump-group/libpcap/blob/master/pcap/usb.h の struct _usb_header を見ます。以下です。

typedef struct _usb_header {
    uint64_t id;
    uint8_t event_type;
    uint8_t transfer_type;
    uint8_t endpoint_number;
    uint8_t device_address;
    uint16_t bus_id;
    char setup_flag;/*if !=0 the urb setup header is not present*/
    char data_flag; /*if !=0 no urb data is present*/
    int64_t ts_sec;
    int32_t ts_usec;
    int32_t status;
    uint32_t urb_len;
    uint32_t data_len; /* amount of urb data really present in this event*/
    pcap_usb_setup setup;
} pcap_usb_header;

USBpcap の場合は、https://github.com/desowin/usbpcap/blob/master/USBPcapDriver/include/USBPcap.h の typedef struct USBPCAP_BUFFER_PACKET_HEADER を見ます。以下です。

#pragma pack(push, 1)
typedef struct
{
    USHORT       headerLen; /* This header length */
    UINT64       irpId;     /* I/O Request packet ID */
    USBD_STATUS  status;    /* USB status code (on return from host controller) */
    USHORT       function;  /* URB Function */
    UCHAR        info;      /* I/O Request info */

    USHORT       bus;       /* bus (RootHub) number */
    USHORT       device;    /* device address */
    UCHAR        endpoint;  /* endpoint number and transfer direction */
    UCHAR        transfer;  /* transfer type */

    UINT32       dataLength;/* Data length */
} USBPCAP_BUFFER_PACKET_HEADER, *PUSBPCAP_BUFFER_PACKET_HEADER;
#pragma pack(pop)

今回は、USBpcap なので、この構造体と実際のデータを見比べていきます。

フィールド名 サイズ
headerLen 2 28
irpId 8 0xFFFF848704BA8990
status 4 0x00000000
function 2 0x000B
info 1 0x00
bus 2 0x0001
device 0x0004
endpoint 1 0x00(bit7 が 0:ホスト→デバイス、1:デバイス→ホスト)
transfer 1 0x02(0:アイソクロナス転送、1:インタラプト転送、2:コントロール転送、3:バルク転送)

では、パケットをざっくり見ていきます。最初の方は、Descriptor の転送がされています。途中からは、デバイスからホストの転送が続きます。Descriptor を見ることで、機器は USBキーボードと分かるそうです。実際に、下図のように、デバイスからホストに転送される Descriptor を順番に見ていくと、Keyboard と書かれてるところが見つかります。

キーボードと分かるデスクリプタ
キーボードと分かるデスクリプタ

パケットは、途中から USB_INTERRUPT in が続きます。これらが、キーボードに入力された内容が入っているようです。

まず、以下から USB規格の HID(Device Class Definition for Human Inteface Devices)をダウンロードできます。

www.usb.org

「B.1 Protocol 1 (Keyboard)」に、以下が記載されています。Modifier keys は後述します。Keycode 1~6 は同時押しの場合に複数の値が入ります。

input report
input report

Modifier keys は、検索してみると、「8.3 Report Format for Array Items」に以下が記載されています。同時に押された Shiftキーなどです。

Modifier keys
Modifier keys

Keycode とどのキーが紐づいているのかは、別の規格書にあります。以下から USB規格の HID Usage Tables をダウンロードできます。

usb.org

10章の「Keyboard/Keypad Page(0x07)」に、Keycode(Usage ID)とキー(Usage Name)の表が記載されています。

Keycode
Keycode

では、実際に Wireshark のパケットを見て、どのキーが押されているのかを確認してみます。Wireshark の USB_INTERRUPT in のパケットの 1つを見ると、左Shiftキーと、0x0B(Usage ID)は Hキーです。Wireshark は Modifier keys をちゃんと書いてくれてるので、HID の規格は無くても分かりましたね。

キーの特定
キーの特定

では、有効そうなパケット(Array が 0以外)を見ていきます。「Hello Boo. Let you know your password. Your password is "flag{Alic3_is_cut3!!}"」になりました。手動でパケットを 1つずつ見ましたが、書籍を見ると Pythonスクリプトで自動で読み取ってました。

USBキーボードの問題は以上です。

ソーシャルハック?

URL が与えられ、犯人を追い詰める、という問題のようです。URL にアクセスしてみると、LINE のようなチャット画面が確認できます。犯人とチャットでやり取りしていきます。いくつかやり取りをすると、画像を送ってください、と言われます。何らかの方法で画像を犯人に渡す必要があります。

ここでは、犯人の情報を得るため、自前の Webサーバを立ち上げ、ダミーの画像ファイルをアップロードして、チャットでダミーの画像ファイルの URL を伝えます。すると、犯人(問題のサーバ)は、URL にアクセスしてくるので、立ち上げた Webサーバのアクセス履歴を確認して、犯人の IPアドレスを得ています。

アクセス履歴には、犯人からのメッセージも含まれていて、「MyVNCpasswordIsVNCpass123」となっていました。犯人のサーバの IPアドレス、狙うべきサービスは VNC であることが分かりました。VNC のポート番号は、5900 から使われるので、適当にアクセスしてみると、GUIログイン出来て、そのデスクトップにメモがあり、そこにフラグが見つかります。

Part4 Webの問題

Login Me!

URL が与えられ、アクセスしてみると、ログイン画面(ID とパスワードの入力フォーム)があります。このログイン画面を突破する問題です。

こういう場合に、どういうステップで進めていくのかが解説されています。

  1. まず、robots.txt、.git、index.php、index.php.bak にアクセスしてみる(今回はヒットせず)
  2. ID とパスワードの推測(何らかのヒントが示された場合、今回はヒントなし)
  3. ログイン済みセッションを奪う(適当な ID とパスワードでログインしてみると、ログインが失敗したことと、入力した ID が表示される、そこで、<>" という XSS で使えそうな文字を ID で送ってみるとエスケープされたので、これは使えない)
  4. SQLインジェクションを試す(ID に hoge' OR 1=1; --(入力の末尾には半角スペースが必要なことに注意!)を入力したところ、adam などと表示されたことで、SQLインジェクションが有効そうであることが分かった)

これらのステップから SQLインジェクションが有効そうだと分かりました。続いて、hoge' UNION SELECT 1,2,3; -- を試します。連番のところを 1つずつ増やしていきます。4 まで入力したところで、挙動が変わりました。

(続きは後回し)

Bonsai XSS Revolutions

Amazing Language

Part5 その他の問題

Venus

Binary, Encrypted PPTX

おわりに

今回は、セキュリティコンテストチャレンジブック CTFで学ぼう!情報を守るための戦い方 の続編と思われる [asin:B074888F88:title] を読みました。実践的な内容でしたが、もう少し、解説が多くても良いのでは、と思いました。一部、読み切れなかったところがありますが、また時間を見つけて埋めていきたいと思います。

最後になりましたが、エンジニアグループのランキングに参加中です。

気楽にポチッとよろしくお願いいたします🙇

今回は以上です!

最後までお読みいただき、ありがとうございました。