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

【Custom Mutator Fuzz】Libprotobuf + LibFuzzer联合使用

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

【Custom Mutator Fuzz】Libprotobuf + LibFuzzer联合使用

前言

终于到了与fuzzer结合使用的章节了,本篇文章为Libprotobuf mutator fuzzing learning项目的第二个练习,其中有一些坑点,在本文中也进行了标注

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

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

模糊测试系列往期回顾:
【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

我们想要的效果是调用目标库中某个函数时,向其中中输入某特定字符参数后程序中断

//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_libufzz.cc

这里需要构思一下这个fuzzer程序需要拥有的功能有哪些:

  • 首先,我们需要用到LibFuzzer对protobuf中的数据进行变异赋值,那么Libprotobuf-mutator中的libfuzzer_macro.h中存在处理proto序列化数据的方法,直接调用就可以了
  • 其次,protobuf中的数据是以二进制流的形式进行存储传递的,但是交给被测试库的时候应该是以字节流的形式传递,因此需要一个格式转换的功能函数ProtoToData()
  • 最后,必须调用harness库中的FuzzTEST函数进行测试
#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); // 目标函数

//LibFuzzer封装DEFINE_PROTO_FUZZER,出自libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h
DEFINE_PROTO_FUZZER(const Hollk &hollk) { 
    auto s = ProtoToData(hollk); // 转化为字节流
    FuzzTEST((const uint8_t*)s.data(), s.size()); // 喂给目标函数FuzzTEST进行fuzz
}

那么这个实验的处理流程就比较清楚了,LibFuzzer为protobuf结构提供变异数据,但由于protobuf中是以二进制流的形式存储的,所以需要先使用ProtoData()函数将数据转化成字节流的形式,再把数据在喂给目标函数FuzzTEST()

编写Makefile文件
TARGET=lpm_libfuzz # 编译目标程序名称
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程序就是执行的fuzzer:

执行程序

这里我们执行一下lpm_libfuzz程序

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

这里可能有点忘记了前面被测试目标函数中的内容,我们来回顾一下:

目标函数FuzzTEST()中,只要传入的数据的第一个字节为x01的话,就会进行中断处理。因此可以看到LibFuzzer找到的漏洞类型与我们设计的中断功能是吻合的。再看一下crash-ed151246ffc12cf4e3ae06b71b61d689e51a9ffd文件中的数据内容:


可以看到a字段中的1作为数据的起始数据,触发了中断效果

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

问题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程序时候$(LPM_LIB)和$(PROTOBUF_LIB)两个动态链接库优先级导致的,$(LPM_LIB)需要在$(PROTOBUF_LIB)前面

这就是本篇Libprotobuf-mutator与LibFuzzer联合使用的全部内容了,虽然例子比较简单,但本文主要介绍的是联合使用的过程,当然你也可以按照实际的需求定制proto文件,以及变异策略~

参考链接
https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning

交个朋友吧~

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

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

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

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