利器|谷歌開源模糊測試工具 ClusterFuzz 應用嚐鮮從 0 到 1

本文發表於 TesterHome 社區,做者爲資深測試開發工程師恆捷,原文標題爲《谷歌開源模糊測試工具 ClusterFuzz 嚐鮮記錄 (進行中)》,原文連接: https://testerhome.com/topics...

背景

模糊測試,是指用隨機壞數據(也稱作 fuzz)攻擊一個程序,而後等着觀察哪裏遭到了破壞。(出自 模糊測試)。一直以來都有很多的模糊測試工具,但大多隻集中在數據生成,執行和異常檢測依賴人工,未有比較完整的方案。python

早在八年前,google 內部就在建設和使用模糊測試的工具來測試其內部的應用,而在兩年前, google 推出了 OSS-Fuzz 服務,用於給開源項目的進行免費的模糊測試服務,可自動在新版本代碼提交後自動完成 測試->異常檢測->issue登記->老版本issue迴歸及自動關閉 的功能。背後使用的就是 ClusterFuzz 技術。流程圖以下:linux

而在過年前,google 開源了 ClusterFuzz ,並解決了原有 ClusterFuzz 必須依賴 Google Cloud 提供的服務這個問題,提供了本地運行的解決方案。根據官方介紹,它具有以下功能:c++

  • 高度可擴展,谷歌的內部實例運行在超過 25000 臺機器上
  • 準確的去副本化(Accurate deduplication)
  • 問題跟蹤器的全自動錯誤歸檔和關閉
  • 最小化測試用例
  • 經過二分法迴歸查找
  • 提供分析 fuzzer 性能和崩潰率的統計信息(不支持本地部署)
  • 易於使用的 Web 界面,用於管理和查看崩潰
  • 支持引導模糊(例如 libFuzzer 和 AFL)和黑盒模糊測試

其大體執行流程以下:git

固然,方案並不完美,如模糊數據統計、崩潰數據統計等功能因爲依賴 google cloud 強大的數據處理能力,本地運行時是用不了的。github

官方說的老是美好的,現實是否這麼完美呢?曾有人說,實踐是檢驗真理的惟一標準,爲了更好地瞭解這個工具,固然就要本地跑個 demo 玩下啦。golang

本地搭建及運行

要得到 ClusterFuzz 的完整功能,須要鏈接 Google Cloud Platform。但結合國情,咱們更指望瞭解它純本地運行能作到什麼,所以此次嚐鮮主要嘗試純本地運行。web

注意:雖然運行能夠脫離 Google Cloud Platform ,但部分安裝時用到的工具須要到 Google 站點下載,因此,你懂得。chrome

如下步驟均是在 macOS 10.14 上進行。docker

環境搭建

一、下載源碼

  1. git clone https://github.com/google/clusterfuzz
  2. cd clusterfuzz

二、安裝 google cloud sdk

進入 https://cloud.google.com/sdk/ ,按照引導安裝 sdk 並配置好環境變量(mac 下能夠直接用解壓後的 install.sh 腳本一鍵安裝),確認命令行可調用 gcloud 命令bootstrap

  1. $ gcloud -v
  2. Google Cloud SDK 226.0.0
  3. bq 2.0.38
  4. core 2018.11.16
  5. gsutil 4.34

三、安裝 python 和 go 運行環境。

特別注意:若是你使用的是 macOS 或者 Ubuntu、Debain,直接執行第4步便可,腳本里會自動安裝 Python 和 go

python 要求 2.7.10 以上,但不能是 python 3。在 mac 上能夠直接運行 brew install python@2 安裝。

go 未要求版本,在 mac 上能夠直接運行 brew install go 安裝。我用的是 go1.11.5 darwin/amd64

四、安裝其餘依賴

針對

  • Ubuntu (14.04, 16.04, 17.10, 18.04, 18.10)
  • Debian 8 (jessie) or later
  • Recent versions of macOS with homebrew (experimental)

幾個系統,官方已經內置了安裝依賴的腳本,直接運行便可:

  1. local/install_deps.bash

執行完畢,會出現

  1. Installation succeeded!
  2. Please load virtualenv environment by running 'source ENV/bin/activate'.

的提示。

坑一,官方的腳本里第一行用了 -ex 參數,會致使運行腳本時若是有命令執行出錯(如 brew install 時有些應用本地已經安裝過,但非最新版本),直接退出程序。

能夠經過 sed-i'''s/bash -ex/bash -x/'local/install_deps* 命令直接去掉 -e 參數。已經給官方提了 issue 。

坑二,官方腳本里使用 python butler.py bootstrap 初始化環境時,會自動去 google 站點下載 chromedriver 相關的文件。

全局搜索了下源代碼,只有跑單測的時候有用到 chromedriver ,因此能夠直接註釋掉這個函數:

  1. diff --git a/src/local/butler/common.py b/src/local/butler/common.py
  2. index 94b17b3..3e9de99 100644
  3. --- a/src/local/butler/common.py
  4. +++ b/src/local/butler/common.py
  5. @@ -275,7 +275,7 @@ def install_dependencies(platform_name=None):
  6. _remove_invalid_files()
  7. execute('bower install --allow-root')
  8. - _install_chromedriver()
  9. + #_install_chromedriver()
  10. def symlink(src, target):

坑三,運行時會報錯 

Analysisof target'//local:create_gopath'failed;build aborted:nosuchpackage'@org_golang_google_api//iterator':failed to fetch org_golang_google_api:2019/02/1901:15:41unrecognizedimportpath"google.golang.org/api"

這是在運行 bazel 構建 go 環境的時候報錯了,緣由是 @org_golang_x_tools、@com_google_cloud_go、@org_golang_google_api 這幾個第三方依賴網絡緣由獲取不到。

嘗試一:使用代理

由於 go 獲取依賴有可能用 http ,也有可能用 git ,因此保險起見所有都配好代理:

  1. export HTTP_PROXY=http://112.126.81.122:6$(date +%m%d)
  2. export HTTPS_PROXY=${HTTP_PROXY}
  3. git config --global https.proxy ${HTTP_PROXY}
  4. git config --global http.proxy ${HTTP_PROXY}

惋惜的是,配置完了仍是不行,bazel 構建時提示 

fatal:unable to access'https://code.googlesource.com/google-api-go-client/':LibreSSLSSL_connect:SSL_ERROR_SYSCALLinconnection to code.googlesource.com:443,此路不通。

嘗試二:修改運行環境,改成在網絡自己就沒問題的地方運行

嗯,哪裏有這樣的環境呢?一個是本身買雲主機,另外一個就是考慮用 docker hub 提供的構建環境了。看了下後面的使用步驟,也沒有須要在源碼目錄作操做的部分,就選擇 docker 吧。

動手 fork 了官方倉庫,開始了漫長的嘗試:https://github.com/chenhengji...

2.23 更新:docker 鏡像已成功打包,基於 ubuntu 16.04 系統。鏡像中已運行完畢本文中的第1-4步(除了坑2中的註釋 chromedriver ),裝好了全部依賴。鏡像地址:https://hub.docker.com/r/chen...

可經過  docker run-it--name clusterfuzz-p9000:9000-p41089:41089-p9001:9001-p9002:9002chenhengjie123/clusterfuzz_local 進入鏡像運行環境,進入後續的步驟。clusterfuzz 的源代碼存放在鏡像的 /clusterfuzz 目錄。

五、切換到 python 的 virtualenv

  1. $ source ENV/bin/activate

校驗是否一切就緒

  1. $ python butler.py --help
  2. python butler.py --help
  3. DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7.
  4. usage: butler.py [-h]
  5. {bootstrap,py_unittest,go_unittest,js_unittest,format,lint,package,deploy,run_server,run,run_bot,remote,clean_indexes,generate_datastore_models,create_config}
  6. ...

運行本地實例

本地實例包含2個部分,一個是管理各個執行機器人的服務端,另外一個是執行機器人。

啓動本地服務

首次運行,添加 --bootstrap 進行各個數據的初始化。同時我的推薦加上 --skip-install-deps 跳過依賴安裝(前面步驟已經裝過了,不須要重複安裝)

  1. $ python butler.py run_server --bootstrap --skip-install-deps

非首次運行,務必去掉 --bootstrap 參數。

坑四:啓動時會到 https://www.googleapis.com/discovery/v1/apis/pubsub/v1/rest 獲取一些信息,若是此時網絡連不通,會報錯

報錯信息:

  1. Created symlink: source: /clusterfuzz/local/storage/local_gcs, target /clusterfuzz/src/appengine/local_gcs.
  2. Traceback (most recent call last):
  3. File "butler.py", line 282, in <module>
  4. main()
  5. File "butler.py", line 256, in main
  6. command.execute(args)
  7. File "src/local/butler/run_server.py", line 162, in execute
  8. test_utils.setup_pubsub(constants.TEST_APP_ID)
  9. File "/clusterfuzz/src/python/tests/test_libs/test_utils.py", line 308, in setup_pubsub
  10. _create_pubsub_topic(client, project, queue['name'])
  11. File "/clusterfuzz/src/python/tests/test_libs/test_utils.py", line 284, in _create_pubsub_topic
  12. if client.get_topic(full_name):
  13. File "/clusterfuzz/src/python/google_cloud_utils/pubsub.py", line 192, in get_topic
  14. request = self._api_client().projects().topics().get(topic=name)
  15. File "/clusterfuzz/src/python/base/retry.py", line 88, in _wrapper
  16. result = func(*args, **kwargs)
  17. File "/clusterfuzz/src/python/google_cloud_utils/pubsub.py", line 89, in _api_client
  18. discovery.DISCOVERY_URI.format(api='pubsub', apiVersion='v1'))
  19. File "/clusterfuzz/src/third_party/httplib2/__init__.py", line 1694, in request
  20. (response, content) = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey)
  21. File "/clusterfuzz/src/third_party/httplib2/__init__.py", line 1434, in _request
  22. (response, content) = self._conn_request(conn, request_uri, method, body, headers)
  23. File "/clusterfuzz/src/third_party/httplib2/__init__.py", line 1390, in _conn_request
  24. response = conn.getresponse()
  25. File "/usr/lib/python2.7/httplib.py", line 1123, in getresponse
  26. raise ResponseNotReady()
  27. httplib.ResponseNotReady

解決猜測:看了下這個頁面,其實是獲取 api 文檔。理論上只要把這個 api 文檔事先下載好並放到資源文件中,而後把這個從網絡獲取文檔的步驟改成讀取資源文件便可。晚些嘗試下。

因爲時間關係,暫時先想辦法讓網絡能訪問 google 先繞過。

啓動到末尾,會出現以下日誌:

  1. | INFO 2019-02-23 06:25:34,648 api_server.py:265] Starting gRPC API server at: http://localhost:39957
  2. | INFO 2019-02-23 06:25:34,877 dispatcher.py:256] Starting module "default" running at: http://localhost:9000
  3. | INFO 2019-02-23 06:25:35,021 dispatcher.py:256] Starting module "cron-service" running at: http://localhost:9001
  4. | INFO 2019-02-23 06:25:35,023 admin_server.py:150] Starting admin server at: http://localhost:9002

代表已經啓動完畢。能夠經過打開 http://localhost:9002/ 打開管理員界面。

坑五:內部監聽地址都是 localhost ,意味着在 docker 容器內部時,即便用 -p 暴露了端口也訪問不了

解決猜測:把源碼中 localhost 都替換爲 0.0.0.0 ,即監聽全部地址,應該能夠解決。目前還在修改中。

後續部分翻譯自官方文檔,還沒親測,你們能夠先看看了解。 

====== 官方文檔翻譯分割線 ======

啓動執行機器人

官方命令:

  1. python butler.py run_bot --name my-bot /path/to/my-bot

其中 my-bot 能夠替換爲本身喜歡的名稱。我改爲了 fuzzing-bot

  1. $ python butler.py run_bot --name fuzzing-bot `cwd`/fuzzing-bot

執行成功後,可在前一步的管理員界面看到機器人狀態。

可經過

  1. tail -f `cwd`/fuzzing-bot/bot.log

查看機器人實時日誌輸出。

開始測試

官方給了一個例子,尋找 OpenSSL 的心臟滴血內存溢出漏洞。下面按照給出的步驟執行。

編譯一個包含這個漏洞和已經帶有 fuzz 插樁的 OpenSSL

  1. # 下載並解壓包含這個漏洞的 OpenSSL :
  2. curl -O https://www.openssl.org/source/openssl-1.0.1f.tar.gz
  3. tar xf openssl-1.0.1f.tar.gz
  4. # 使用 AScan 和 fuzzer 插樁編譯 OpenSSL:
  5. cd openssl-1.0.1f/
  6. ./config
  7. # 注意:$CC 必須指向 clang 二進制文件。簡單地說,按照這個命令來寫就對了
  8. make CC="$CC -g -fsanitize=address,fuzzer-no-link"
  9. cd ..
  10. # 下載 fuzz target 和它的數據依賴:
  11. curl -O https://raw.githubusercontent.com/google/clusterfuzz/master/docs/setting-up-fuzzing/heartbleed/handshake-fuzzer.cc
  12. curl -O https://raw.githubusercontent.com/google/clusterfuzz/master/docs/setting-up-fuzzing/heartbleed/server.key
  13. curl -O https://raw.githubusercontent.com/google/clusterfuzz/master/docs/setting-up-fuzzing/heartbleed/server.pem
  14. # 編譯可用於 ClusterFuzz 的 OpenSSL fuzz target ($CXX 須要指向一個 clang++ 二進制文件):
  15. $CXX -g handshake-fuzzer.cc -fsanitize=address,fuzzer openssl-1.0.1f/libssl.a \
  16. openssl-1.0.1f/libcrypto.a -std=c++17 -Iopenssl-1.0.1f/include/ -lstdc++fs \
  17. -ldl -lstdc++ -o handshake-fuzzer
  18. zip openssl-fuzzer-build.zip handshake-fuzzer server.key server.pem

上傳 fuzzer 到 ClusterFuzz

一、進入 Jobs 頁面,點擊 【ADD NEW JOB】按鈕 二、job 的各個輸入框填寫如下內容:

輸入框名稱 內容
Name libfuzzerasanlinux_openssl
Platform LINUX
Templates libfuzzer engine_asan
Environment String CORPUS_PRUNE = True

三、把上一步打包的 openssl-fuzzer-build.zip 文件上傳到 "Custom Build" 字段 四、點擊 【ADD】 按鈕,完成添加 五、點擊【Select/modify jobs】,勾選 "libfuzzerasanlinux_openssl" ,而後點擊【SUBMIT】 按鈕

執行及查看結果

經過查看本地的機器人執行日誌,能夠發現 fuzz libFuzzer libfuzzer_asan_linux_openssl 這個字符串,表明目前 fuzz 測試已經在進行中了。

稍等一會,會在日誌中發現一個堆棧信息和 AddressSanitizer:heap-buffer-overflow 出如今日誌中。

再稍等一會,能夠在 <> 頁面看到一個標題爲 "Heap-buffer-overflow READ{*}" 的測試用例,這個就是 ClusterFuzz 發現的心臟滴血漏洞了。

擴展性

從官方文檔上看,上面的例子只是用到了引導式 fuzz ,ClusterFuzz 還支持可任意擴展的黑盒 fuzz ,可支持使用 Python 編寫 fuzz 生成器。這次因爲時間關係未能嘗試,有興趣的同窗能夠嘗試一下。

同時官方的 local 文件夾中有看到 docker 運行相關的腳本,相信將來會支持經過 docker 運行,下降環境配置成本。

侷限性

從官方文檔中能夠看到,被測試的軟件須要在編譯時插入一些樁用於檢測異常,而這個方案目前僅支持 C/C++ ,且主要用於內存地址檢測。而對於咱們平時接觸到的 Java/python/go 應用,沒有提供對應的方案,須要另行擴展。

小結及展望

ClusterFuzz 正如其名,一個集羣運行的 Fuzz 工具。它提供了執行機器人管理以及一個很是簡便的管理界面,也作到了和研發流程無縫的接入,甚至更進一步地作到了 bug 自動建立及修復檢測。

從小的地方看,它讓模糊測試經過集羣得到了更高的執行效率和問題發現效率。

從大的地方看,它提供的總體流程,包含了自動報 bug 和檢測 bug 修復狀況,讓你們只在須要的時候感知到它的存在,正是目前大部分 CI 實踐中欠缺的最後一千米路,缺陷的自動上報與修復檢測,值得咱們思考補全咱們的 CI 流程。

雖然目前並未提供除 C/C++ 以外的完整解決方案,但相信按照其擴展性,擴展到支持更多的語言並非難題。指望將來有更多的同窗參與擴展這個工具,造成開箱即用的解決方案。(end)

谷歌 ClusterFuzz 嚐鮮意猶未盡?

想掌握更多測試前沿技術神兵利器?

推薦關注 MTSC2019 互聯網測試開發大會

聚焦測試前沿技術趨勢與創新發展

分享質量管理案例與最佳實戰經驗

MTSC 中國移動互聯網測試開發大會(Mobile Testing Summit China)是由 TesterHome 社區主辦的軟件測試行業年度技術盛會。自 2015 年創辦以來,MTSC 已成功舉辦 4 屆。MTSC2019 第五屆大會將於 2019 年 6 月 28~29 日在北京國際會議中心舉行。大會官網:https://testerhome.com/mtsc/2019

推薦演講嘉賓特別福利

目前,MTSC2019 面向軟件測試行業全球公開徵集議題,期待各位資深測試技術專家和質量管理經理貢獻 Topic 或者推薦演講嘉賓。推薦成功者,免費贈送一張 MTSC2019 大會門票。:)

議題信息可發送郵件至: topic@testerhome.com

往屆 MTSC 大會風采

相關文章
相關標籤/搜索