什么是LibFuzzer

是模糊测试的一种,动态跟踪程序执行路径,获取高价值输入,每一轮随机选取种子进行操作,若果可以出发新的代码路径,则被保留加入语料库,否则抛弃,但是这个是采用库连接集成到目标中,可以这对特定函数进行测试,而不是整个黑盒,可控性比较好

配置

安装必要clang++-12

1
2
3
4
5
6
7
8
sudo apt update && sudo apt install clang-12 lldb-12
#验证
~/libFUZZER
❯ clang++-12 --version
Ubuntu clang version 12.0.1-19ubuntu3
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

编译选项

1
2
3
4
5
clang++-12 -fsanitize=fuzzer,address,undefined \
-fno-omit-frame-pointer \
-g -O1 target.cpp -o fuzzer
#简洁版
clang++-12 -fsanitize=fuzzer,adress -g target.cpp -o fuzzer

这里借用下ai的解释

1
2
3
4
5
6
7
8
9
10
你的代码

-fsanitize=fuzzer → 变成自动测试工具
-fsanitize=address → 监控内存访问,检测溢出
-fsanitize=undefined→ 监控未定义行为
-fno-omit-frame-pointer → 崩溃时显示完整调用栈
-g → 显示源代码行号
-O1 → 速度优化

强大的漏洞发现工具!

编写Fuzzer

初探

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

~/libFUZZER

❯ cat fuzzer.cpp
#include <stdint.h>
#include <stddef.h>

#首先是写一个函数,就是这个vulnParser()
bool vulnParser(const uint8_t* data, size_t size) {
bool result = false;
if (size >= 3) {
result = data[0] == 'F' &&
data[1] == 'U' &&
data[2] == 'Z';
}
return result;
}

#然后在下面的这个函数中不断调用这个函数,LibFuzzer会不断调用他,date->输入数据的指针,size->输入数据的大小
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
vulnParser(data, size);
return 0;
}

~/libFUZZER

❯ ls
fuzzer.cpp

编译运行

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
~/libFUZZER 

❯ clang++-12 -fsanitize=fuzzer,address -g fuzzer.cpp -o fuzzer

~/libFUZZER

❯ ls
fuzzer fuzzer.cpp

~/libFUZZER
#可以看到编译的文件是x86架构的64位可执行文件,有个动态链接库
❯ file fuzzer
fuzzer: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b37da6dabc8fbcda367b623d7314e479f9c98cd0, for GNU/Linux 3.2.0, with debug_info, not stripped

~/libFUZZER
#运行成功的话会出现一下代码
#cov指的是路径条数,每次出现一条新路径或者测试案例字节减少都会显示
❯ ./fuzzer
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2561694265
INFO: Loaded 1 modules (6 inline 8-bit counters): 6 [0x546ec0, 0x546ec6),
INFO: Loaded 1 PC tables (6 PCs): 6 [0x51ecc0,0x51ed20),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2 INITED cov: 3 ft: 4 corp: 1/1b exec/s: 0 rss: 29Mb
#70 NEW cov: 4 ft: 5 corp: 2/4b lim: 4 exec/s: 0 rss: 29Mb L: 3/3 MS: 3 CopyPart-InsertByte-CopyPart-
#827 NEW cov: 5 ft: 6 corp: 3/8b lim: 11 exec/s: 0 rss: 30Mb L: 4/4 MS: 2 InsertByte-CMP- DE: "F\x00"-
#931 REDUCE cov: 5 ft: 6 corp: 3/7b lim: 11 exec/s: 0 rss: 30Mb L: 3/3 MS: 4 EraseBytes-InsertByte-PersAutoDict-EraseBytes- DE: "F\x00"-
#10307 REDUCE cov: 6 ft: 7 corp: 4/11b lim: 98 exec/s: 0 rss: 30Mb L: 4/4 MS: 1 InsertByte-
#10793 REDUCE cov: 6 ft: 7 corp: 4/10b lim: 98 exec/s: 0 rss: 30Mb L: 3/3 MS: 1 EraseBytes-
#8388608 pulse cov: 6 ft: 7 corp: 4/10b lim: 4096 exec/s: 2796202 rss: 609Mb
^C==22629== libFuzzer: run interrupted; exiting

~/libFUZZER took 3s



栈溢出案例检测

这里直接借用给的代码

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
// fuzzer_demo.cpp
#include <cstdint>
#include <cstring>
#include <cstdlib>

// 这是一个故意写错的函数,包含典型的缓冲区溢出漏洞
bool ProcessInput(const char* input, size_t len) {
char buffer[32]; // 固定大小的缓冲区

// 危险操作1:没有长度检查的strcpy
if (len >= 4 && memcmp(input, "FUZZ", 4) == 0) {
strcpy(buffer, input); // 这里会溢出!
return true;
}

// 危险操作2:基于用户输入的memcpy
if (len > 10 && input[len-1] == '!') {
memcpy(buffer, input, len); // len可能大于32
return true;
}

return false;
}

// libFuzzer的测试入口
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// 输入验证:太小或太大的输入没意义
if (size == 0 || size > 1000) {
return 0;
}

// 确保字符串以null结尾(安全做法)
char* null_terminated = (char*)malloc(size + 1);
memcpy(null_terminated, data, size);
null_terminated[size] = '\0';

// 调用有漏洞的函数
ProcessInput(null_terminated, size);

free(null_terminated);
return 0;
}
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

~/libFUZZER

❯ clang++-12 -fsanitize=fuzzer,address -g fuzzer.cpp -o fuzzer

~/libFUZZER

❯ ./fuzzer
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 2232771517
INFO: Loaded 1 modules (11 inline 8-bit counters): 11 [0x545f00, 0x545f0b),
INFO: Loaded 1 PC tables (11 PCs): 11 [0x51ed50,0x51ee00),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2 INITED cov: 5 ft: 6 corp: 1/1b exec/s: 0 rss: 29Mb
#12 NEW cov: 6 ft: 7 corp: 2/5b lim: 4 exec/s: 0 rss: 29Mb L: 4/4 MS: 5 InsertByte-CrossOver-ChangeBit-CopyPart-InsertByte-
#759 NEW cov: 7 ft: 8 corp: 3/16b lim: 11 exec/s: 0 rss: 30Mb L: 11/11 MS: 2 ChangeByte-InsertRepeatedBytes-
#1584 NEW cov: 8 ft: 9 corp: 4/29b lim: 17 exec/s: 0 rss: 30Mb L: 13/13 MS: 5 ChangeBinInt-EraseBytes-ChangeByte-InsertRepeatedBytes-CMP- DE: "FUZZ"-
#1610 REDUCE cov: 8 ft: 9 corp: 4/26b lim: 17 exec/s: 0 rss: 30Mb L: 10/11 MS: 1 EraseBytes-
#1951 REDUCE cov: 8 ft: 9 corp: 4/25b lim: 17 exec/s: 0 rss: 30Mb L: 9/11 MS: 1 CrossOver-
#2034 REDUCE cov: 8 ft: 9 corp: 4/24b lim: 17 exec/s: 0 rss: 30Mb L: 8/11 MS: 3 ShuffleBytes-ChangeByte-EraseBytes-
#2226 REDUCE cov: 8 ft: 9 corp: 4/21b lim: 17 exec/s: 0 rss: 30Mb L: 5/11 MS: 2 ChangeBit-EraseBytes-
#2464 REDUCE cov: 8 ft: 9 corp: 4/20b lim: 17 exec/s: 0 rss: 30Mb L: 4/11 MS: 3 PersAutoDict-PersAutoDict-EraseBytes- DE: "FUZZ"-"FUZZ"-
#4324 NEW cov: 9 ft: 10 corp: 5/52b lim: 33 exec/s: 0 rss: 30Mb L: 32/32 MS: 5 ChangeBit-ShuffleBytes-ChangeByte-CopyPart-InsertRepeatedBytes-
#4345 REDUCE cov: 9 ft: 10 corp: 5/41b lim: 33 exec/s: 0 rss: 30Mb L: 21/21 MS: 1 EraseBytes-
#4405 REDUCE cov: 9 ft: 10 corp: 5/37b lim: 33 exec/s: 0 rss: 30Mb L: 17/17 MS: 5 PersAutoDict-ChangeByte-ChangeByte-PersAutoDict-EraseBytes- DE: "FUZZ"-"FUZZ"-
#4436 REDUCE cov: 9 ft: 10 corp: 5/35b lim: 33 exec/s: 0 rss: 30Mb L: 15/15 MS: 1 EraseBytes-
#4653 REDUCE cov: 9 ft: 10 corp: 5/32b lim: 33 exec/s: 0 rss: 30Mb L: 12/12 MS: 2 ShuffleBytes-EraseBytes-
=================================================================
==24969==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffca1585300 at pc 0x0000004d2d2a bp 0x7ffca15852b0 sp 0x7ffca1584a78
WRITE of size 33 at 0x7ffca1585300 thread T0
#0 0x4d2d29 in __asan_memcpy (/home/voyager/libFUZZER/fuzzer+0x4d2d29)
#1 0x507584 in ProcessInput(char const*, unsigned long) /home/voyager/libFUZZER/fuzzer.cpp:18:9
#2 0x50777c in LLVMFuzzerTestOneInput /home/voyager/libFUZZER/fuzzer.cpp:38:5
#3 0x43f703 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/voyager/libFUZZER/fuzzer+0x43f703)
#4 0x43eb3d in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/voyager/libFUZZER/fuzzer+0x43eb3d)
#5 0x44071a in fuzzer::Fuzzer::MutateAndTestOne() (/home/voyager/libFUZZER/fuzzer+0x44071a)
#6 0x441295 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) (/home/voyager/libFUZZER/fuzzer+0x441295)
#7 0x42f49b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/voyager/libFUZZER/fuzzer+0x42f49b)
#8 0x458ac2 in main (/home/voyager/libFUZZER/fuzzer+0x458ac2)
#9 0x70ebfae29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#10 0x70ebfae29e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#11 0x4238c4 in _start (/home/voyager/libFUZZER/fuzzer+0x4238c4)

Address 0x7ffca1585300 is located in stack of thread T0 at offset 64 in frame
#0 0x5072df in ProcessInput(char const*, unsigned long) /home/voyager/libFUZZER/fuzzer.cpp:7

This frame has 1 object(s):
[32, 64) 'buffer' (line 8) <== Memory access at offset 64 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/voyager/libFUZZER/fuzzer+0x4d2d29) in __asan_memcpy
Shadow bytes around the buggy address:
0x1000142a8a10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a50: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00
=>0x1000142a8a60:[f3]f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8aa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8ab0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==24969==ABORTING
MS: 5 ChangeByte-PersAutoDict-PersAutoDict-InsertRepeatedBytes-CopyPart- DE: "FUZZ"-"FUZZ"-; base unit: c9bba1a5cf7bc890d591ea990bf7c7078b323ffd
0x55,0x46,0x55,0x5a,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x46,0x55,0x5a,0x5a,0xff,0x0,0x0,0x0,0x0,0xff,0xff,0x21,
UFUZ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00FUZZ\xff\x00\x00\x00\x00\xff\xff!
artifact_prefix='./'; Test unit written to ./crash-ab68f7b1266d9825ba3bc866c7ba15aae5d603df
Base64: VUZVWgAAAAAAAAAAAAAAAAAAAAAARlVaWv8AAAAA//8h

~/libFUZZER

❯ ls
crash-ab68f7b1266d9825ba3bc866c7ba15aae5d603df fuzzer fuzzer.cpp

~/libFUZZER



这里主要看这个,写的是出现栈溢出错误,写入33字节出现错误,错误来源于第18行,第18行代码是这个memcpy(buffer, input, len)

1
2
3
4
5
==24969==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffca1585300 at pc 0x0000004d2d2a bp 0x7ffca15852b0 sp 0x7ffca1584a78
WRITE of size 33 at 0x7ffca1585300 thread T0
#0 0x4d2d29 in __asan_memcpy (/home/voyager/libFUZZER/fuzzer+0x4d2d29)
#1 0x507584 in ProcessInput(char const*, unsigned long) /home/voyager/libFUZZER/fuzzer.cpp:18:9
#2 0x50777c in LLVMFuzzerTestOneInput /home/voyager/libFUZZER/fuzzer.cpp:38:5

然后这里还贴出了栈结构,这里的f1 f1 f1 f1是左保护区,一个f1占8字节,[f3]f3 f3 f3这是右保护区,有个中括号就是说程序企图在那个位置写入数据结构报错了

1
2
3
4
5
6
7
8
9
10
11
12
13
Shadow bytes around the buggy address:
0x1000142a8a10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a50: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00
=>0x1000142a8a60:[f3]f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8a90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8aa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000142a8ab0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):

cov:目前覆盖的代码块的数量,ft是特征函数,corp:前者指的是输入数据,后者指的是占用的字节数,rss:内存使用量

1
2
3
4
5
INFO: A corpus is not provided, starting from an empty corpus
#2 INITED cov: 5 ft: 6 corp: 1/1b exec/s: 0 rss: 29Mb
#12 NEW cov: 6 ft: 7 corp: 2/5b lim: 4 exec/s: 0 rss: 29Mb L: 4/4 MS: 5 InsertByte-CrossOver-ChangeBit-CopyPart-InsertByte-
#759 NEW cov: 7 ft: 8 corp: 3/16b lim: 11 exec/s: 0 rss: 30Mb L: 11/11 MS: 2 ChangeByte-InsertRepeatedBytes-
#1584 NEW cov: 8 ft: 9 corp: 4/29b lim: 17 exec/s: 0 rss: 30Mb L: 13/13 MS: 5 ChangeBinInt-EraseBytes-ChangeByte-InsertRepeatedBytes-CMP- DE: "FUZZ"-

实战进阶

种子语料库

正常情况下如果不给任何提示的话,会一个个尝试,但是如果给个提示,规划会更加准确

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

~/libFUZZER

❯ mkdir corpus

~/libFUZZER

❯ echo "FUZZ" > corpus/1.txt

~/libFUZZER

❯ echo "" > corpus/2.txt

~/libFUZZER

❯ ./fuzzer corpus/
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 972519927
INFO: Loaded 1 modules (11 inline 8-bit counters): 11 [0x545f00, 0x545f0b),
INFO: Loaded 1 PC tables (11 PCs): 11 [0x51ed50,0x51ee00),
INFO: 2 files found in corpus/
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: seed corpus: files: 2 min: 1b max: 5b total: 6b rss: 29Mb
#3 INITED cov: 6 ft: 7 corp: 2/6b exec/s: 0 rss: 30Mb
#4 NEW cov: 7 ft: 8 corp: 3/11b lim: 5 exec/s: 0 rss: 30Mb L: 5/5 MS: 1 ChangeBit-
#97 REDUCE cov: 7 ft: 8 corp: 3/10b lim: 5 exec/s: 0 rss: 30Mb L: 4/5 MS: 3 CopyPart-CopyPart-CrossOver-
#181 REDUCE cov: 7 ft: 8 corp: 3/9b lim: 5 exec/s: 0 rss: 30Mb L: 4/4 MS: 4 CopyPart-ShuffleBytes-CrossOver-EraseBytes-
#899 NEW cov: 8 ft: 9 corp: 4/20b lim: 12 exec/s: 0 rss: 30Mb L: 11/11 MS: 3 ChangeBinInt-CopyPart-InsertRepeatedBytes-
=================================================================
==4785==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcbd501b20 at pc 0x0000004bf2f8 bp 0x7ffcbd501ad0 sp 0x7ffcbd501290
WRITE of size 33 at 0x7ffcbd501b20 thread T0
#0 0x4bf2f7 in strcpy (/home/voyager/libFUZZER/fuzzer+0x4bf2f7)
#1 0x507490 in ProcessInput(char const*, unsigned long) /home/voyager/libFUZZER/fuzzer.cpp:12:9
#2 0x50777c in LLVMFuzzerTestOneInput /home/voyager/libFUZZER/fuzzer.cpp:38:5
#3 0x43f703 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/voyager/libFUZZER/fuzzer+0x43f703)
#4 0x43eb3d in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/voyager/libFUZZER/fuzzer+0x43eb3d)
#5 0x44071a in fuzzer::Fuzzer::MutateAndTestOne() (/home/voyager/libFUZZER/fuzzer+0x44071a)
#6 0x441295 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) (/home/voyager/libFUZZER/fuzzer+0x441295)
#7 0x42f49b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/voyager/libFUZZER/fuzzer+0x42f49b)
#8 0x458ac2 in main (/home/voyager/libFUZZER/fuzzer+0x458ac2)
#9 0x7ba577629d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#10 0x7ba577629e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#11 0x4238c4 in _start (/home/voyager/libFUZZER/fuzzer+0x4238c4)

Address 0x7ffcbd501b20 is located in stack of thread T0 at offset 64 in frame
#0 0x5072df in ProcessInput(char const*, unsigned long) /home/voyager/libFUZZER/fuzzer.cpp:7

This frame has 1 object(s):
[32, 64) 'buffer' (line 8) <== Memory access at offset 64 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/voyager/libFUZZER/fuzzer+0x4bf2f7) in strcpy
Shadow bytes around the buggy address:
0x100017a98310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100017a98320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100017a98330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100017a98340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100017a98350: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
=>0x100017a98360: 00 00 00 00[f3]f3 f3 f3 00 00 00 00 00 00 00 00
0x100017a98370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100017a98380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100017a98390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100017a983a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100017a983b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==4785==ABORTING
MS: 3 CopyPart-InsertRepeatedBytes-CrossOver-; base unit: aea2e3923af219a8956f626558ef32f30a914ebc
0x46,0x55,0x5a,0x5a,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xdb,0x46,0xd,0xd,
FUZZ\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\x0d\x0d\x0d\x0d\x0d\x0d\x0d\x0d\xdbF\x0d\x0d
artifact_prefix='./'; Test unit written to ./crash-48c0a02544c52a452337234011a2505e4935c662
Base64: RlVaWtvb29vb29vb29vb29vb29sNDQ0NDQ0NDdtGDQ0=

~/libFUZZER


先建立一个语料库,然后写入数据,接下来运行时加上那个文件夹就好了,这次可以看到文件时先触发

strcpy(buffer, input);函数

1
2
3
4
5
==4785==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcbd501b20 at pc 0x0000004bf2f8 bp 0x7ffcbd501ad0 sp 0x7ffcbd501290
WRITE of size 33 at 0x7ffcbd501b20 thread T0
#0 0x4bf2f7 in strcpy (/home/voyager/libFUZZER/fuzzer+0x4bf2f7)
#1 0x507490 in ProcessInput(char const*, unsigned long) /home/voyager/libFUZZER/fuzzer.cpp:12:9
#2 0x50777c in LLVMFuzzerTestOneInput /home/voyager/libFUZZER/fuzzer.cpp:38:5

字典文件

就是理解成程序读取语料库后在获取字典的文本进行拼接或者是替换,这个可以用在SQL语句注入或者是http查找,但是我现在还不太了解web安全,就不举例子

执行代码

1
./fuzzer corpus/ -dict=demo.dict

参数调优

上面讲到字典运用的话是在后面加上-dict=xxx,那么还有哪些搭配?

1
2
3
4
5
6
7
8
-max_total_time=600	#限制运行时间
-max_len=1024 #限制最大字节
-runs=1000 #限制运行次数
-rss_limit_mb=2048 #限制内存占用
-timeout=10 #设置超时时间,防止hang
./fuzzer & ./fuzzer & ./fuzzer #多核并运行
#或者使用内置函数
./fuzzer -jobs=4 corpus/

调试分析

就是说漏洞发现后怎么处理

crash复现

我们每次执行完都会生成文本,一堆乱码,然后可以通过这样子复现,一般不能复现出的话可能是全局污染或者是有竞争条件

1
2
3
4
5
6
7
8
9
10
11
❯ ls
corpus demo.dict
crash-48c0a02544c52a452337234011a2505e4935c662 fuzzer
crash-e971bffe6a98b8c21b19a388086c7f7846b5acbd fuzzer.cpp

~/libFUZZER

❯ ./fuzzer crash-48c0a02544c52a452337234011a2505e4935c662
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1128742899

然后就是想法上面一样找到错误原因和代码行,但是有的时候会生成很大一部分内容,比较难找到

像这样,直接告诉我们报错的地方,尝试输入32字节造成crash,比较方便分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
~/libFUZZER 

❯ ./fuzzer -minimize_crash=1 crash-48c0a02544c52a452337234011a2505e4935c662
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 941477935
INFO: Loaded 1 modules (11 inline 8-bit counters): 11 [0x545f00, 0x545f0b),
INFO: Loaded 1 PC tables (11 PCs): 11 [0x51ed50,0x51ee00),
INFO: you need to specify -runs=N or -max_total_time=N with -minimize_crash=1
INFO: defaulting to -max_total_time=600
CRASH_MIN: minimizing crash input: 'crash-48c0a02544c52a452337234011a2505e4935c662' (32 bytes)
CRASH_MIN: executing: ./fuzzer -max_total_time=600 crash-48c0a02544c52a452337234011a2505e4935c662 2>&1
CRASH_MIN: 'crash-48c0a02544c52a452337234011a2505e4935c662' (32 bytes) caused a crash. Will try to minimize it further
CRASH_MIN: executing: ./fuzzer -max_total_time=600 crash-48c0a02544c52a452337234011a2505e4935c662 -minimize_crash_internal_step=1 -exact_artifact_path=./minimized-from-48c0a02544c52a452337234011a2505e4935c662 2>&1


参考致谢

libFuzzer模糊测试基础教程:从入门到实战-先知社区