linux 内核 fuzz,fuzz技术和漏洞挖掘

声明:谢绝一切形式的转载。

在计算机的世界中,有输入的地方就有江湖,因为有输入的地方,就有可能有漏洞。比如xss,目前很多大型网站依然存在xss漏洞。

一个简单的程序

下面的程序是求一个数的平方。

#include

intmain(){

inta=0;

scanf("%d",&a);

printf("%d\n",a*a);

return0;

}

编译 gcc test.c 运行 ./a.out

linux 内核 fuzz,fuzz技术和漏洞挖掘_第1张图片通过观察,很容易看出当输入一些"畸形"数据的时候会导致程序运行出现错误。如果只有二进制文件,有什么办法可以检测出这种错误?

Fuzz

A software testing technique, often automated or semi-automated, that involves passing invalid, unexpected or random input to a program and monitor result for crashes, failed assertions, races, leaks, etc.

术语

● Target

Consumes an array of bytes

Calls the code we want to test

● Fuzzer

A tool that feed the target with different random inputs

● Corpus

A set of valid & invalid inputs for the target

Collected manually, by fuzzing, or by crawling

fuzz的种类

Generation Based :通过对目标协议或文件格式建模的方法,从零开始产生测试用例,没有先前的状态

Mutation Based :基于一些规则,从已有的数据样本或存在的状态变异

Evolutionary :包含了上述两种,同时会根据代码覆盖率的回馈进行变异。

传统fuzz

传统的 fuzz 大多通过对已有的样本 按照预先设置好的规则 进行变异产生测试用例,然后喂给 目标程序同时监控目标程序的运行状态。这类 fuzz 有很多,比如: peach , FileFuzz 等。

实战

相关文件位于:

生成测试用例

radamsa 是一个 测试用例生成引擎,它是通过对已有的样本进行变异来生成新的测试用例。下面的代码主要通过调用 radamsa ,然后随机选取 seed_corpus 目录中的文件名作为参数,传递给 radamsa 进行变异,把生成的测试用例,放到 work/corpus。

#!/usr/bin/env python2

# generate_testcases.py

importos

importrandom

WORK_DIR='work'

# Create work `directory` and `corpus` subdirectory.

ifnotos.path.exists(WORK_DIR):

os.mkdir(WORK_DIR)

corpus_dir=os.path.join(WORK_DIR,'corpus')

ifnotos.path.exists(corpus_dir):

os.mkdir(corpus_dir)

seed_corpus_filenames=os.listdir('seed_corpus')

foriinxrange(1000):

random_seed_filename=random.choice(seed_corpus_filenames)

random_seed_filename=os.path.join('seed_corpus',random_seed_filename)

output_filename=os.path.join(WORK_DIR,'corpus','testcase-%06d'%i)

cmd='bin/radamsa "%s" > "%s"'%(random_seed_filename,output_filename)

os.popen(cmd)

开始fuzz

fuzz程序如下,target是vscode,Corpus是上面程序生成的测试用例。

#!/usr/bin/env python2

# run_fuzzing.py

importos

importsubprocess

WORK_DIR='work'

defcheckOutput(s):

if'Segmentation fault'insor'error'ins.lower():

returnFalse

else:

returnTrue

corpus_dir=os.path.join(WORK_DIR,'corpus')

corpus_filenames=os.listdir(corpus_dir)

forfincorpus_filenames:

testcase_path=os.path.join(corpus_dir,f)

cmd=['/usr/bin/code',testcase_path]

process=subprocess.Popen(cmd,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)

output=process.communicate()[0]

ifnotcheckOutput(output):

printtestcase_path

printoutput

print'-'*80

由于用于变异样本的选取 和 样本的变异方式是随机的,可能需要重复多次 样本生成 && fuzz 才能找到 crash。写个 bash 脚本,不断重复即可

#!/bin/bash

while["0"-lt"1"]

do

rm-rf./work/

./generate_testcases.py

./run_fuzzing.py

done

libFuzzer

feature

In-process, in-memory

Guided fuzz testing

Very effective at a function / protocol level

1000x faster

It’s easy to write a libFuzzer-based fuzzer

Can be checked along with unit-tests

Memory Tools

● AddressSanitizer (aka ASan)

Detects use-after-free, buffer overflows (heap, stack, globals), stack-use-after-return, container-overflow

Cpu: 2x, memory 1.5x-3x

● MemorySanitizer (aka MSan)

Detects uninitialized memory reads

Cpu: 3x, memory: 2x

Special mode: origins

● UndefinedBehaviorSanitizer (aka UBSan)

Detects several classes of bugs (19?), esp on type confusion, signed-integer-overflow, undefined shift, etc.

Cpu: 10-50%

Memory: ~1x (no allocator, no shadow)

Helloworld-For-libFuzzer

本节资源位于:。

target

boolVulnerableFunction1(constuint8_t*data,size_tsize){

boolresult=false;

if(size>=3){

result=data[0]=='F'&&

data[1]=='U'&&

data[2]=='Z'&&

data[3]=='Z';

}

returnresult;

}

在上面的程序中,当size=3的时候,访问data[3]会产生越界。

fuzzer

first_fuzzer.cc

extern"C"intLLVMFuzzerTestOneInput(constuint8_t*data,size_tsize){

VulnerableFunction1(data,size);

return0;

}

编译: clang++-g-fsanitize=address,fuzzer first_fuzzer.cc 运行fuzzer: ./a.out2>&1|grep ERROR

linux 内核 fuzz,fuzz技术和漏洞挖掘_第2张图片完整的输出

INFO:Seed:100908111

INFO:Loaded1modules(35inline8-bit counters):35[0x7f8120,0x7f8143),

INFO:Loaded1PC tables(35PCs):35[0x5b7f68,0x5b8198),

INFO:-max_len is not provided;libFuzzer will not generate inputs larger than4096bytes

INFO:A corpus is not provided,starting from an empty corpus

#2 INITED cov: 3 ft: 3 corp: 1/1b exec/s: 0 rss: 27Mb

#15 NEW cov: 4 ft: 4 corp: 2/4b lim: 4 exec/s: 0 rss: 27Mb L: 3/3 MS: 3 CopyPart-CrossOver-InsertByte-

#1156 NEW cov: 5 ft: 5 corp: 3/7b lim: 14 exec/s: 0 rss: 27Mb L: 3/3 MS: 1 CMP- DE: "F\x00"-

#2688 NEW cov: 6 ft: 6 corp: 4/15b lim: 29 exec/s: 0 rss: 27Mb L: 8/8 MS: 2 ChangeByte-InsertRepeatedBytes-

#2704 REDUCE cov: 6 ft: 6 corp: 4/13b lim: 29 exec/s: 0 rss: 28Mb L: 6/6 MS: 1 EraseBytes-

#2844 REDUCE cov: 6 ft: 6 corp: 4/12b lim: 29 exec/s: 0 rss: 28Mb L: 5/5 MS: 5 CrossOver-PersAutoDict-CrossOver-EraseBytes-EraseBytes- DE: "F\x00"-

#3080 REDUCE cov: 6 ft: 6 corp: 4/11b lim: 29 exec/s: 0 rss: 28Mb L: 4/4 MS: 1 EraseBytes-

#3172 REDUCE cov: 6 ft: 6 corp: 4/10b lim: 29 exec/s: 0 rss: 28Mb L: 3/3 MS: 2 CopyPart-EraseBytes-

#10828 REDUCE cov: 7 ft: 7 corp: 5/70b lim: 104 exec/s: 0 rss: 28Mb L: 60/60 MS: 1 InsertRepeatedBytes-

#10840 REDUCE cov: 7 ft: 7 corp: 5/29b lim: 104 exec/s: 0 rss: 28Mb L: 19/19 MS: 2 PersAutoDict-CrossOver- DE: "F\x00"-

#10933 REDUCE cov: 7 ft: 7 corp: 5/24b lim: 104 exec/s: 0 rss: 28Mb L: 14/14 MS: 3 ChangeByte-InsertByte-EraseBytes-

#11125 REDUCE cov: 7 ft: 7 corp: 5/21b lim: 104 exec/s: 0 rss: 28Mb L: 11/11 MS: 2 InsertByte-EraseBytes-

#11361 REDUCE cov: 7 ft: 7 corp: 5/18b lim: 104 exec/s: 0 rss: 28Mb L: 8/8 MS: 1 EraseBytes-

#11482 REDUCE cov: 7 ft: 7 corp: 5/14b lim: 104 exec/s: 0 rss: 28Mb L: 4/4 MS: 1 EraseBytes-

=================================================================

==3357==ERROR:AddressSanitizer:heap-buffer-overflow on address0x602000040f73at pc0x00000059b461bp0x7fff657ee560sp0x7fff657ee558

READ of size1at0x602000040f73thread T0

#0 0x59b460 in VulnerableFunction1(unsigned char const*, unsigned long) /home/burning/linux/libfuzzer-workshop-master/lessons/04/./vulnerable_functions.h:22:14

#1 0x59bde4 in LLVMFuzzerTestOneInput /home/burning/linux/libfuzzer-workshop-master/lessons/04/first_fuzzer.cc:10:3

#2 0x466186 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:556

#3 0x46b7e9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:470

#4 0x46b7e9 in fuzzer::Fuzzer::MutateAndTestOne() /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:699

#5 0x46e80f in fuzzer::Fuzzer::Loop(std::Fuzzer::vector>&) /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:830

#6 0x456b99 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:824

#7 0x41f522 in main /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerMain.cpp:19

#8 0x7fa043c3eb96 in __libc_start_main /build/glibc-2ORdQG/glibc-2.27/csu/../csu/libc-start.c:310

#9 0x41f599 in _start (/home/burning/linux/libfuzzer-workshop-master/lessons/04/a.out+0x41f599)

0x602000040f73is located0bytes to the right of3-byte region[0x602000040f70,0x602000040f73)

allocated by thread T0 here:

#0 0x597b58 in operator new[](unsigned long) /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/asan/asan_new_delete.cpp:102

#1 0x466092 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:541

#2 0x46b7e9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:470

#3 0x46b7e9 in fuzzer::Fuzzer::MutateAndTestOne() /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:699

#4 0x46e80f in fuzzer::Fuzzer::Loop(std::Fuzzer::vector>&) /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:830

#5 0x456b99 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:824

#6 0x41f522 in main /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerMain.cpp:19

#7 0x7fa043c3eb96 in __libc_start_main /build/glibc-2ORdQG/glibc-2.27/csu/../csu/libc-start.c:310

SUMMARY:AddressSanitizer:heap-buffer-overflow/home/burning/linux/libfuzzer-workshop-master/lessons/04/./vulnerable_functions.h:22:14inVulnerableFunction1(unsignedcharconst*,unsignedlong)

Shadowbytes around the buggy address:

0x0c0480000190:fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa

0x0c04800001a0:fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa

0x0c04800001b0:fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa

0x0c04800001c0:fa fa fd fd fa fa fd fd fa fa fd fa fa fa fd fa

0x0c04800001d0:fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa

=>0x0c04800001e0:fa fa fd fa fa fa fd fa fa fa fd fa fa fa[03]fa

0x0c04800001f0:fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa

0x0c0480000200:fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa

0x0c0480000210:fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa

0x0c0480000220:fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa

0x0c0480000230:fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa

Shadowbyte legend(one shadow byte represents8application bytes):

Addressable:00

Partiallyaddressable:01020304050607

Heapleft redzone:fa

Freedheap region:fd

Stackleft redzone:f1

Stackmid redzone:f2

Stackright redzone:f3

Stackafterreturn:f5

Stackuse after scope:f8

Globalredzone:f9

Globalinit order:f6

Poisonedby user:f7

Containeroverflow:fc

Arraycookie:ac

Intraobject redzone:bb

ASaninternal:fe

Leftalloca redzone:ca

Rightalloca redzone:cb

Shadowgap:cc

==3357==ABORTING

MS:5CopyPart-EraseBytes-PersAutoDict-EraseBytes-ChangeBinInt-DE:"F\x00"-;base unit:9dbab5c181e3d878fa7e1229929a99dc1f04abf4

0x46,0x55,0x5a,

FUZ

artifact_prefix='./';Testunit written to./crash-0eb8e4ed029b774d80f2b66408203801cb982a60

正常的话应该可以看到类似上面的输出,这里对其中的一些信息解析一下

Seed: 1608565063 说明这次的种子数据

max_len 用于设置最大的数据长度

接下来 # 开头的行是 fuzz 过程中找到的路径信息

最后一行是触发漏洞的测试用例

重现crash:

ASAN_OPTIONS=symbolize=1./a.out./crash-0eb8e4ed029b774d80f2b66408203801cb982a60

# ASAN_OPTIONS=symbolize=1 用于显示 栈的符号信息

写在最后

文章中涉及到很多英文内容,个人不能翻译到信、达、雅的地步,所以就保留原文了。

公众号

更多内容,欢迎关注我的微信公众号:无情剑客。

你可能感兴趣的:(linux,内核,fuzz)