← All Articles

Boofuzzで始めるネットワークプロトコルファジング

Posted on

boofuzz

環境準備


Windows端末にboofuzzをソースコードからインストールします。手順はboofuzzのドキュメントを参照してください。

ドキュメントには記載されていませんが、Python2.7を使用しないとFuzzing対象プロセスを監視するprocess_monitor.pyが動作しませんので注意が必要です。

Boofuzzは、Fuzzing toolであるSulleyの後継として作られたOSSです。BoofuzzのBooはMonsters incのBooから取っているそうです。

Sulleyはもうメンテナンスされていないので、これからネットワークプロトコルファジングを始める方はBoofuzzを使うのが良いでしょう。

BoofuzzはSulleyよりもネット上に落ちている情報が少ないと感じるかもしれませんが、Sulleyについて書かれた情報はBoofuzzでも活用できるものが多いはずです。

Boo

Fuzzing開始


1. Fuzzing対象とするバイナリを用意

今回はWebサーバのSavant 3.0を実例に使用します。

Savant icon


2. process_monitor.pyを実行

python process_monitor.pyを実行するとプロセス監視サーバが26002番ポートで立ち上がります。

process_monitor.py


3. Boofuzzを実行するPythonスクリプトを用意

以下に今回用意したサンプルスクリプトのソースコードを記載します。

start_commandsstop_commandsを指定することで、テストケース毎にSavant.exeを実行しなおすようにしています。これをやらない場合、あるテストケースでクラッシュが発生したとしても、そのテストケース単体を入力してもクラッシュが発生しない場合があるので注意が必要です。

procmon=bf.pedrpc.Client(host, 26002)ではprocess_monitor.pyで立ち上げたプロセス監視用サーバを指定しています。

bf.s_initialize()にて、テストケースとするパケットの構造をブロックとして指定します。今回はベーシックなHTTPプロトコルの構造を指定します。

プロトコル構造の指定方法については、以下サンプルスクリプトと読めばだいたい想像がつくかと思います。詳細を知りたい方はBoofuzzのドキュメントを参照してください。

WebサーバへのFuzzingサンプル
import boofuzz as bf
import datetime


def main():
    port = 80
    host = 'localhost'
    protocol = 'tcp'

    start_commands = ['C:\\Savant\\Savant.exe']
    stop_commands = ['wmic process where (name="Savant.exe") delete']

    now = datetime.datetime.now()
    csv_log_name = now.strftime('%Y%m%d_%H%M%S') + '_fuzzing.csv'
    csv_log = open(csv_log_name, 'w')
    my_logger = [bf.FuzzLoggerCsv(file_handle=csv_log)]

    target = bf.Target(
        connection=bf.SocketConnection(host, port, proto=protocol),
        procmon=bf.pedrpc.Client(host, 26002),
        procmon_options={
            'start_commands': start_commands,
            'stop_commands': stop_commands,
        }
    )

    session = bf.Session(
            target=target,
            fuzz_loggers=my_logger,
    )

    bf.s_initialize(name='Request')
    bf.s_group(
        'Method',
        [
            'GET',
            'HEAD',
            'POST',
            'PUT',
            'DELETE',
            'CONNECT',
            'OPTIONS',
            'TRACE',
        ]
    )
    bf.s_delim(' ', name='space-1')
    bf.s_string('/index.html', name='Request-URI')
    bf.s_delim(' ', name='space-2')
    bf.s_string('HTTP/1.1', name='HTTP-Version')
    bf.s_static('\r\n', name='Request-Line-CRLF')
    bf.s_string('Host:', name='Host-Line')
    bf.s_delim(' ', name='space-3')
    bf.s_string('example.com', name='Host-Line-Value')
    bf.s_static('\r\n', name='Host-Line-CRLF')
    bf.s_static('\r\n', 'Request-CRLF')

    session.connect(bf.s_get('Request'))
    session.fuzz()


if __name__ == '__main__':
    main()

4. 作成したPythonスクリプトからBoofuzzを実行

http://localhost:26000へアクセスするとプロセス監視サーバのGUIにて、発生したクラッシュの情報を確認することが出来ます。

赤の下線で示した部分を見ると、今回は004091670040a2e5の2か所でmemory access violationが発生していることが分かります。

Crash synopsis

画面左の506と書かれた数字をクリックすると460番のテストケースに関する詳細情報を見ることが出来ます。

入力パケットが ’\’ を大量に繰り返していることが分かります。

Test case log

シンプルなPythonスクリプトでテストケースをパケットとして送信してみると実際にSavant 3.0がクラッシュすることを確認できます。

そのときのPCAPをWiresharkで確認してみるとこんな感じです。

Test case 506 packet

余談ですが、RawCapを使えばWindowsでlocalhost宛のループバック通信をキャプチャすることが可能です。


5. クラッシュした原因をリバースエンジニアリングで調査

0040a2e5でmemory access violationが発生していましたが、そこからIDA Pro等の逆アセンブラ/デバッガにて実行パスを逆に遡っていくと、char FileName\[260]としてHTTPのRequest URIを保存していることが分かります。

※ 皆さんのFuzzing環境とデバッガのある環境が別の端末の場合には、リモートデバッグが便利です。やり方はIDA Proによるリモートデバッグに記載しています。

Buffer of request URI

ここから順に実行パスをたどると、Request URIの末尾が ’\’ である場合に、GetFileAttributesA(Filename);をCallしています。(デコンパイラ上ではエスケープされて’\‘と表示)

GetFileAttributesA(Filename);が失敗した場合には、Fuzzingにてクラッシュが発生した関数をCallすること流れとなっています。

以上より、テストケース506ではRequest URIが260バイト以上のサイズを超える ’\’ で埋められているためにバッファオーバフロー(BOF)が発生したことで、0040a2e5にてmemory access violationが発生したと判明します。

xref to func


Boofuzzを使ってBOFの脆弱性を見つけることができました。


reversingfuzzingida pro