什么是 Fuzz (模糊测试)
一种软件测试技术
自动或半自动的生成随机的测试数据
监视程序异常(crash,oom,timeout等等)
传统 Fuzz 的特点
随机构造数据
对于大型、复杂程序需要预先准备大量用例 一些代码分支很难走到
靠运气
现代 Fuzz 的特点
在有源代码的前提下给代码插桩
基于代码覆盖率反馈的 Fuzz
把那些能产生新的覆盖路径的用例给保存下来
与各种 Sanitizer 相结合(如ASAN、UBSAN、MSAN、TSAN 等等) 知名的工具有 AFL、LibFuzzer、HongFuzz 等
进入正题:FFmpeg 介绍
FFmpeg是一套目前非常流行的的开源计算机程序。
它提供了录制、播放、转换以及流化音视频的完整解决方案。
目前有非常多的视音频软件或是视频网站、手机 APP 都采用了这个库,但
是这个库历史上曝出的漏洞也非常之多。
视音频技术主要包含以下几点:
封装技术,视频压缩编码技术以及音频压缩编码技术。如果考虑到网络传输的话,还包括流媒体协议技术。
Fuzz 准备
寻找合适的攻击层面
Codec
google 已经 Fuzz 过了,OSS-Fuzz 上也有 FFmpeg 的 project,里面的 fuzz target 就是针对的 codec Muxer and Demuxer Google 也有 Fuzz(通过查看 git log) Protocol Emil Lerner和Pavel Cheremushkin使用 AFL Fuzz,改用 libFuzzer。
LibFuzzer
Google 工程师2015年开发,基于 LLVM 的 clang 实现和需要被 Fuzz 的库链接在一起,通过一个特殊的 Fuzz 入口点将Fuzz 输入喂给 library 基于代码覆盖率的反馈来生成新的用例和丢弃无用的用例 In-process Fuzz, 速度最高能达到 AFL 的几十倍。
如何 Fuzz 网络协议?
libFuzzer 只能从 buffer 输入 重载所有的 socket 相关的操作 已有的轮子——preeny ( written by Shellphish ) LD_PRELOAD=/path/to/desock.so
Preeny中 desock.c实现原理
重载了socket,accept,accept4,bind,listen,connect 等函数。
通过 LD_PRELOAD 环境变量预先加载desock.so文件,覆 盖libc 中的同名函数。
Stdin写入 back socket
遇到 EOF终止
启动两个线程
返回 front socket
Back socket 写入stdout
遇到 EOF 终止
定制 preeny
libFuzzer 在fuzz target 启动之初就把所有的数据喂给 stdin, 在 desock.c 里面 socket 函数是启动一个线程,把所有数据从 stdin 写入了 back socket ,等这个线程结束了再往下运行。 针对 ftp 这种需要开启两个 socket 的连接的,原本的 preeny 代码不适用。如果 检测是第二次socket 连接(ftp data port)就往 stdin 里面喂一堆随机数据。
Fuzz目标
针对 rtmp 协议进行 Fuzz
编写一个客户端程序,接受从 rtmp 服务器传来的视频流
测试 avformat_open_input() 这个函数
收集样本
编写一个 FFmpeg 的程序,从 rtmp 服务器获取视频流,使用 wireshark 抓包 寻找不同的 rtmp 源,调整客户端程序的参数来获取不同的 corpus 样本自己写一个 rtmp 协议的字典。
参考资料
CVE: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-11665
LibFuzzer: https://llvm.org/docs/LibFuzzer.html
Preeny: https://github.com/zardus/preeny
google OSS-Fuzz: https://github.com/google/oss-fuzz/