7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

セキュリティAdvent Calendar 2021

Day 3

AFLでファジングやってみた

Last updated at Posted at 2021-12-03

はじめに

IPAのファジング実践資料(AFL編)を参考に、AFLでファジングしてみました。

AFLとは

AFL (American Fuzzy Lop) は、最も著名なファジングツールの1つです。

やってみた

環境構築

仮想サーバ起動

仮想サーバ上でファジングをやりましょう。
Ubuntuの仮想サーバを起動します。

$ git clone git@github.com:kannkyo/boilerplate-vagrant.git
$ cd vagrant-sandbox/ubuntu-focal64-20201117.0.0/
$ vagrant up
$ vagrant ssh

AFL インストール

Ubuntuのオフィシャルレポジトリは、AFL派生版のafl++を提供しています。

apt-getで簡単にインストールできるのでafl++をインストールしましょう。

$ sudo apt update
$ sudo apt install afl++ afl++-clang afl++-doc

RAMディスク作成

aflの開発コミュニティは、RAMディスク上でaflを実行することを推奨しています。
aflはファイルI/Oが多く、HDDの寿命を縮める恐れが有ります。

RAMディスク /tmp/afl-ramdisk/ を作成しましょう。

$ mkdir -p /tmp/afl-ramdisk && chmod 777 /tmp/afl-ramdisk/
$ sudo mount -t tmpfs -o size=512M tmpfs /tmp/afl-ramdisk/
$ cd /tmp/afl-ramdisk/

ソースコードの準備

まず、ファジングの対象とするソースコードを作成します。

$ cat <<EOF >example.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char** argv)
{
  char buf[8];

  if(read(0, buf, 8) < 1)
  {
     exit(1);
  }

  printf(buf);
  exit(0);
}
EOF

標準入力から文字列を読み取るプログラムです。

コンパイル

afl-gccを使って、ソースコードをコンパイルします。
afl-gccgccのラッパーです。

コード内に測定用コードを埋め込んだあとにコンパイルします。
測定コードは、ファジングにより発見された不具合の発生箇所の特定、カバレッジの測定などを可能にします。

afl-gccのマニュアルを確認します。

$ afl-gcc 
afl-cc++2.59d by Michal Zalewski
[!] NOTE: afl-gcc is deprecated, llvm_mode is much faster and has more options

This is a helper application for afl-fuzz. It serves as a drop-in replacement
for gcc or clang, letting you recompile third-party code with the required
runtime instrumentation. A common use pattern would be one of the following:

  CC=/usr/bin/afl-gcc ./configure
  CXX=/usr/bin/afl-g++ ./configure

You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.
Setting AFL_HARDEN enables hardening optimizations in the compiled code.

afl-gccは廃止deprecatedされていて、中身はafl+cc++に書き換わっているようです。

コンパイルを実行します。

$ afl-gcc -o example example.c
afl-cc++2.59d by Michal Zalewski
[!] NOTE: afl-gcc is deprecated, llvm_mode is much faster and has more options
example.c: In function ‘main’:
example.c:14:10: warning: format not a string literal and no format arguments [-Wformat-security]
   14 |   printf(buf);
      |          ^~~
afl-as++2.59d by Michal Zalewski
[+] Instrumented 2 locations (64-bit, non-hardened mode, ratio 100%).

ファジング

afl-fuzzコマンドを使って、ファジングを実行します。

afl-fuzzのマニュアルを確認します。

$ afl-fuzz 
afl-fuzz++2.59d based on afl by Michal Zalewski and a big online community

afl-fuzz [ options ] -- /path/to/fuzzed_app [ ... ]

Required parameters:
  -i dir        - input directory with test cases
  -o dir        - output directory for fuzzer findings

Execution control settings:
  -p schedule   - power schedules recompute a seed's performance score.
                  <explore (default), fast, coe, lin, quad, or exploit>
                  see docs/power_schedules.txt
  -f file       - location read by the fuzzed program (stdin)
  -t msec       - timeout for each run (auto-scaled, 50-1000 ms)
  -m megs       - memory limit for child process (50 MB)
  -Q            - use binary-only instrumentation (QEMU mode)
  -U            - use unicorn-based instrumentation (Unicorn mode)
  -W            - use qemu-based instrumentation with Wine (Wine mode)

Mutator settings:
  -R[R]         - add Radamsa as mutator, add another -R to exclusivly run it
  -L minutes    - use MOpt(imize) mode and set the limit time for entering the
                  pacemaker mode (minutes of no new paths, 0 = immediately).
                  a recommended value is 10-60. see docs/README.MOpt

Fuzzing behavior settings:
  -N            - do not unlink the fuzzing input file
  -d            - quick & dirty mode (skips deterministic steps)
  -n            - fuzz without instrumentation (dumb mode)
  -x dir        - optional fuzzer dictionary (see README, its really good!)

Testing settings:
  -s seed       - use a fixed seed for the RNG
  -V seconds    - fuzz for a maximum total time of seconds then terminate
  -E execs      - fuzz for a maximum number of total executions then terminate
  Note: -V/-E are not precise, they are checked after a queue entry is done
  which can be many minutes/execs later

Other stuff:
  -T text       - text banner to show on the screen
  -M / -S id    - distributed mode (see parallel_fuzzing.txt)
  -I command    - execute this command/script when a new crash is found
  -B bitmap.txt - mutate a specific test case, use the out/fuzz_bitmap file
  -C            - crash exploration mode (the peruvian rabbit thing)
  -e ext        - File extension for the temporarily generated test case

For additional tips, please consult /usr/share/doc/afl++-doc/README

次に、ファジング用のサンプル・テストケースをダウンロードして、ファジングを実行します。

$ wget -O - https://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz | tar zxvf -
$ afl-fuzz -i afl-2.52b/testcases/others/text/ -o out/ ./example
afl-fuzz++2.59d based on afl by Michal Zalewski and a big online community
[+] afl++ is maintained by Marc "van Hauser" Heuse, Heiko "hexcoder" Eissfeldt and Andrea Fioraldi
[+] afl++ is open source, get it at https://github.com/vanhauser-thc/AFLplusplus
[+] Power schedules from github.com/mboehme/aflfast
[+] Python Mutator and llvm_mode whitelisting from github.com/choller/afl
[+] afl-tmin fork server patch from github.com/nccgroup/TriforceAFL
[+] MOpt Mutator from github.com/puppet-meteor/MOpt-AFL
[*] Getting to work...
[+] Using exploration-based constant power schedule (EXPLORE)
[+] You have 1 CPU core and 1 runnable tasks (utilization: 100%).
[*] Checking core_pattern...
[!] WARNING: Could not check CPU scaling governor
[*] Setting up output directories...
[+] Output directory exists but deemed OK to reuse.
[*] Deleting old session data...
[+] Output dir cleanup successful.
[*] Scanning 'afl-2.52b/testcases/others/text/'...
[+] No auto-generated dictionary tokens to reuse.
[*] Creating hard links for all input files...
[*] Validating target binary...
[*] Attempting dry run with 'id:000000,time:0,orig:hello_world.txt'...
[*] Spinning up the fork server...
[+] All right - fork server is up.
    len = 6, map size = 2, exec speed = 242 us
[+] All test cases processed.

[+] Here are some useful stats:

    Test case count : 1 favored, 0 variable, 1 total
       Bitmap range : 2 to 2 bits (average: 2.00 bits)
        Exec timing : 242 to 242 us (average: 242 us)

[*] No -t option specified, so I'll use exec timeout of 20 ms.
[+] All set and ready to roll!

              american fuzzy lop ++2.59d (example) [explore] {-1}
┌─ process timing ────────────────────────────────────┬─ overall results ────┐
│        run time : 0 days, 0 hrs, 4 min, 5 sec       │  cycles done : 4789  │
│   last new path : none yet (odd, check syntax!)     │  total paths : 1     │
│ last uniq crash : 0 days, 0 hrs, 4 min, 3 sec       │ uniq crashes : 1     │
│  last uniq hang : none seen yet                     │   uniq hangs : 0     │
├─ cycle progress ───────────────────┬─ map coverage ─┴──────────────────────┤
│  now processing : 0.4789 (0.0%)    │    map density : 0.00% / 0.00%        │
│ paths timed out : 0 (0.00%)        │ count coverage : 1.00 bits/tuple      │
├─ stage progress ───────────────────┼─ findings in depth ───────────────────┤
│  now trying : havoc                │ favored paths : 1 (100.00%)           │
│ stage execs : 255/256 (99.61%)     │  new edges on : 1 (100.00%)           │
│ total execs : 1.23M                │ total crashes : 31 (1 unique)         │
│  exec speed : 5054/sec             │  total tmouts : 0 (0 unique)          │
├─ fuzzing strategy yields ──────────┴───────────────┬─ path geometry ───────┤
│   bit flips : 0/32, 0/31, 0/29                     │    levels : 1         │
│  byte flips : 0/4, 0/3, 0/1                        │   pending : 0         │
│ arithmetics : 0/224, 0/0, 0/0                      │  pend fav : 0         │
│  known ints : 0/23, 0/84, 0/44                     │ own finds : 0         │
│  dictionary : 0/0, 0/0, 0/0                        │  imported : n/a       │
│   havoc/rad : 1/1.23M, 0/0, 0/0                    │ stability : 100.00%   │
│   py/custom : 0/0, 0/0                             ├───────────────────────┘
│        trim : 33.33%/1, 0.00%                      │             [cpu:199%]
└────────────────────────────────────────────────────┘^C

+++ Testing aborted by user +++
[+] We're done here. Have a nice day!

ファジング実行中は実行状況のサマリーがカラフルに表示されます。

afl-sample.png

結果の確認

ファジングの結果を確認します。

まず、出力ファイルを確認します。

$ ll out/
total 80
drwx------ 5 root root   200 Aug 30 15:30 ./
drwxrwxrwt 4 root root   140 Aug 30 15:29 ../
-rw------- 1 root root     2 Aug 30 15:34 .cur_input
-rw------- 1 root root    10 Aug 30 15:30 cmdline
drwx------ 2 root root    80 Aug 30 15:30 crashes/
-rw------- 1 root root 65536 Aug 30 15:30 fuzz_bitmap
-rw------- 1 root root   802 Aug 30 15:34 fuzzer_stats
drwx------ 2 root root    40 Aug 30 15:30 hangs/
-rw------- 1 root root  2835 Aug 30 15:34 plot_data
drwx------ 3 root root    80 Aug 30 15:30 queue/

$ ll out/crashes/
total 8
drwx------ 2 root root  80 Aug 30 15:30 ./
drwx------ 5 root root 200 Aug 30 15:30 ../
-rw------- 1 root root 586 Aug 30 15:30 README.txt
-rw------- 1 root root   6 Aug 30 15:30 id:000000,sig:11,src:000000,time:2487,op:havoc,rep:32

$ hexdump -C out/crashes/id\:000000\,sig\:11\,src\:000000\,time\:2487\,op\:havoc\,rep\:32 
00000000  40 c0 72 40 25 53                                 |@.r@%S|
00000006

out/フォルダに出力結果のファイルが格納されているようですが、バイナリファイルなどもあってよくわかりません。

afl-plotコマンドで出力結果を可視化してみます。

まず、afl-plotに必要なgnuplotをインストールします。

$ sudo apt install gnuplot -y

次に、コマンドを実行します。

$ afl-plot out/ graph/

graph/ファルダには下図のようなグラフの画像が格納されています。

afl-plot.png

1つ目のグラフは、時系列で実行パス数などをプロットしたグラフです。
プロットされる項目は以下の5つです。

  • total paths:トータルパス
  • current path:現在のパス
  • pending paths:ペンディングされているパス
  • pending favs:ペンディングされているfavs
  • cycles done:サイクル終了数

2つ目のグラフは、時系列で発見された不具合を種類別にプロットしたグラフです。
以下の3つの項目がプロットされます。

  • uniq crashes:一意なクラッシュ数
  • uniq hangs:一意なハング数
  • levels:レベル

3つ目のグラフは、時系列で実行速度が表示されるグラフです。
以下の1つの項目がプロットされます。

  • execs/sec:1秒あたりの実行数
7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?