首页
论坛
课程
招聘
[原创]go语言模糊测试与oss-fuzz
2022-5-27 00:02 5814

[原创]go语言模糊测试与oss-fuzz

2022-5-27 00:02
5814

本文介绍了如何使用OSS-fuzz对一些go项目进行模糊测试,oss-fuzz是谷歌提出的一款多引擎的模糊测试平台,该平台以docker为基础,能够实现多种语言的持续模糊测试。Google希望通过“模糊测试(fuzz testing,fuzzing)”为程序提供随机数据输入,作为开源开发的标准部分,oss-fuzz能够针对开源软件进行持续的模糊测试,其测试开发团队也提到“OSS-Fuzz的目的是利用更新的模糊测试技术与可拓展的分布式执行相结合,提高一般软件基础架构的安全性与稳定性。OSS-Fuzz结合了多种模糊测试技术/漏洞捕捉技术(即原来的libfuzzer)与清洗技术(即原来的AddressSanitizer),并且通过ClusterFuzz为大规模可分布式执行提供了测试环境,当然fuzzer也可以选择AFL,libfuzzer。在oss-fuzz中,go语言由于其运行环境,如网络问题的联通性等问题,相比于C与C++编写的项目,环境搭建较为复杂,本文以容器运行时项目containerd为例,演示如何使用oss-fuzz构建模糊测试句号并介绍了go语言模糊测试中的一个开源项目 go-fuzz-header。

OSS-fuzz

项目地址:https://github.com/google/oss-fuzz

 

先将oss-fuzz下载到本地,project文件夹下列出了集成到oss-fuzz中进行持续模糊测试的项目,目前有将近600个项目,如下图所示

 

1653549118895.png

 

其中每个项目下有3个主要的文件,分别为project.yaml ,Dockerfilebuild.sh

project.yaml

该文件记录了项目的基本信息,以containerd为例,该文件夹下的project.yaml如下:

1
2
3
4
5
6
7
8
9
10
homepage: "https://github.com/containerd/containerd"
main_repo: "https://github.com/containerd/containerd"
primary_contact: "security@containerd.io"
auto_ccs :
  - "adam@adalogics.com"
language: go
fuzzing_engines:
  - libfuzzer
sanitizers:
  - address
  • homepage: 项目地址
  • main_repo: 托管代码的源代码存储库的路径
  • laguange: 项目编写的编程语言
  • primary_contact, auto_css: 联系人列表
  • fuzzing_engines: 模糊测试所使用的引擎,如afl,libfuzzer
  • sanitizer: 消毒剂,支持ASAN 和MSAN ,可以有效的提高模糊测试发现crash的概率
  • architectures: 架构列表
  • ...

Dockerfile

Dockerfile 为项目定义了docker 镜像,build.sh也将在镜像中运行 ,containerdDockerfile如下,

1
2
3
4
5
6
FROM gcr.io/oss-fuzz-base/base-builder-go
RUN apt-get update && apt-get install -y btrfs-progs libc-dev pkg-config libseccomp-dev gcc wget libbtrfs-dev
RUN git clone --depth 1 https://github.com/containerd/containerd
RUN git clone --depth 1 https://github.com/cncf/cncf-fuzzing
COPY build.sh $SRC/
WORKDIR $SRC/containerd
  • FROM: 规定了项目的基本镜像
  • RUN:构建镜像时执行的命令,首先下载了一些必须的软件和库,之后下载了项目源码
  • COPY: 将build.sh复制到镜像中
  • WORKDIR :指定工作目录
  • ...

在Dockerfile中可以看到cncf-fuzzing的项目,该项目致力于将CNCF中的开源项目集成到OSS-fuzz中进行持续的模糊测试,如kubernetes、cri-o、runc等。

build.sh

构建脚本,用来编译项目的,生成的二进制文件应放在$OUT中,以示例,一般就是编译和复制语句,示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash -eu
 
./buildconf.sh
# configure scripts usually use correct environment variables.
./configure
 
make clean
make -j$(nproc) all
 
$CXX $CXXFLAGS -std=c++11 -Ilib/ \
    $SRC/parse_fuzzer.cc -o $OUT/parse_fuzzer \
    $LIB_FUZZING_ENGINE .libs/libexpat.a
 
cp $SRC/*.dict $SRC/*.options $OUT/

以下位置对应的环境变量

  • $OUT ->/out:用来存储构建好的文件

  • $SRC -> /src: 放源文件的位置

  • $WORK -> work: 存储中间文件的位置

更多的变量可以参考官方文档

环境准备

1.确保Docker安装成功以及主机能够访问外网。
2.克隆 oss-fuzz项目代码到本地。
3.设置Dockerd走代理以解决pull镜像时无法访问的问题。 oss中用到的镜像都需要从谷歌拉取,有两种解决方法,给docker挂个代理然后直接pull ; 或者利用github+dockerhub的方法将所有的镜像先拖到dockerhub上,然后将源码中所有的gcr.io/oss-fuzz-base/xxxx改成对应dockerhub上的就行 ;这里使用直接给docker走代理的方式,

  • 首先创建 /etc/systemd/system/docker.service.d/proxy.conf 配置文件,添加以下内容设置代理:

    1
    2
    3
    4
    [Service]
    Environment="HTTP_PROXY=http://127.0.0.1:7890"
    Environment="HTTPS_PROXY=https://127.0.0.1:7890"
    Environment="NO_PROXY=127.0.0.1"
  • 然后重新加载配置并重启服务:

    1
    2
    systemctl daemon-reload
    systemctl restart docker
  • 检查加载的配置是否生效

    1
    systemctl show docker --property Environment

4.修改containerd文件下的Dockerfile如下,首先是加了ENV 配置环境变量设置容器内部的网络代理,确定容器内部能够在git clone 或者 go install 等命令时不会报错,这里注意地址要填主机的ip,不能是127.0.0.1;其次修改containerd的分支为1.6版本,因为最新版本的containerd 的go mod 规定的是go语言的1.18版本, 目前的go环境基础容器暂时不支持 go 1.18

1
2
3
4
5
6
7
8
9
10
11
FROM gcr.io/oss-fuzz-base/base-builder-go
RUN apt-get update && apt-get install -y btrfs-progs libc-dev pkg-config libseccomp-dev gcc wget libbtrfs-dev
ENV HTTP_PROXY "http://192.168.xx.xx:7890"
ENV HTTPS_PROXY "http://192.168.xx.xx:7890"
RUN git clone https://github.com/containerd/containerd
WORKDIR containerd
RUN git checkout -b remotes/origin/release/1.6 remotes/origin/release/1.6
WORKDIR $SRC
RUN git clone --depth 1 https://github.com/cncf/cncf-fuzzing
COPY build.sh $SRC/
WORKDIR $SRC/containerd

构建Harness进行模糊测试

1.构建fuzz的基本镜像

1
2
cd /path/to/oss-fuzz
python infra/helper.py build_image containerd

第一次构建需要下载很多基础镜像,如果在pull 镜像时出现gcr.io的网络连接问题,则需要检查代理是否生效。

 

成功构建镜像后,查看镜像列表,应该如下图所示,包括oss-fuzz提供的几个基础环境的镜像,和红框内的构建的containerd的镜像。
1653531736652.png

 

2.构建fuzz目标

1
python infra/helper.py build_fuzzers containerd

构建完成后,在/path/to/oss-fuzz/build/out/containerd 文件夹会生成对应编译好的harness二进制文件,如 fuzz_applyfuzz_archive_exportfuzz_parse_auth 等,每一个harness对应的一个fuzz目标。

 

1653532295721.png

 

harness的构建源码可以从containerd项目中找到,每一个fuzz函数都对应一个harness。下图为containerd中的构建脚本,其中 compile_go_fuzzer 对应的编译引擎为go-fuzz,在containerd中使用的是在go-fuzz基础上改进的go-fuzz-header。compile_native_go_fuzzer 对应的是 go 1.18 中的原生模糊测试。
1653533251377.png
在CNCF的很多go语言项目的模糊测试中,都用到了go-fuzz-header,前面的文章中已经介绍了go-fuzzgo native fuzz,相比于go-fuzz,go原生模糊测试引擎除了标准字节数组外,还可以为 Harness 提供如int,bool等多种类型 , 但一些项目可能需要更复杂的类型,如结构、映射和切片 ,go-fuzz-header就是为了解决对复杂类型的结构体进行模糊测试的挑战而出现的。以 containerd 中的 FuzzParseAuth 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package fuzz
 
import (
    fuzz "github.com/AdaLogics/go-fuzz-headers"
    runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
 
    "github.com/containerd/containerd/pkg/cri/server"
)
 
func FuzzParseAuth(data []byte) int {
    f := fuzz.NewConsumer(data)
    auth := &runtime.AuthConfig{}
    err := f.GenerateStruct(auth)
    if err != nil {
        return 0
    }
    host, err := f.GetString()
    if err != nil {
        return 0
    }
    _, _, _ = server.ParseAuth(auth, host)
    return 1
}

go-fuzz-header首先使用模糊引擎提供的随机字节 data 创建一个新的Consumer , 之后f调用GenerateStruct方法根据模糊测试引擎提供的随机数据来填充auth结构体进行测试。

 

3.开始fuzz,选择一个或者多个fuzz 对象开始进行模糊测试,以 fuzz_image_store为例,其中 --corpus-dir 参数可以指定种子目录,不加该参数默认以空语料库进行fuzz。

1
python infra/helper.py run_fuzzer  --corpus-dir=./build/out/containerd/corpus containerd fuzz_image_store

在oss-fuzz中,对于go语言模糊测试默认使用 go-fuzz 来编译harness,之后使用libfuzzer作为引擎进行fuzz,fuzz开始后会在out文件夹下生成一个文件夹,用来存放相关输出。

 

1653570805928

 

经过漫长的等待之后可能会发生崩溃,

 

1653565726388.png

 

崩溃信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
runtime: unexpected return pc for runtime.gopark called from 0x0
stack: frame={sp:0x10c000078f40, fp:0x10c000078f60} stack=[0x10c000078000,0x10c000079000)
0x000010c000078e400x0000000000000000  0x0000000000000000
0x000010c000078e500x0000000000000000  0x0000000000000000
0x000010c000078e600x7a75662f706d742f  0x3833303039332d7a
0x000010c000078e700x39332d7a7a75662f  0x3430383538333030
0x000010c000078e800xdef0995b8d5812aa  0x758f15f0dcd67525
0x000010c000078e900xee5d5b00aa1475d6  0x1d3fd1a2d44b0579
0x000010c000078ea00x0000000000000000  0x0000000000000000
0x000010c000078eb00x0000006901000000  0x0000000000000000
0x000010c000078ec00x0000000000000000  0x0000000000000000
0x000010c000078ed00x0000000000070000  0x0000000000000000
0x000010c000078ee00xffffffffffffffff  0x00ffffffffffffff
0x000010c000078ef00x000010c0001ddb80  0x000010c0005a3600
0x000010c000078f000x000010c0004651e0  0x000010c000465340
0x000010c000078f100x000010c0001dc420  0x000010c0003920e0
0x000010c000078f200x000010c0001948d0  0x000010c0001906b0
0x000010c000078f300x000010c000582f90  0x000010c000582fd0
0x000010c000078f40: <0x000010c0005837d0  0x000010c000190590
0x000010c000078f500x0000000000000000 !0x0000000000000000
0x000010c000078f60: >0x000093f73283d9b8  0x000010c0001282c0
0x000010c000078f700x0000000000001418  0x0000000000000000
0x000010c000078f800x0000000000000000  0x0000000000000000
0x000010c000078f900x00000a8c46505853  0x0000000000000207
0x000010c000078fa00x0000000000000a88  0x0000000000000000
0x000010c000078fb00x0000000000000000  0x0000000000000000
0x000010c000078fc00x0000000000000203  0x0000000000000000
0x000010c000078fd00x0000000000000000  0x0000000000000000
0x000010c000078fe00x0000000000000000  0x0000000000000000
0x000010c000078ff00x0000000000000000  0x0000000000000000
fatal error: unknown caller pc
 
runtime stack:
runtime.throw({0x1f3f3fb, 0x328e0e0})
    runtime/panic.go:1198 +0x71
runtime.gentraceback(0x7f220a620c90, 0x1, 0x0, 0x7f220a620b30, 0x0, 0x0, 0x7fffffff, 0x7f220a620c90, 0x0, 0x0)
    runtime/traceback.go:274 +0x1956
runtime.scanstack(0x10c000001ba0, 0x10c000051698)
    runtime/mgcmark.go:748 +0x197
runtime.markroot.func1()
    runtime/mgcmark.go:232 +0xb1
runtime.markroot(0x10c000051698, 0x1f)
    runtime/mgcmark.go:205 +0x170
runtime.gcDrain(0x10c000051698, 0x3)
    runtime/mgcmark.go:1013 +0x379
runtime.gcBgMarkWorker.func2()
    runtime/mgc.go:1269 +0xa5
runtime.systemstack()
    runtime/asm_amd64.s:383 +0x46
 
goroutine 6 [GC worker (idle)]:
runtime.systemstack_switch()
    runtime/asm_amd64.s:350 fp=0x10c00006af60 sp=0x10c00006af58 pc=0x5c4a20
runtime.gcBgMarkWorker()
    runtime/mgc.go:1256 +0x1b3 fp=0x10c00006afe0 sp=0x10c00006af60 pc=0x5790b3
runtime.goexit()
    runtime/asm_amd64.s:1581 +0x1 fp=0x10c00006afe8 sp=0x10c00006afe0 pc=0x5c6cc1
created by runtime.gcBgMarkStartWorkers
    runtime/mgc.go:1124 +0x25
 
goroutine 17 [runnable, locked to thread]:
runtime.goexit()
    runtime/asm_amd64.s:1581 +0x1
 
goroutine 7 [chan receive]:
k8s.io/klog/v2.(*loggingT).flushDaemon(0x0)
    k8s.io/klog/v2@v2.30.0/klog.go:1181 +0x8b
created by k8s.io/klog/v2.init.0
    k8s.io/klog/v2@v2.30.0/klog.go:420 +0x115
AddressSanitizer:DEADLYSIGNAL
=================================================================
==12==ERROR: AddressSanitizer: ABRT on unknown address 0x00000000000c (pc 0x0000005c85e1 bp 0x7f220a620678 sp 0x7f220a620660 T8)
SCARINESS: 10 (signal)
    #0 0x5c85e1 in runtime.raise.abi0 runtime/sys_linux_amd64.s:165
    #1 0x5aa097 in runtime.crash runtime/signal_unix.go:861
    #2 0x593c70 in runtime.fatalthrow.func1 runtime/panic.go:1257
    #3 0x593bef in runtime.fatalthrow runtime/panic.go:1250
    #4 0x5939b0 in runtime.throw runtime/panic.go:1198
    #5 0x5b9975 in runtime.gentraceback runtime/traceback.go:274
    #6 0x57b856 in runtime.scanstack runtime/mgcmark.go:748
    #7 0x57a790 in runtime.markroot.func1 runtime/mgcmark.go:232
    #8 0x57a54f in runtime.markroot runtime/mgcmark.go:205
    #9 0x57c3b8 in runtime.gcDrain runtime/mgcmark.go:1013
    #10 0x579404 in runtime.gcBgMarkWorker.func2 runtime/mgc.go:1269
    #11 0x5c4a85 in runtime.systemstack.abi0 runtime/asm_amd64.s:383
 
DEDUP_TOKEN: runtime.raise.abi0--runtime.crash--runtime.fatalthrow.func1
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: ABRT runtime/sys_linux_amd64.s:165 in runtime.raise.abi0
Thread T8 created by T3 here:
    #0 0x50d32c in __interceptor_pthread_create /src/llvm-project/compiler-rt/lib/asan/asan_interceptors.cpp:207:3
    #1 0x55d070 in _cgo_try_pthread_create /_/runtime/cgo/gcc_libinit.c:100:9
    #2 0x599d86 in runtime.newm runtime/proc.go:2230
    #3 0x59a46e in runtime.startm runtime/proc.go:2485
    #4 0x59a999 in runtime.wakep runtime/proc.go:2584
    #5 0x59c164 in runtime.resetspinning runtime/proc.go:3216
    #6 0x59c71d in runtime.schedule runtime/proc.go:3374
    #7 0x59cc4c in runtime.park_m runtime/proc.go:3516
    #8 0x5c4a04 in runtime.mcall runtime/asm_amd64.s:307
 
DEDUP_TOKEN: __interceptor_pthread_create--_cgo_try_pthread_create--runtime.newm
Thread T3 created by T1 here:
    #0 0x50d32c in __interceptor_pthread_create /src/llvm-project/compiler-rt/lib/asan/asan_interceptors.cpp:207:3
    #1 0x55d070 in _cgo_try_pthread_create /_/runtime/cgo/gcc_libinit.c:100:9
    #2 0x599d86 in runtime.newm runtime/proc.go:2230
    #3 0x59a46e in runtime.startm runtime/proc.go:2485
    #4 0x59a999 in runtime.wakep runtime/proc.go:2584
    #5 0x59e897 in runtime.newproc.func1 runtime/proc.go:4261
    #6 0x5c4a85 in runtime.systemstack.abi0 runtime/asm_amd64.s:383
 
DEDUP_TOKEN: __interceptor_pthread_create--_cgo_try_pthread_create--runtime.newm
Thread T1 created by T0 here:
    #0 0x50d32c in __interceptor_pthread_create /src/llvm-project/compiler-rt/lib/asan/asan_interceptors.cpp:207:3
    #1 0x55cfbf in _cgo_try_pthread_create /_/runtime/cgo/gcc_libinit.c:100:9
    #2 0x55cfbf in x_cgo_sys_thread_create /_/runtime/cgo/gcc_libinit.c:27:12
    #3 0x1f0cb0c in __libc_csu_init (/out/fuzz_image_store+0x1f0cb0c)
 
DEDUP_TOKEN: __interceptor_pthread_create--_cgo_try_pthread_create--x_cgo_sys_thread_create
==12==ABORTING
MS: 2 EraseBytes-ChangeBinInt-; base unit: feb33bf726c50d41c5dc2c8cea890cb18040c1f8
0x10,0xd,0xb,0x3b,0x2,0x0,0x0,0x0,0x0,0x0,0x84,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x3,0xfa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
\020\015\013;\002\000\000\000\000\000\204\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\010\000\000\000\000\000\003\372\000\000\000\000\000\000\000\000\000\000\000\000
artifact_prefix='./'; Test unit written to ./crash-66c182f8f6dac7209a14e631d117b0879331cbfe
Base64: EA0LOwIAAAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAD+gAAAAAAAAAAAAAAAA==

参考链接

  • go-fuzz-header:

    https://github.com/AdaLogics/go-fuzz-headers

    https://adalogics.com/blog/structure-aware-go-fuzzing-complex-types

  • oss-fuzz教程

    https://n0va-scy.github.io/2022/02/14/oss-fuzz%E5%88%9D%E6%8E%A2/

    https://github.com/google/oss-fuzz/blob/master/docs/getting-started/new_project_guide.md

  • containerd

    https://github.com/containerd/containerd/blob/11de19af68c7d21c8fe01058026257ecd5d6ed13/contrib/fuzz/oss_fuzz_build.sh


看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~

最后于 2022-5-27 00:10 被ZxyNull编辑 ,原因:
收藏
点赞1
打赏
分享
打赏 + 50.00雪花
打赏次数 1 雪花 + 50.00
 
赞赏  Editor   +50.00 2022/06/17 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (4)
雪    币: 211
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
Ricardio609 活跃值 2022-5-31 21:41
2
0
您好,请问国内不能访问grr.io,请问您是怎么解决的?
雪    币: 1028
活跃值: 活跃值 (863)
能力值: ( LV5,RANK:68 )
在线值:
发帖
回帖
粉丝
ZxyNull 活跃值 2022-6-1 16:43
3
0
mb_mztaapcy 您好,请问国内不能访问grr.io,请问您是怎么解决的?
文中有说,需要设置docker 在pull 镜像时候的代理
雪    币: 211
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
Ricardio609 活跃值 2022-6-5 19:00
4
0
ZxyNull 文中有说,需要设置docker 在pull 镜像时候的代理
好的,谢谢师傅
雪    币: 211
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
Ricardio609 活跃值 2022-6-10 18:14
5
0
师傅,您在本地部署过fuzzbench吗?
游客
登录 | 注册 方可回帖
返回