栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

【Custom Mutator Fuzz】Libprotobuf + LibFuzzer Custom Mutator

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Custom Mutator Fuzz】Libprotobuf + LibFuzzer Custom Mutator

前言

这篇文章主要讲的是在Libprotobuf-mutator与LibFuzzer联合使用的基础上,加上custom mutator功能。首先需要明确的是为什么要这么做,如果你看了上一篇【Custom Mutator Fuzz】Libprotobuf + LibFuzzer联合使用就可以发现,我们虽然构造了拥有a、b两个字段的结构,但是结构中的数据是由LibFuzzer随机突变生成的。那么假设b字段只有为"FUZZ"或"PWN"两个字符的时候才能进入下一个程序分支的情况,当然LibFuzzer也可以在代码覆盖率的加持下进入下一个程序分支,但如果你通过逆向的方式已经知道了这个关键点,难道还需要等LibFuzzer跑出这两个字符串吗?这显然十分的浪费时间和资源,所以custom mutator在某些情况下还是更加有效的

编写不易,如果能够帮助到你,希望能够点赞收藏加关注哦Thanks♪(・ω・)ノ
PS:文章末尾有联系方式,交个朋友吧~

本文链接:https://hollk.blog.csdn.net/article/details/124725688

模糊测试系列往期回顾:
【Custom Mutator Fuzz】Libprotobuf + LibFuzzer联合使用
【Custom Mutator Fuzz】简单Protobuf使用练习
【Custom Mutator Fuzz】Protocol Buffer基础(下):C++生成代码介绍 + 配套代码
【Custom Mutator Fuzz】Protocol Buffer基础(上):proto2编写格式
【Custom Mutator Fuzz】libprotobuf-mutator安装
LibFuzzer学习(三):使用小trick
LibFuzzer学习(二):提高代码覆盖率和速度
LibFuzzer学习(一):轻松找到心脏出血漏洞
Fuzz出结果不会看?Address Sanitizer(ASan)各类溢出demo分析
Fuzzing101 Exercise 5 - LibXML2 学习笔记
Fuzzing101 Exercise 4 - LibTIFF 学习笔记
Fuzzing101 Exercise 3 - TCPdump 学习笔记
Fuzzing101 Exercise 2 - Libexif 学习笔记
Fuzzing101 Exercise 1 - Xpdf学习笔记
AFL源码分析之afl-fuzz.c详细注释(二):FUZZ执行流程(五万字警告,慎入)
AFL源码分析之afl-fuzz.c详细注释(一):初始配置(6万字警告,慎入)
AFL源码分析之afl-gcc.c详细注释
AFL源码分析之afl-as.c详细注释
免费资源:AFL-2.57b.zip(AFL源码分析章节版本)

编写protobuf文件

第三个练习依然还是使用上一篇【Custom Mutator Fuzz】简单Protobuf使用练习中的proto源码:

syntax = "proto2";

message Hollk {
  required uint32 a = 1;
  required string b = 2;
}

这里定义了一个required的无符号整型a,和一个required的字符串类型b。接下来使用前面【Custom Mutator Fuzz】libprotobuf-mutator安装文章中已经装好了的protoc编译器编一下:

/home/hollk/libprotobuf-mutator/build/external.protobuf/bin/protoc-3.17.3.0 ./test.proto  --cpp_out=./

接下来就可以在当前目录下看到hollk.pb.h和hollk.pb.cc两个文件了

编写目标库harness.cc

和练习2一样,需要设计一个目标程序,向其中中输入某特定字符参数后程序中断

//harness.cc
#include 
#include 

extern "C" int FuzzTEST(const uint8_t *data, size_t size) {
    if(data[0] == 'x01') {
        __builtin_trap();
    }
    return 0;
}

简单解释一下这个目标程序,整体只有一个函数FuzzTEST,并且支持C和C++混合编程,主要的参数有两个,一个是指针数组data,另外一个是size。函数内部,如果data的第一个字节为x01则整个程序中断

编写fuzzer程序lpm_libfuzz_custom_mutator.cc

这里和上一篇【Custom Mutator Fuzz】Libprotobuf + LibFuzzer联合使用文章中的过程稍有不同,如果仅仅只是靠LibFuzzer按照protobuf的结构进行编译,其实没有能够诠释custom mutator的效果。因此在这篇文章编写fuzzer的时候想要加一点自定义的部分进去,也就是说玩点花的~那么首先还是需要缕清一下fuzzer程序的执行流程:

  • 第一,使用LibFuzzer为protobuf中的数据结构提供变异的数据
  • 第二,protobuf中的二进制流数据需要转化成字节流数据传递给被测试函数
  • 第三,需要加一点自定义变异策略进去,起始向上翻一翻,可以看到目标FuzzTEST函数中判断崩溃的条件是数据起始字节为x01,这就意味着protobuf中的b字段其实并没有什么变异的必要,那么就可以考虑在b字段中做一些花样来展示自定义效果
#include "libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h" //实现LibFuzzer封装头文件
#include "hollk.pb.h" //protobuf头文件

#include 

using std::cin; //初始化输入
using std::cout; //初始化输出
using std::endl; //初始化换行

std::string ProtoToData(const Hollk &hollk) { //protobuf二进制流数据转字节流数据
    std::stringstream all; 
    const auto &aa = hollk.a(); //获取a字段值存放进aa中
    const auto &bb = hollk.b(); //获取b字段值存放进bb中
    all.write((const char*)&aa, sizeof(aa)); //读取aa中的二进制流数据,存入all中
    if(bb.size() != 0) { //如果bb中的数据长度不为0
        all.write(bb.c_str(), bb.size()); //读取bb中的二进制流数据,存入all中
    }

    std::string res = all.str(); //将all中二进制流数据转化为string类型,赋值给res
    if (bb.size() != 0 && res.size() != 0) { //如果bb和res中都存在数据
        // 设置PROTO_FUZZER_DUMP_PATH env以便用来转储protobuf的序列化数据
        if (const char *dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) {
            std::ofstream of(dump_path);
            of.write(res.data(), res.size());
        }
    }
    return res;
}

extern "C" int FuzzTEST(const uint8_t* data, size_t size); // 目标函数
bool hasRegister = false; //设置一个bool型全局变量,用来判断是否已完成自定义mutator

//LibFuzzer封装DEFINE_PROTO_FUZZER,出自libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h
DEFINE_PROTO_FUZZER(const Hollk &hollk) {
    if(!hasRegister) {
        protobuf_mutator::libfuzzer::RegisterPostProcessor( //libprotobuf-mutator后处理器回调
                Hollk::descriptor(), //一参,指向Hollk message描述符的const指针
                //二参,实现自定义的回调lambda函数
                //lambda函数一参:message对象,二参:Libprotobuf-mutator提供的int型伪随机数种子seed
                [](google::protobuf::Message* message, unsigned int seed) { 
                    Hollk *t = static_cast(message); //强制转换为Hollk message对象
                    // 随机奇偶,即50%概率设置b字段为"FUZZ"或者"PWN"
                    if (seed % 2) {
                        t->set_b("FUZZ");
                    }
                    else {
                        t->set_b("PWN");
                    }
                }
            );
        hasRegister = true; 
        return;
    }

    auto s = ProtoToData(hollk); // 转化为字节流
    FuzzTEST((const uint8_t*)s.data(), s.size()); // 喂给目标函数FuzzTEST进行fuzz
}

前面与上一篇相似的部分这里就不再过多赘述了,可以查看【Custom Mutator Fuzz】Libprotobuf + LibFuzzer联合使用这篇文章进行比对。这里主要想说一下libprotobuf-mutator提供的后处理器回调设置函数RegisterPostProcessor,函数所在文件路径为:/home/hollk/libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h。函数原型如下:

void RegisterPostProcessor(
    const protobuf::Descriptor* desc,
    std::function
        callback);

Libprotobuf-mutator通过使用该函数在protobuf的message类型上注册后处理器回调,在Libprotobuf-mutator每次执行突变之后调用后处理器

  • 一参:protobuf中message描述符,Libprotobuf-mutator将回调映射到该描述符中
  • 二参:回调函数(一参:protobuf message结构元素,二参Libprotobuf-mutator提供的伪随机数)

这里的二参使用了lambda表达式的形式,首先指定元素为Hollk message对象,再通过判断伪随机数的奇偶值,即构造50%概率对b字段赋值为FUZZ或PWN字符,这样一来就做出了一个比较简单的自定义结构

编写Makefile文件
TARGET=lpm_libfuzz_custom_mutator # 编译目标程序名称
CXX=clang++ # 设置编译器为clang++
CXXFLAGS=-g -fsanitize=fuzzer,address # 设置使用LibFuzzer和ASAN
PB_SRC=hollk.pb.cc # 设置protobuf文件

PROTOBUF_DIR=$(HOME)/libprotobuf-mutator/build/external.protobuf # 设置protobuf目录
LPM_DIR=$(HOME)/libprotobuf-mutator # 设置Libprotobuf-mutator目录
PROTOBUF_LIB=$(PROTOBUF_DIR)/lib/libprotobufd.a # 设置protobuf动态链接库
# 设置Libprotobuf-mutator中与LibFuzzer联动的动态链接库
LPM_LIB=$(LPM_DIR)/build/src/libfuzzer/libprotobuf-mutator-libfuzzer.a $(LPM_DIR)/build/src/libprotobuf-mutator.a
INC=-I$(PROTOBUF_DIR)/include -I$(LPM_DIR) # 添加包含文件路径
DFUZZ=-DLLVMFuzzerTestOneInput=FuzzTEST # 设置模糊测试目标函数名称

all: $(TARGET) # 按顺序完成所有编译操作

harness.o: harness.cc # 将harness.cc编译成一个库文件
	$(CXX) $(CXXFLAGS) -c $(DFUZZ) $< 

$(TARGET): harness.o $(TARGET).cc # 编译lpm_libfuzz程序
	$(CXX) $(CXXFLAGS) -o $@ $^ $(PB_SRC) $(LPM_LIB) $(PROTOBUF_LIB) $(INC)

.PHONY: clean
clean: 
	rm $(TARGET) *.o

这样一来我们只需要在当前目录使用make all命令,Makefile就相当于帮助我们执行了如下两条命令:

clang++ -g -fsanitize=fuzzer,address -c -DLLVMFuzzerTestOneInput=FuzzTEST harness.cc
clang++ -g -fsanitize=fuzzer,address -o lpm_libfuzz harness.o lpm_libfuzz.cc hollk.pb.cc /home/hollk/libprotobuf-mutator/build/src/libfuzzer/libprotobuf-mutator-libfuzzer.a /home/hollk/libprotobuf-mutator/build/src/libprotobuf-mutator.a -I /home/hollk/libprotobuf-mutator/build/external.protobuf/include/ -I /home/hollk/libprotobuf-mutator

当然,你也可以分别单独使用make harness.o命令和make lpm_libfuzz命令单独对两个程序进行编译。使用make all命令之后,就会在当前目录中出现如下的几个文件,生成的lpm_libfuzz_custom_mutator程序就是执行的fuzzer:

执行程序

可以看到LibFuzzer在尝试3次突变后发现了deadly signal类型错误(不知道什么是deadly signal类型错误,可以参考我的Fuzz出结果不会看?Address Sanitizer(ASan)各类溢出demo分析这篇文章),并将crash数据存储在了本地crash-0ec76c8f1e8099ec26bfc85609785b2d628bcaa6文件中

再看一下crash-0ec76c8f1e8099ec26bfc85609785b2d628bcaa6文件中保存的崩溃数据:


可以看到b字段按照我们定义的方式成为了PWN

可能会遇到的问题,以及处理方法

问题1
可能会出现提示版本错误的问题:error: This file was generated by an older version of protoc which is

如果在做libprotobuf-mutator_fuzzing_learning练习的时候,直接使用项目练习2中的test.pb.h和test.pb.cc的话可能会出现版本错误的问题。这是因为项目中存放的这两个文件是由比较老版本的protobuf编译器进行编译的,但是在后面使用Makefile的时候就会发现,所用的动态链接库都是都是protoc-3.17.3.0版本。这就导致了版本不匹配的问题

解决方法:

/home/hollk/libprotobuf-mutator/build/external.protobuf/bin/protoc-3.17.3.0 ./test.proto  --cpp_out=./

使用新的protobuf编译器重新编译.proto文件,你可能在练习2中找不到.proto文件,这里其实可以沿用练习1中的.proto文件内容。或者直接用本文开头中的proto代码

问题2
可能会出现的问题:clang: error: linker command failed with exit code 1 (use -v to see invocation)

这是因为在编译lpm_libfuzz_custom_mutator程序时候$(LPM_LIB)和$(PROTOBUF_LIB)两个动态链接库优先级导致的,$(LPM_LIB)需要在$(PROTOBUF_LIB)前面

参考链接
https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning
https://bshastry.github.io/2019/12/27/Custom-Proto-Mutation.html

交个朋友吧~

扫描下方微信二维码,一起讨论研究

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/889440.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号