本文發表於 TesterHome 社區,做者爲資深測試開發工程師恆捷,原文標題爲《谷歌開源模糊測試工具 ClusterFuzz 嚐鮮記錄 (進行中)》,原文連接: https://testerhome.com/topics...
模糊測試,是指用隨機壞數據(也稱作 fuzz)攻擊一個程序,而後等着觀察哪裏遭到了破壞。(出自 模糊測試)。一直以來都有很多的模糊測試工具,但大多隻集中在數據生成,執行和異常檢測依賴人工,未有比較完整的方案。python
早在八年前,google 內部就在建設和使用模糊測試的工具來測試其內部的應用,而在兩年前, google 推出了 OSS-Fuzz 服務,用於給開源項目的進行免費的模糊測試服務,可自動在新版本代碼提交後自動完成 測試->異常檢測->issue登記->老版本issue迴歸及自動關閉 的功能。背後使用的就是 ClusterFuzz 技術。流程圖以下:linux
而在過年前,google 開源了 ClusterFuzz ,並解決了原有 ClusterFuzz 必須依賴 Google Cloud 提供的服務這個問題,提供了本地運行的解決方案。根據官方介紹,它具有以下功能:c++
其大體執行流程以下:git
固然,方案並不完美,如模糊數據統計、崩潰數據統計等功能因爲依賴 google cloud 強大的數據處理能力,本地運行時是用不了的。github
官方說的老是美好的,現實是否這麼完美呢?曾有人說,實踐是檢驗真理的惟一標準,爲了更好地瞭解這個工具,固然就要本地跑個 demo 玩下啦。golang
要得到 ClusterFuzz 的完整功能,須要鏈接 Google Cloud Platform。但結合國情,咱們更指望瞭解它純本地運行能作到什麼,所以此次嚐鮮主要嘗試純本地運行。web
注意:雖然運行能夠脫離 Google Cloud Platform ,但部分安裝時用到的工具須要到 Google 站點下載,因此,你懂得。chrome
如下步驟均是在 macOS 10.14 上進行。docker
git clone https://github.com/google/clusterfuzz
cd clusterfuzz
進入 https://cloud.google.com/sdk/ ,按照引導安裝 sdk 並配置好環境變量(mac 下能夠直接用解壓後的 install.sh
腳本一鍵安裝),確認命令行可調用 gcloud 命令bootstrap
$ gcloud -v
Google Cloud SDK 226.0.0
bq 2.0.38
core 2018.11.16
gsutil 4.34
特別注意:若是你使用的是 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
針對
幾個系統,官方已經內置了安裝依賴的腳本,直接運行便可:
local/install_deps.bash
執行完畢,會出現
Installation succeeded!
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 ,因此能夠直接註釋掉這個函數:
diff --git a/src/local/butler/common.py b/src/local/butler/common.py
index 94b17b3..3e9de99 100644
--- a/src/local/butler/common.py
+++ b/src/local/butler/common.py
@@ -275,7 +275,7 @@ def install_dependencies(platform_name=None):
_remove_invalid_files()
execute('bower install --allow-root')
- _install_chromedriver()
+ #_install_chromedriver()
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 ,因此保險起見所有都配好代理:
export HTTP_PROXY=http://112.126.81.122:6$(date +%m%d)
export HTTPS_PROXY=${HTTP_PROXY}
git config --global https.proxy ${HTTP_PROXY}
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 目錄。
$ source ENV/bin/activate
校驗是否一切就緒
$ python butler.py --help
python butler.py --help
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.
usage: butler.py [-h]
{bootstrap,py_unittest,go_unittest,js_unittest,format,lint,package,deploy,run_server,run,run_bot,remote,clean_indexes,generate_datastore_models,create_config}
...
本地實例包含2個部分,一個是管理各個執行機器人的服務端,另外一個是執行機器人。
首次運行,添加 --bootstrap
進行各個數據的初始化。同時我的推薦加上 --skip-install-deps
跳過依賴安裝(前面步驟已經裝過了,不須要重複安裝)
$ python butler.py run_server --bootstrap --skip-install-deps
非首次運行,務必去掉 --bootstrap
參數。
坑四:啓動時會到 https://www.googleapis.com/discovery/v1/apis/pubsub/v1/rest
獲取一些信息,若是此時網絡連不通,會報錯
報錯信息:
Created symlink: source: /clusterfuzz/local/storage/local_gcs, target /clusterfuzz/src/appengine/local_gcs.
Traceback (most recent call last):
File "butler.py", line 282, in <module>
main()
File "butler.py", line 256, in main
command.execute(args)
File "src/local/butler/run_server.py", line 162, in execute
test_utils.setup_pubsub(constants.TEST_APP_ID)
File "/clusterfuzz/src/python/tests/test_libs/test_utils.py", line 308, in setup_pubsub
_create_pubsub_topic(client, project, queue['name'])
File "/clusterfuzz/src/python/tests/test_libs/test_utils.py", line 284, in _create_pubsub_topic
if client.get_topic(full_name):
File "/clusterfuzz/src/python/google_cloud_utils/pubsub.py", line 192, in get_topic
request = self._api_client().projects().topics().get(topic=name)
File "/clusterfuzz/src/python/base/retry.py", line 88, in _wrapper
result = func(*args, **kwargs)
File "/clusterfuzz/src/python/google_cloud_utils/pubsub.py", line 89, in _api_client
discovery.DISCOVERY_URI.format(api='pubsub', apiVersion='v1'))
File "/clusterfuzz/src/third_party/httplib2/__init__.py", line 1694, in request
(response, content) = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey)
File "/clusterfuzz/src/third_party/httplib2/__init__.py", line 1434, in _request
(response, content) = self._conn_request(conn, request_uri, method, body, headers)
File "/clusterfuzz/src/third_party/httplib2/__init__.py", line 1390, in _conn_request
response = conn.getresponse()
File "/usr/lib/python2.7/httplib.py", line 1123, in getresponse
raise ResponseNotReady()
httplib.ResponseNotReady
解決猜測:看了下這個頁面,其實是獲取 api 文檔。理論上只要把這個 api 文檔事先下載好並放到資源文件中,而後把這個從網絡獲取文檔的步驟改成讀取資源文件便可。晚些嘗試下。
因爲時間關係,暫時先想辦法讓網絡能訪問 google 先繞過。
啓動到末尾,會出現以下日誌:
| INFO 2019-02-23 06:25:34,648 api_server.py:265] Starting gRPC API server at: http://localhost:39957
| INFO 2019-02-23 06:25:34,877 dispatcher.py:256] Starting module "default" running at: http://localhost:9000
| INFO 2019-02-23 06:25:35,021 dispatcher.py:256] Starting module "cron-service" running at: http://localhost:9001
| 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 ,即監聽全部地址,應該能夠解決。目前還在修改中。
後續部分翻譯自官方文檔,還沒親測,你們能夠先看看了解。
====== 官方文檔翻譯分割線 ======
官方命令:
python butler.py run_bot --name my-bot /path/to/my-bot
其中 my-bot 能夠替換爲本身喜歡的名稱。我改爲了 fuzzing-bot
$ python butler.py run_bot --name fuzzing-bot `cwd`/fuzzing-bot
執行成功後,可在前一步的管理員界面看到機器人狀態。
可經過
tail -f `cwd`/fuzzing-bot/bot.log
查看機器人實時日誌輸出。
官方給了一個例子,尋找 OpenSSL 的心臟滴血內存溢出漏洞。下面按照給出的步驟執行。
# 下載並解壓包含這個漏洞的 OpenSSL :
curl -O https://www.openssl.org/source/openssl-1.0.1f.tar.gz
tar xf openssl-1.0.1f.tar.gz
# 使用 AScan 和 fuzzer 插樁編譯 OpenSSL:
cd openssl-1.0.1f/
./config
# 注意:$CC 必須指向 clang 二進制文件。簡單地說,按照這個命令來寫就對了
make CC="$CC -g -fsanitize=address,fuzzer-no-link"
cd ..
# 下載 fuzz target 和它的數據依賴:
curl -O https://raw.githubusercontent.com/google/clusterfuzz/master/docs/setting-up-fuzzing/heartbleed/handshake-fuzzer.cc
curl -O https://raw.githubusercontent.com/google/clusterfuzz/master/docs/setting-up-fuzzing/heartbleed/server.key
curl -O https://raw.githubusercontent.com/google/clusterfuzz/master/docs/setting-up-fuzzing/heartbleed/server.pem
# 編譯可用於 ClusterFuzz 的 OpenSSL fuzz target ($CXX 須要指向一個 clang++ 二進制文件):
$CXX -g handshake-fuzzer.cc -fsanitize=address,fuzzer openssl-1.0.1f/libssl.a \
openssl-1.0.1f/libcrypto.a -std=c++17 -Iopenssl-1.0.1f/include/ -lstdc++fs \
-ldl -lstdc++ -o handshake-fuzzer
zip openssl-fuzzer-build.zip handshake-fuzzer server.key server.pem
一、進入 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 大會風采