RESTlerとは
RESTler is the first stateful REST API fuzzing tool for automatically testing cloud services through their REST APIs and finding security and reliability bugs in these services. For a given cloud service with an OpenAPI/Swagger specification, RESTler analyzes its entire specification, and then generates and executes tests that exercise the service through its REST API.
- Microsoft製のFuzzing toolで,状態を持つ処理シーケンスを扱えるのが特徴 (状態を扱える=API呼び出し→その結果を受けて次のAPI呼び出し,のような流れをテストできる)
- 一番大元の論文のメモは→RESTler: Stateful REST API Fuzzing - jagijagijag1 tiny tech notes
- OSSで公開されている→GitHub - microsoft/restler-fuzzer: RESTler is the first stateful REST API fuzzing tool for automatically testing cloud services through their REST APIs and finding security and reliability bugs in these services.
- FazzingについてはIPAのサイトに概要あり
- Windows/Linux 64-bitのみ対応なので,macOS上でDockerを使って動かしてみた例をメモ
環境
- macOS Catelina
- Docker Engine - Community
- Version: 20.10.2
実行環境コンテナ準備
.NET SDK入りDockerイメージ取得
- .NET core SDK 3.1が必要なので,下記のMicrosoft公式イメージを利用
- .NET SDK | Docker Hub
- 下記コマンドだとOSはDebian 10
- 他のOSにしたい場合はDokcer Hubに記載のタグを指定
1$ docker pull mcr.microsoft.com/dotnet/sdk:3.1
2$ docker images
3REPOSITORY TAG IMAGE ID CREATED SIZE
4mcr.microsoft.com/dotnet/sdk 3.1 52a3845cafb1 4 days ago 710MB
5...
コンテナ起動
- コンテナを立ち上げてbashに入る (お試しなのでDockerfile化せず直接作業)
1$ docker run -it -d --name restler-test mcr.microsoft.com/dotnet/sdk:3.1
2$ docker exec -it restler-test /bin/bash
Python 3.8.2インストール
- Python 3.8.2が必要なのでインストール (お試しなのでrootユーザのままで作業,以下コマンド例はroot実行だが先頭シンボルは$で記載)
- 3.7しかリポジトリにないようなので下記を参考にインストール
1$ apt update
2$ apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev libreadline-dev libffi-dev curl libbz2-dev
3$ cd ~
4$ curl -O https://www.python.org/ftp/python/3.8.2/Python-3.8.2.tar.xz
5$ tar -xf Python-3.8.2.tar.xz
6$ cd Python-3.8.2
7$ ./configure --enable-optimizations
8$ nproc
93
10$ make -j 3
11$ make install
12$ python3 --version
13Pyhon 3.8.2
python
コマンドの設定
- RESTlerの処理の中では,
python
コマンドで自身の持つスクリプトを実行する - ここまで設定した状態では,
python
コマンドがPython 2.7.x,python3
コマンドががPython 3.8.xをさしているはず - そのため,下記などにて
python
コマンドでPython 3.8.xが実行されるような設定が必要alias python="python3"
だとRESTler実行時に反映されないようでだめだった
1$ which python
2/usr/bin/python
3
4$ ls -la /usr/bin/python
5lrwxrwxrwx 1 root root 7 Mar 4 2019 /usr/bin/python -> python2
6
7$ which python3
8/usr/local/bin/python3
9
10$ ln -fs /usr/local/bin/python3 /usr/bin/python
11
12$ ls -la /usr/bin/python
13lrwxrwxrwx 1 root root 22 Jan 27 05:35 /usr/bin/python -> /usr/local/bin/python3
14
15$ python --version
16Python 3.8.2
RESTlerインストール
- READMEに従い下記を実行
1$ git clone https://github.com/microsoft/restler-fuzzer.git
2$ cd restler-fuzzer/
3$ python3 ./build-restler.py --dest_dir ~/restler_bin #RESTlerファイル一式をおく任意のpath
チュートリアル
- 下記に従って動かしてみる
- デモ対象はブログの記事管理API
<cloneしたrestler-fuzzerのpath>/demo_server/swagger.json
で定義されている- 例えば
GET /blog/posts
で記事一覧取得
0. デモサーバ準備
- まずFuzzing対象とするサーバを起動する必要あり
demo_server/README.md
を参照
- Python環境を構築してサーバ起動
1$ cd <cloneしたrestler-fuzzerのpath>/demo_server
2$ python -m venv venv
3$ source venv/bin/activate
4$ pip install -r requirements.txt
5$ python demo_server/app.py
1. Compile
- Swagger定義をRESTlerで解釈できる形式に変換する
1$ mkdir ~/restler-test
2$ cd $_
3$ cp <cloneしたrestler-fuzzerのpath>/demo_server/swagger.json .
4$ ~/restler_bin/restler/Restler compile --api_spec swagger.json
restler-fuzzer/demo_server/Compile
の下に結果が出力される- 例えば
grammar.json
に,RESTlerでSwagger定義を解釈した結果を保持 - 例えば
dict.json
はfuzzingで使うパラメタ定義
- 例えば
1{
2 "restler_fuzzable_string": [
3 "fuzzstring"
4 ],
5 ...
6 "restler_fuzzable_uuid4": [
7 "903bcc44-30cf-4ea7-968a-d9d0da7c072f"
8 ],
9 "restler_fuzzable_uuid4_unquoted": [],
10 "restler_fuzzable_int": [
11 "0",
12 "1"
13 ],
14 "restler_fuzzable_number": [
15 "0.1",
16 "1.2"
17 ],
18 "restler_fuzzable_bool": [
19 "true"
20 ],
21 ...
22}
2. Test
- 各エンドポイント・メソッドに対しアクセス可能か,レスポンス受け取れるかテスト
1$ ~/restler_bin/restler/Restler test --grammar_file Compile/grammar.py --dictionary_file Compile/dict.json --settings Compile/engine_settings.json --no_ssl
2Log share was not specified. Logs will not be uploaded.
3Starting task Test...
4Request coverage (successful / total): 5 / 6
5No bugs were found.
6Task Test succeeded.
7Collecting logs...
- 成功すると,5/6のリクエストが成功したことが出力される
- デモサーバを正常に起動できていないと全リクエストが失敗になる
- Tutorialページでは
GET /blog/posts?per_page=
で失敗するとあるが,自分の環境ではPUT /blog/posts/{post_id}
で失敗していた- ペイロードの
id
に文字列を入れてしまって404ぽい - 直し方は詳細調べないと不明
- ペイロードの
3. Fuzz-lean
- 各エンドポイント・メソッドに一度だけチェックをかける
- 説明には
a default set of checkers
とあるがなんのことか不明
- 説明には
1$ ~/restler_bin/restler/Restler fuzz-lean --grammar_file Compile/grammar.py --dictionary_file Compile/dict.json --settings Compile/engine_settings.json --no_ssl
2Log share was not specified. Logs will not be uploaded.
3Starting task FuzzLean...
4
5ERROR: Results analyzer for logs in /root/restler-test/FuzzLean failed.
6
7Request coverage (successful / total): 5 / 6
8Bugs were found!
9Bug buckets:
10
11InvalidDynamicObjectChecker_20x: 1
12InvalidDynamicObjectChecker_500: 1
13PayloadBodyChecker_500: 1
14Task FuzzLean succeeded.
15Collecting logs...
検出されたバグの情報は
FizzLean/RestlerResults/experiment<...>/bug_buckets
に出力bug_buckets.txt
にサマリInvalidDynamicObjectChecker_20x_1.txt
などに個別バグのもう少し詳細- このログのテキストファイルを入力にしてRESTlerを実行すれば,同じバグの再現が可能 (
--replay_log
オプション)
- このログのテキストファイルを入力にしてRESTlerを実行すれば,同じバグの再現が可能 (
- ここでは3つのバグ発見
以下,発見されたバグの解説
InvalidDynamicObjectChecker_20x
- まずブログ記事をPOST→レスポンスとして記事ID取得
- その後,当該IDを用いてブログ記事をGET,ただしFuzzerで余分なクエリ文字列付与
- 具体的には,
GET /api/blog/posts/<ID>
とするところを,GET /api/blog/posts/<ID>?inject_query_stirng=123
としてリクエスト発行
- 具体的には,
- エラー発生を期待してFuzzerで余計な情報を付け足したにもかかわらず,200系のレスポンスが返ってきてしまったのでバグと判定
InvalidDynamicObjectChecker_500
- まず
InvalidDynamicObjectChecker_20x
のケースと同じくPOST→余計なクエリ文字列つけてGET - その後余計なクエリ文字列のところを
?inject_query_stirng=123
から??
に変えてGET - すると,500 Internal Server Errorをレスポンスとして受け取ったので,バグと判定
PayloadBodyChecker_500
- まずブログ記事をPOST→レスポンスとして記事ID取得
- その後,当該IDのブログ記事を更新するためのPUTするが,Fuzzerによりbodyから
id
とchecksum
が削除された状態でリクエストを発行 - その結果,500 Internal Server Errorをレスポンスとして受け取ったので,バグと判定
4. Fuzz
- API呼び出しの組み合わせも含めてFuzzingする
--time_budget
は実行する時間(hour単位)の指定- 下記コマンドだと1時間実行される
1$ ~/restler_bin/restler/Restler fuzz --grammar_file Compile/grammar.py --dictionary_file Compile/dict.json --settings Compile/engine_settings.json --no_ssl --time_budget 1
- 今回の例は小規模で複雑度が低いため,
FuzzLean
と同じバグしか発見していない
その他
- ドキュメント見るかぎり維持れる項目とか機能がまだまだありそう
- 実際に使うにはこの辺読まないとダメそう
Tagged: #RESTler #REST API #API testing #Fuzzing