oss-fuzz没什么用笔记

0x01 什么是oss-fuzz

谷歌的oss-fuzz能够针对开源软件进行持续的模糊测试,其测试开发团队也提到“OSS-Fuzz的目的是利用更新的模糊测试技术与可拓展的分布式执行相结合,提高一般软件基础架构的安全性与稳定性。OSS-Fuzz结合了多种模糊测试技术/漏洞捕捉技术(即原来的libfuzzer)与清洗技术(即原来的AddressSanitizer),并且通过ClusterFuzz为大规模可分布式执行提供了测试环境。”,当然fuzzer也可以选择AFL,libfuzzer

先下载源码

0x02 三个主要的文件

project.yaml

  • homepage:You project’s homepage.(没啥用)

  • laguange:项目编写的编程语言,一般是c或c++

  • primary_contact, auto_css:联系人(也没啥用)

  • sanitizer: 可以使用去更有效的查找 memory address bugs

  • architectures: 架构

其它没啥用的就不一一介绍了

写个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
homepage: "https://www.baidu.com"
language: c
primary_contact: "kurt@roeckx.be"
auto_ccs:
- "openssl-security@openssl.org"
- "appronym@gmail.com"
- "caswell.matt@googlemail.com"
- "richard@levitte.org"
sanitizers:
- address
- memory
- undefined
architectures:
- x86_64
- i386

Dockerfile

这个很重要,为项目定义了docker 镜像,build.sh也将在镜像中运行,长这样

1
2
3
4
5
6
7
FROM gcr.io/oss-fuzz-base/base-builder       # base image with clang toolchain,这个是谷歌上的镜像,把可先把它拖到dockerhub上然后替换掉,也可以换成我已经拖好的,后面会给出
MAINTAINER YOUR_EMAIL # maintainer for this file
RUN apt-get update && apt-get install -y ... # install required packages to build your project
RUN go get ... # install dependencies to build your Go project
RUN git clone <git_url> <checkout_dir> # checkout all sources needed to build your project,github上自己的项目
WORKDIR <checkout_dir> # current directory for the build script
COPY build.sh fuzzer.cc $SRC/ # copy build script and other fuzzer files in src dir

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 : 存储中间文件的位置

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

0x3 拉取镜像

oss中用到的镜像都需要从谷歌拉取,有两种解决方法,给docker挂个代理然后直接pull;或者利用github+dockerhub的方法将所有的镜像先拖到dockerhub上,然后将源码中所有的gcr.io/oss-fuzz-base/xxxx改成对应dockerhub上的就行。教程(如果你壕无人性可以直接买个国外服务器,就不会有这些烦恼了)

也可以用我拉好的:

1
2
3
4
5
6
7
gcr.io/oss-fuzz-base/base-image         -->  r4bbit/base-image
gcr.io/oss-fuzz-base/base-clang --> r4bbit/base-clang
gcr.io/oss-fuzz-base/base-builder --> r4bbit/oss-fuzz (这个名字就不要去介意了)
gcr.io/oss-fuzz-base/base-runner --> r4bbit/base-runner
gcr.io/oss-fuzz-base/base-runner-debug --> r4bbit/base-runner-debug
gcr.io/oss-fuzz-base/base-msan-builder --> r4bbit/base-msan-builder
gcr.io/oss-fuzz-base/msan-builder --> r4bbit/masn-builder

0x4 起oss中自带的project

我们可以看到在project中有很多项目,这些都已经写好了build.sh、Dockerfile等必要文件

1590472567605

举个例子:openssl

1
2
# ls projects/openssl/
bignum.options build.sh Dockerfile project.yaml

build.sh文件不用修改,需要将Dockerfile文件中的FROM gcr.io/oss-fuzz-base/base-builder修改为FROM r4bbit/oss-fuzz:latest

并且,infra/help.py 中BASE_IMAGES修改为对应的镜像地址

之后 根据文档中的test locally步骤将fuzz起起来

1590473150008

举个例子,我这里用的是engine 选 afl,sanitizer 选undefined

1
2
# python infra/helper.py build_image openssl
# python infra/helper.py build_fuzzers openssl --sanitizer undefined --engine afl

build_fuzzers后可以在/path/to/oss/build/out中看到多了一个openssl文件夹,openssl而里面就有编译好的二进制文件

1590473858387

1
2
# python infra/helper.py check_build openssl --sanitizer undefined --engine afl
# python infra/helper.py run_fuzzer openssl --sanitizer undefined --engine afl server <fuzzer_target:选一个自己想fuzz的文件>

check build pass后就可以开始fuzz了,(其实个觉得好像也没必要check,貌似也只是跑了一句docker),会在build/out/openssl下面生成新的文件夹server_afl_undefined_out,里面的东西就跟AFL一样了

1590475013616

0x5 fuzz自己写的程序

如果想fuzz自己的程序就需要写build.sh和Dockerfile了,project.yaml倒不是很重要,模板上面已经有了,这里我直接举自己的例子。

先用c写一个漏洞程序 test.c

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
int vuln(char *str)
{
int len = strlen(str);
if(str[0] == 'A' && len == 66)
{
raise(SIGSEGV);
//如果输入的字符串的首字符为A并且长度为66,则异常退出
}
else if(str[0] == 'F' && len == 6)
{
raise(SIGSEGV);
//如果输入的字符串的首字符为F并且长度为6,则异常退出
}
else
{
printf("it is good!\n");
}
return 0;
}
int main(int argc, char *argv[])
{
char buf[100]={0};
gets(buf);//存在栈溢出漏洞
printf(buf);//存在格式化字符串漏洞
vuln(buf);
return 0;
}

然后上传到github或者gitee,接下来写Dockerfile,直接套就行了

1
2
3
4
5
6
FROM r4bbit/oss-fuzz:latest
MAINTAINER dvyukov@googlt.com
RUN git clone --depth 1 https://gitee.com/R4bb1t-n0va/test.git

WORKDIR test
COPY build.sh afl* $SRC/

build.sh,这里说一下我踩过的坑

1
2
3
4
5
6
7
# build project
gcc /src/test/test.c -o /src/test/test
# build fuzzers
$CXX $CXXFLAGS -std=c++11 -Iinclude \
/src/test/test.c -o $OUT/test

cp $SRC/test/* $OUT/

用gcc进行编译(肯定是我脑子糊了),在run_fuzzer时会报错:原因是没有插桩

1
2
3
4
5
6
7
8
9
10
11
12
[-] Looks like the target binary is not instrumented! The fuzzer depends on
compile-time instrumentation to isolate interesting test cases while
mutating the input data. For more information, and for tips on how to
instrument binaries, please see docs/README.

When source code is not available, you may be able to leverage QEMU
mode support. Consult the README for tips on how to enable this.
(It is also possible to use afl-fuzz as a traditional, "dumb" fuzzer.
For that, you can use the -n option - but expect much worse results.)

[-] PROGRAM ABORT : No instrumentation detected
Location : check_binary(), afl-fuzz.c:6959

所以需要换afl-gcc进行编译,在build_fuzzers步骤已经将afl-gcc等文件复制到/out/目录下了,所以将编译命令改成$OUT/afl-gcc /src/test/test.c -o /src/test/test,成功调用了afl-gcc进行编译,但是开始fuzz时又发现,还是没有插桩

对比一下正常情况下afl-gcc的编译情况可以发现

1590476034384

1590476078122

build.sh中没有调用afl-as,而afl-as的作用就是插桩,正常来讲指定AFL_PATH就能解决了,然而这里并没有解决,最后结合AFL源码,我用了一个方法:在build中建立 afl-as 软链接到 as

1
2
3
4
5
afl-gcc.c 中 find_as,让它在这里自己找到as_path
if (!access(AFL_PATH "/as", X_OK)) {
as_path = AFL_PATH;
return;
}

问题解决,最后成功的build.sh脚本

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
#!/bin/bash -eu
# Copyright 2020 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################

# build project
#gcc /src/test/test.c -o /src/test/test
#export AFL_PATH=$OUT/
#echo $AFL_PATH
#pwd
#which afl-gcc
#which afl-as
ln -s $OUT/afl-as $OUT/as
$OUT/afl-gcc /src/test/test.c -o /src/test/test
# build fuzzers
$CXX $CXXFLAGS -std=c++11 -Iinclude \
/src/test/test.c -o $OUT/test

cp $SRC/test/* $OUT/
0%