Atheris で Python コードの Fuzzing

はじめに

ドローンのソフトウェア開発しています。イチノです。

Atheris を触ったのでメモ書きです。 Atheris は、プログラムの実行経路に応じたテスト用データを生成する Fuzzing ツールです。

github.com

Fuzzing とは、プログラムに問題の有りそうなデータやランダムなデータを渡して、プログラムのバグを探す脆弱性検査です。Fuzzing で WebRTC の脆弱性を発見したなんていう事例も有ります。

WebRTC Security, Fuzzing, and more! - YouTube

Fuzzing に関する詳しい説明は IPA の資料をご覧ください。

脆弱性対策:ファジング:IPA 独立行政法人 情報処理推進機構

Atheris のインストールと使い方を以下に記載しています。

インストール

Atheris は、 Linux, macOS に対応しています。

pip3 install atheris 

Atheris の準備

例として、bytes を UTF-8 の str へ変換する処理を Fuzzing します。 来たものを UTF-8 へ変換するコードをそのまま実装したものがこちら。 converter.py として保存します。

# converter.py
def bytes_to_utf_8(b):
    return b.decode('utf_8')

Fuzzing の検証コードは、こちらです。 Atheris から渡されたデータを converter.bytes_to_utf_8 へ渡す処理を TestConverter として実装しています。 fuzzing.py として保存します。

# fuzzing.py
import atheris
    import sys
  
# Atheris によるカバレッジを利かしたいモジュールを import
with atheris.instrument_imports():
    import converter
  
# Fuzzing 対象へデータを受け渡し
def TestConverter(data):
    return converter.bytes_to_utf_8(data)
 
atheris.Setup(sys.argv, TestConverter)
 
# Fuzzing の開始
atheris.Fuzz()

Atheris による Fuzzing の実行

fuzizng.py を実行すると、次のような結果が出ます。

$ python3 fuzzing.py
INFO: Instrumenting converter
INFO: Instrumenting base64
INFO: Instrumenting struct
INFO: Using built-in libfuzzer
WARNING: Failed to find function "__sanitizer_acquire_crash_state".
WARNING: Failed to find function "__sanitizer_print_stack_trace".
WARNING: Failed to find function "__sanitizer_set_death_callback".
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 692311049
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2 INITED cov: 1 ft: 1 corp: 1/1b exec/s: 0 rss: 35Mb
 
=== Uncaught Python exception: ===
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd1 in position 0: unexpected end of data
Traceback (most recent call last):
  File "fuzzing.py", line 10, in TestConverter
    return converter.bytes_to_utf_8(data)
  File "/home/user/converter.py", line 4, in bytes_to_utf_8
    return b.decode('utf_8')
 
==14145== ERROR: libFuzzer: fuzz target exited
SUMMARY: libFuzzer: fuzz target exited
MS: 4 ChangeByte-ChangeBit-CrossOver-ChangeByte-; base unit: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
0xd1,
\321
artifact_prefix='./'; Test unit written to ./crash-c314f8c1fa13e1afa11c8716c7bce6dbf02d09aa
Base64: 0Q==

こんな感じで、問題の有るデータを crash-*というファイルへ保存しておいてくれます。 見つかったデータに対して修正コードを書いてから、修正します。 例えば、 decode の例外発生時は空文字を返す修正を converter.py に施します。

# converter.py
def bytes_to_utf_8(b):
    try:
        return b.decode('utf_8')
    except UnicodeDecodeError:
        return ''

再度、python3 fuzzing.py を実行すると、エラーは発生しなくなります。

さいごに

Atheris を使って Fuzzing を実施してバグを引き起こすデータを発見できました。また、見つかったデータに対して有効な修正を施せたことを確認できました。

オプティムでは、ロボティクス事業や様々な事業で活躍していただくエンジニアを募集中です。ご応募お待ちしております。

www.optim.co.jp