文档章节

利器|谷歌开源模糊测试工具 ClusterFuzz 应用尝鲜从 0 到 1

TesterHome
 TesterHome
发布于 02/27 22:07
字数 3550
阅读 204
收藏 0

本文发表于 TesterHome 社区,作者为资深测试开发工程师恒捷,原文标题为《谷歌开源模糊测试工具 ClusterFuzz 尝鲜记录 (进行中)》,原文链接:https://testerhome.com/topics/18171

背景

模糊测试,是指用随机坏数据(也称做 fuzz)攻击一个程序,然后等着观察哪里遭到了破坏。(出自 模糊测试)。一直以来都有不少的模糊测试工具,但大多只集中在数据生成,执行和异常检测依赖人工,未有比较完整的方案。

早在八年前,google 内部就在建设和使用模糊测试的工具来测试其内部的应用,而在两年前, google 推出了 OSS-Fuzz 服务,用于给开源项目的进行免费的模糊测试服务,可自动在新版本代码提交后自动完成 测试->异常检测->issue登记->老版本issue回归及自动关闭 的功能。背后使用的就是 ClusterFuzz 技术。流程图如下:

而在过年前,google 开源了 ClusterFuzz ,并解决了原有 ClusterFuzz 必须依赖 Google Cloud 提供的服务这个问题,提供了本地运行的解决方案。根据官方介绍,它具备如下功能:

  • 高度可扩展,谷歌的内部实例运行在超过 25000 台机器上

  • 准确的去副本化(Accurate deduplication)

  • 问题跟踪器的全自动错误归档和关闭

  • 最小化测试用例

  • 通过二分法回归查找

  • 提供分析 fuzzer 性能和崩溃率的统计信息(不支持本地部署)

  • 易于使用的 Web 界面,用于管理和查看崩溃

  • 支持引导模糊(例如 libFuzzer 和 AFL)和黑盒模糊测试

其大致执行流程如下:

当然,方案并不完美,如模糊数据统计、崩溃数据统计等功能由于依赖 google cloud 强大的数据处理能力,本地运行时是用不了的。

官方说的总是美好的,现实是否这么完美呢?曾有人说,实践是检验真理的唯一标准,为了更好地了解这个工具,当然就要本地跑个 demo 玩下啦。

本地搭建及运行

要获得 ClusterFuzz 的完整功能,需要连接 Google Cloud Platform。但结合国情,我们更期望了解它纯本地运行能做到什么,因此这次尝鲜主要尝试纯本地运行。

注意:虽然运行可以脱离 Google Cloud Platform ,但部分安装时用到的工具需要到 Google 站点下载,所以,你懂得。

以下步骤均是在 macOS 10.14 上进行。

环境搭建

1、下载源码

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

  2. cd clusterfuzz

2、安装 google cloud sdk

进入 https://cloud.google.com/sdk/ ,按照引导安装 sdk 并配置好环境变量(mac 下可以直接用解压后的 install.sh 脚本一键安装),确认命令行可调用 gcloud 命令

  1. $ gcloud -v

  2. Google Cloud SDK 226.0.0

  3. bq 2.0.38

  4. core 2018.11.16

  5. gsutil 4.34

3、安装 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

4、安装其他依赖

针对

  • 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/chenhengjie123/clusterfuzz

2.23 更新:docker 镜像已成功打包,基于 ubuntu 16.04 系统。镜像中已运行完毕本文中的第1-4步(除了坑2中的注释 chromedriver ),装好了所有依赖。镜像地址:https://hub.docker.com/r/chenhengjie123/clusterfuzz_local

可通过  docker run-it--name clusterfuzz-p9000:9000-p41089:41089-p9001:9001-p9002:9002chenhengjie123/clusterfuzz_local进入镜像运行环境,进入后续的步骤。clusterfuzz 的源代码存放在镜像的 /clusterfuzz 目录。

5、切换到 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

1、进入 Jobs 页面,点击 【ADD NEW JOB】按钮 2、job 的各个输入框填写以下内容:

输入框名称 内容
Name libfuzzerasanlinux_openssl
Platform LINUX
Templates libfuzzer engine_asan
Environment String CORPUS_PRUNE = True

3、把上一步打包的 openssl-fuzzer-build.zip 文件上传到 "Custom Build" 字段 4、点击 【ADD】 按钮,完成添加 5、点击【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)

谷歌 Cluster****Fuzz 尝鲜意犹未尽?

想掌握更多测试前沿技术神兵利器?

推荐关注 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 大会风采

© 著作权归作者所有

TesterHome
粉丝 0
博文 5
码字总数 10911
作品 0
QA/测试工程师
私信 提问
给Chrome“捉虫”16000个,Google开源bug自检工具

整理 | 一一 出品 | AI科技大本营(ID:rgznai100) 在内部开发和使用八年之久,近日,Google 宣布开源 bug 自动化检测工具 ClusterFuzz。 ClusterFuzz 是一款提供端到端的自动化模糊测试工具...

AI科技大本营
02/10
0
0
谷歌开源 ClusterFuzz,自动化查找并修复 bug

近日,谷歌开源了一个模糊测试基础设施——ClusterFuzz,可以非常简单地自动化查找并修复程序中的 bug。 模糊测试是一种用于自动化检测软件中存在的问题的方法,其通过向目标程序提供意外输入...

h4cd
02/09
4.8K
2
模糊测试基础设施 - ClusterFuzz

ClusterFuzz 是一个模糊测试基础设施,研发到现在已经有 8 年时间,其旨在无缝地融入开发人员工作流程,并使得查找 bug 并修复它们变得非常简单。 ClusterFuzz 提供端到端的自动化,从 bug ...

匿名
02/09
5.5K
3
谷歌发布 OSS-Fuzz 开源模糊测试服务

谷歌发布 OSS-Fuzz 开源模糊测试服务:专利还是开源好? 仅在微软宣布提供Springfield编码模糊测试服务两个月后,谷歌也发布了他们自己的面向对象存储模糊测试,OSS-Fuzz的测试版。这两个项目...

达尔文
2016/12/09
4.1K
1
Google 模糊测试服务--OSS-Fuzz

OSS-Fuzz OSS-Fuzz 能够针对开源软件进行持续的模糊测试,它的目的是利用更新的模糊测试技术与可拓展的分布式执行相结合,提高一般软件基础架构的安全性与稳定性。OSS-Fuzz 结合了多种模糊测...

匿名
2016/12/09
3.6K
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周一乱弹 —— 熟悉的味道,难道这就是恋爱的感觉

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @xiaoshiyue :好久没分享歌了分享张碧晨的单曲《今后我与自己流浪》 《今后我与自己流浪》- 张碧晨 手机党少年们想听歌,请使劲儿戳(这里)...

小小编辑
今天
146
8
SpringBoot中 集成 redisTemplate 对 Redis 的操作(二)

SpringBoot中 集成 redisTemplate 对 Redis 的操作(二) List 类型的操作 1、 向列表左侧添加数据 Long leftPush = redisTemplate.opsForList().leftPush("name", name); 2、 向列表右......

TcWong
今天
18
0
排序––快速排序(二)

根据排序––快速排序(一)的描述,现准备写一个快速排序的主体框架: 1、首先需要设置一个枢轴元素即setPivot(int i); 2、然后需要与枢轴元素进行比较即int comparePivot(int j); 3、最后...

FAT_mt
昨天
4
0
mysql概览

学习知识,首先要有一个总体的认识。以下为mysql概览 1-架构图 2-Detail csdn |简书 | 头条 | SegmentFault 思否 | 掘金 | 开源中国 |

程序员深夜写bug
昨天
11
0
golang微服务框架go-micro 入门笔记2.2 micro工具之微应用利器micro web

micro web micro 功能非常强大,本文将详细阐述micro web 命令行的功能 阅读本文前你可能需要进行如下知识储备 golang分布式微服务框架go-micro 入门笔记1:搭建go-micro环境, golang微服务框架...

非正式解决方案
昨天
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部