安全なコーディングは非常に難しい練習です。 通常、プログラマーがソフトウェアを開発するとき、彼らの目的はソフトウェアが壊れることではなく、動作するようにすることです。 この過程で、より安全な関数の代わりにレガシーな関数を使用した場合に、脆弱性が発生することがあります。
C言語は、本質的に非常に汎用的で強力な言語の1つですが、1つの重大な欠点があり、Cベースのソフトウェアのセキュリティは、プログラマーの知識に依存します。 つまり、プログラマーが安全なコーディングについてよく理解していれば、そのソフトウェアも安全であるということです。
私のように、プログラミングの知識はあるがセキュリティ業界は初めてという者にとって、脆弱なコードを研究し、起こりうる結果を理解することは非常に重要です。
正直なところ、バッファオーバーフローのような脆弱性を探す場合、アプリケーションの完全なソースコードを研究するのはかなり面倒です。 この方法にはそれなりの利点がありますが、致命的かもしれない単純な脆弱性を見つけるには、最も簡単な方法とは言えません。 そのような脆弱性はすぐに解決しなければならず、それを見つける最も簡単な方法は、ファジングと呼ばれる手法です。
ファジングは実行ファイルに「ランダムに」発生するデータを送ってコードに「簡単な」脆弱性を発見するための手法です。 一般に、ファザーには 3 つのタイプがあります。
- 変異。 不正な入力サンプルが生成され、実行可能ファイルに提供される「ダム」ファジングの一種。 この入力は、アプリケーションが期待するタイプの入力に適合することもしないこともあるので、本当のバグを発見できる確率は高くありません。 ファザーアルゴリズムがゼロから不正な入力を生成できるように、いくつかの初期テストデータを必要とする「スマート」ファジングの一種です。 このタイプのファジングは、プログラムが期待される入力を受け取るので、多くの場合、ダム ファジングよりも優れています。
- 進化型: このタイプのファザーは、各「ファズ」からのフィードバックを使用して、入力の形式を時間とともに学習します。
この投稿では、American Fuzzy Lop (AFL) によるファジングについて見ていきます。 これは進化型ファザーの一種で、STDIN またはファイルから入力を受け取るプログラムのファジングに適しています。
Peach や syzkaller など、数多くのファザーが乱立しています。 では、なぜ AFL なのでしょうか。
- ユースケース。 これは、考慮すべき最も重要なポイントです。 私のユースケースは、ファイルから入力を受け取るアプリケーションをファジングすることでした。
- インストールは簡単です。
- AFLのUIインターフェースには、ファジングプロセスのリアルタイム統計など、たくさんの情報が詰まっています。
AFLの設定
AFLの設定は簡単で、私はあなたに代わってそれをインストールする簡単(しかし粗雑な)シェルスクリプトを書いて簡単にしました!
AFLのインストールは簡単です。 ユーザー権限でスクリプトを実行すると、依存関係、AFL、関連するツールをすべてインストールします。 シェルスクリプトはここにあります。 https://github.com/nikhilh-20/enpm691_project/blob/master/install_afl.sh
ファジングするアプリケーションを選択する
この投稿では、ソース コードがあるアプリケーションのファジングのみについて見ていきます。 これは、AFL が実行、エラー、およびパフォーマンスに関連する他のものを監視するために、ソース コードを計測するためです。 実行ファイルを直接ファジングすることも可能ですが、これは実験的なものであり、この記事の範囲外です (ヒント: QEMU が必要です)。
ファジング用に GitHub から任意のオープン ソース システムを選択します。
ファジングを行うために、GitHub から任意のオープン ソース システムを選択します。選択するものが有名であればあるほど、脆弱性の数が少なくなる可能性があります。 他の人もバグを探しているのです! 私が脆弱なコードを見つけるために使用している1つの簡単な方法は、GitHub検索を使用することです。
- 脆弱性のある関数、たとえば strcpy.
- 検索結果は数百万件になります。 結果のコミット カテゴリに移動します。 ここで、strcpy が使用された (または、削除された) それらのリポジトリを見つけることができます。
アプリケーションを計測する
プライバシー上の理由から、使用しているリポジトリを公開することはできません。
gitリポジトリをクローンします。
nikhilh@ubuntu:~$ git clone https://github.com/vuln; cd vuln
環境変数、AFL_HARDEN=1を設定します。
nikhilh@ubuntu:~/vuln$ export AFL_HARDEN=1
コンパイラフラグを設定して、脆弱性を見つけやすくするためにアプリケーションをコンパイルするようにします。 理想的には、必要なものを設定するために環境変数を使用しますが、かなり多くのカスタマイズがあります。
使用するコンパイラーが、それぞれ gcc と clang ではなく afl-gcc または afl-clang であることを確認します。
コンパイラフラグの追加:
-fno-stack-protector は、バッファオーバーフローを悪用するためのスタックプロテクターを無効にします。
-m32 は32ビットマシンを使っている場合のみ必要で、それ以外は不要です。 make を実行します。
Instrumented 123 locations (32-bit, hardened-mode, ratio 100%).
このようなステートメントがない場合は、AFL がアプリケーションのコードをファジング用にアクティブにしていないことを意味します。 言い換えると、ソース コードを正常にインストルメント化できていません。
テスト サンプル
AFL は進化型ファザーです。 つまり、世代ベースのファザーと同様に、ターゲット アプリケーションが期待するデータの種類を理解するために、初期のテスト データも必要です。 オープン ソース システムをターゲットにする場合、これを見つけるのは簡単です。
nikhilh@ubuntu:~/vuln$ mkdir afl_in afl_out
nikhilh@ubuntu:~/vuln$ cp test/* afl_in/
Fuzzing Begins
テストサンプルが揃いましたので、ファズの準備ができました!
ファジィを開始するには、次のようにします。
ああ、ちょっと待ってください…アプリケーションのクラッシュ通知の送信先も変更する必要があります。 By default, when an application crashes, the core dump (basically, the contents of RAM are stored in a file to help in debugging) notification is sent to the system’s core handler. We don’t want this. Why? By the time this notification reaches AFL, it’ll be classified as a timeout rather than a crash.
nikhilh@ubuntu:~/vuln$ sudo su
password for nikhilh:
root@ubuntu:/home/nikhilh/vuln# echo core > /proc/sys/kernel/core_pattern
root@ubuntu:/home/nikhilh/vuln# exit
NOW, we are ready to fuzz!
nikhilh@ubuntu:~/vuln$ afl-fuzz -i afl_in -o afl_out -S slaveX — ./vuln @@
Command line flags used:
- -i — This marks the test input directory.
- -o- これは、AFL がクラッシュ、ハングアップなどに関する有用な情報を書き込むディレクトリです。
- -S – これは、スレーブ モードです。 基本的に、AFL は入力をランダムに微調整し、非決定論的ファジングを引き起こします。
- -M オプションはマスターモードで、決定論的ファジングを行い、基本的に入力のすべてのビットが何らかの形で変更されることを意味します。 (これは遅いです! … 明らかに。)
- @@ – これは入力テスト ファイルがある位置です。 AFL はこれを自動的に代入してくれます。 実行ファイルが STDIN から入力を受け取る場合、これは必要ありません。
ファジング結果
これは表示するのに少し時間がかかります。 多くの場合、人々は 24 時間以上ファジングを行います (そして、何も得られないまま終わることもあります)。 私の場合、アプリケーションが少し脆弱すぎたようで、1 時間以内に 516 件のユニークなクラッシュが発生しました。 しかし、それは 516 の脆弱性があるという意味ではありません!
ファジング セッションは Ctrl-C で終了することができます。
Analysis Phase
Now that we have results, we need to analyze them to see which ones are exploitable. To this end, we will use one of AFL’s utilities called afl-collect. This will have been installed through the installation script as well.
nikhilh@ubuntu:~/afl-utils$ afl-collect -d crashes.db -e gdb_script -r -rr ~/vuln/afl_out/slaveX ./output_dir — ~/vuln/vuln
To understand what each command line flag does, refer to its help section.
nikhilh@ubuntu:~/afl-utils$ afl-collect — help
If you see lines such as these in the output, celebrate! You’ve found something interesting to try and exploit.
*** GDB+EXPLOITABLE SCRIPT OUTPUT ***
…
…
slaveX:id:000000,sig:11,src:000000,op:havoc,rep:2……………:EXPLOITABLE
…
slaveX:id:000046,sig:11,src:000004,op:havoc,rep:4……:EXPLOITABLE
AFL は、どの入力によってアプリケーションがクラッシュしたかを表示します。 このケースでは、ファイル: id:000046,sig:11,src:000004,op:havoc,rep:4 が、アプリケーションの StackBufferOverflow を引き起こしたことを示しています。 このようなファイルは ../afl_out-slaveX/crashes/
完了!
ファジングのクイックスタートは以上です!
ファジングを行うには、次のような方法があります。 このプロセスは本当にシンプルで、すべて自動化されているため非常に便利です。 次のステップは、入力がなぜバッファ オーバーフローを引き起こしたかを分析し、それを悪用する方法を探すことです。 すべての脆弱性がエクスプロイトにつながるわけではないことを忘れないでください。