Chinaunix首页 | 论坛 | 博客
  • 博客访问: 518485
  • 博文数量: 184
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1172
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-21 13:40
个人简介

技术改变命运

文章分类

全部博文(184)

文章存档

2020年(16)

2017年(12)

2016年(156)

我的朋友

分类: 网络与安全

2020-07-22 21:36:31

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

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

一个简单的程序

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

  1. #include
  2. int main(){
  3. int a =0;
  4. scanf("%d",&a);
  5. printf("%d\n",a*a);
  6. return 0;
  7. }

编译 gcc test.c 运行 ./a.out 在这里插入图片描述通过观察,很容易看出当输入一些"畸形"数据的时候会导致程序运行出现错误。如果只有二进制文件,有什么办法可以检测出这种错误?

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。

  1. #!/usr/bin/env python2
  2. # generate_testcases.py
  3. import os
  4. import random
  5. WORK_DIR = 'work'
  6. # Create work `directory` and `corpus` subdirectory.
  7. if not os.path.exists(WORK_DIR):
  8. os.mkdir(WORK_DIR)
  9. corpus_dir = os.path.join(WORK_DIR, 'corpus')
  10. if not os.path.exists(corpus_dir):
  11. os.mkdir(corpus_dir)
  12. seed_corpus_filenames = os.listdir('seed_corpus')
  13. for i in xrange(1000):
  14. random_seed_filename = random.choice(seed_corpus_filenames)
  15. random_seed_filename = os.path.join('seed_corpus', random_seed_filename)
  16. output_filename = os.path.join(WORK_DIR, 'corpus', 'testcase-%06d' % i)
  17. cmd = 'bin/radamsa "%s" > "%s"' % (random_seed_filename, output_filename)
  18. os.popen(cmd)
开始fuzz

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

  1. #!/usr/bin/env python2
  2. # run_fuzzing.py
  3. import os
  4. import subprocess
  5. WORK_DIR = 'work'
  6. def checkOutput(s):
  7. if 'Segmentation fault' in s or 'error' in s.lower():
  8. return False
  9. else:
  10. return True
  11. corpus_dir = os.path.join(WORK_DIR, 'corpus')
  12. corpus_filenames = os.listdir(corpus_dir)
  13. for f in corpus_filenames:
  14. testcase_path = os.path.join(corpus_dir, f)
  15. cmd = ['/usr/bin/code', testcase_path]
  16. process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
  17. output = process.communicate()[0]
  18. if not checkOutput(output):
  19. print testcase_path
  20. print output
  21. print '-' * 80

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

  1. #!/bin/bash
  2. while [ "0" -lt "1" ]
  3. do
  4. rm -rf ./work/
  5. ./generate_testcases.py
  6. ./run_fuzzing.py
  7. 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
  1. bool VulnerableFunction1(const uint8_t* data, size_t size) {
  2. bool result = false;
  3. if (size >= 3) {
  4. result = data[0] == 'F' &&
  5. data[1] == 'U' &&
  6. data[2] == 'Z' &&
  7. data[3] == 'Z';
  8. }
  9. return result;
  10. }

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

fuzzer

first_fuzzer.cc

  1. extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  2. VulnerableFunction1(data, size);
  3. return 0;
  4. }

编译: clang++-g-fsanitize=address,fuzzer first_fuzzer.cc 运行fuzzer: ./a.out2>&1|grep ERROR 在这里插入图片描述完整的输出

  1. INFO: Seed: 100908111
  2. INFO: Loaded 1 modules (35 inline 8-bit counters): 35 [0x7f8120, 0x7f8143),
  3. INFO: Loaded 1 PC tables (35 PCs): 35 [0x5b7f68,0x5b8198),
  4. INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
  5. INFO: A corpus is not provided, starting from an empty corpus
  6. #2 INITED cov: 3 ft: 3 corp: 1/1b exec/s: 0 rss: 27Mb
  7. #15 NEW cov: 4 ft: 4 corp: 2/4b lim: 4 exec/s: 0 rss: 27Mb L: 3/3 MS: 3 CopyPart-CrossOver-InsertByte-
  8. #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"-
  9. #2688 NEW cov: 6 ft: 6 corp: 4/15b lim: 29 exec/s: 0 rss: 27Mb L: 8/8 MS: 2 ChangeByte-InsertRepeatedBytes-
  10. #2704 REDUCE cov: 6 ft: 6 corp: 4/13b lim: 29 exec/s: 0 rss: 28Mb L: 6/6 MS: 1 EraseBytes-
  11. #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"-
  12. #3080 REDUCE cov: 6 ft: 6 corp: 4/11b lim: 29 exec/s: 0 rss: 28Mb L: 4/4 MS: 1 EraseBytes-
  13. #3172 REDUCE cov: 6 ft: 6 corp: 4/10b lim: 29 exec/s: 0 rss: 28Mb L: 3/3 MS: 2 CopyPart-EraseBytes-
  14. #10828 REDUCE cov: 7 ft: 7 corp: 5/70b lim: 104 exec/s: 0 rss: 28Mb L: 60/60 MS: 1 InsertRepeatedBytes-
  15. #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"-
  16. #10933 REDUCE cov: 7 ft: 7 corp: 5/24b lim: 104 exec/s: 0 rss: 28Mb L: 14/14 MS: 3 ChangeByte-InsertByte-EraseBytes-
  17. #11125 REDUCE cov: 7 ft: 7 corp: 5/21b lim: 104 exec/s: 0 rss: 28Mb L: 11/11 MS: 2 InsertByte-EraseBytes-
  18. #11361 REDUCE cov: 7 ft: 7 corp: 5/18b lim: 104 exec/s: 0 rss: 28Mb L: 8/8 MS: 1 EraseBytes-
  19. #11482 REDUCE cov: 7 ft: 7 corp: 5/14b lim: 104 exec/s: 0 rss: 28Mb L: 4/4 MS: 1 EraseBytes-
  20. =================================================================
  21. ==3357==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000040f73 at pc 0x00000059b461 bp 0x7fff657ee560 sp 0x7fff657ee558
  22. READ of size 1 at 0x602000040f73 thread T0
  23. #0 0x59b460 in VulnerableFunction1(unsigned char const*, unsigned long) /home/burning/linux/libfuzzer-workshop-master/lessons/04/./vulnerable_functions.h:22:14
  24. #1 0x59bde4 in LLVMFuzzerTestOneInput /home/burning/linux/libfuzzer-workshop-master/lessons/04/first_fuzzer.cc:10:3
  25. #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
  26. #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
  27. #4 0x46b7e9 in fuzzer::Fuzzer::MutateAndTestOne() /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:699
  28. #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
  29. #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
  30. #7 0x41f522 in main /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerMain.cpp:19
  31. #8 0x7fa043c3eb96 in __libc_start_main /build/glibc-2ORdQG/glibc-2.27/csu/../csu/libc-start.c:310
  32. #9 0x41f599 in _start (/home/burning/linux/libfuzzer-workshop-master/lessons/04/a.out+0x41f599)
  33. 0x602000040f73 is located 0 bytes to the right of 3-byte region [0x602000040f70,0x602000040f73)
  34. allocated by thread T0 here:
  35. #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
  36. #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
  37. #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
  38. #3 0x46b7e9 in fuzzer::Fuzzer::MutateAndTestOne() /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:699
  39. #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
  40. #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
  41. #6 0x41f522 in main /home/burning/linux/libfuzzer-workshop-master/src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerMain.cpp:19
  42. #7 0x7fa043c3eb96 in __libc_start_main /build/glibc-2ORdQG/glibc-2.27/csu/../csu/libc-start.c:310
  43. SUMMARY: AddressSanitizer: heap-buffer-overflow /home/burning/linux/libfuzzer-workshop-master/lessons/04/./vulnerable_functions.h:22:14 in VulnerableFunction1(unsigned char const*, unsigned long)
  44. Shadow bytes around the buggy address:
  45. 0x0c0480000190: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  46. 0x0c04800001a0: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  47. 0x0c04800001b0: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  48. 0x0c04800001c0: fa fa fd fd fa fa fd fd fa fa fd fa fa fa fd fa
  49. 0x0c04800001d0: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  50. =>0x0c04800001e0: fa fa fd fa fa fa fd fa fa fa fd fa fa fa[03]fa
  51. 0x0c04800001f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  52. 0x0c0480000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  53. 0x0c0480000210: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  54. 0x0c0480000220: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  55. 0x0c0480000230: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  56. Shadow byte legend (one shadow byte represents 8 application bytes):
  57. Addressable: 00
  58. Partially addressable: 01 02 03 04 05 06 07
  59. Heap left redzone: fa
  60. Freed heap region: fd
  61. Stack left redzone: f1
  62. Stack mid redzone: f2
  63. Stack right redzone: f3
  64. Stack after return: f5
  65. Stack use after scope: f8
  66. Global redzone: f9
  67. Global init order: f6
  68. Poisoned by user: f7
  69. Container overflow: fc
  70. Array cookie: ac
  71. Intra object redzone: bb
  72. ASan internal: fe
  73. Left alloca redzone: ca
  74. Right alloca redzone: cb
  75. Shadow gap: cc
  76. ==3357==ABORTING
  77. MS: 5 CopyPart-EraseBytes-PersAutoDict-EraseBytes-ChangeBinInt- DE: "F\x00"-; base unit: 9dbab5c181e3d878fa7e1229929a99dc1f04abf4
  78. 0x46,0x55,0x5a,
  79. FUZ
  80. artifact_prefix='./'; Test unit written to ./crash-0eb8e4ed029b774d80f2b66408203801cb982a60

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

  • Seed: 1608565063 说明这次的种子数据
  • max_len 用于设置最大的数据长度
  • 接下来 # 开头的行是 fuzz 过程中找到的路径信息
  • 最后一行是触发漏洞的测试用例

重现crash:

  1. ASAN_OPTIONS=symbolize=1 ./a.out ./crash-0eb8e4ed029b774d80f2b66408203801cb982a60
  2. # ASAN_OPTIONS=symbolize=1 用于显示 栈的符号信息

写在最后

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

公众号

更多内容,欢迎关注我的微信公众号:无情剑客。 在这里插入图片描述

阅读(2426) | 评论(0) | 转发(0) |
1

上一篇:网络嗅探

下一篇:Frida入门

给主人留下些什么吧!~~