Beacon 实验
Last updated
Last updated
Beacon 实验是指将目标软件编译成 .bc 文件后再喂给 fuzzer 的过程。基础知识主要涉及到 Beacon & LLVM。bitcode 是 LLVM IR 的二进制编码(IR 是前后端的一种中间层),主要包括 bitstream container format 和 encoding LLVM IR,前者是为后者如何进行编码提供帮助的。bitstream 和 LLVM IR 有各自的格式,用不同的参数去刻画文件,在过程中可通过 Abbreviations ID 来大致理解目前的文件流。
本实验是针对 Beacon 论文中提到的 51 个 CVE 进行复现,测量其性能,并与自身 fuzzer 进行对比。 参考文档:LLVM & Beacon document
以下按流水线的方式,记录自己踩过的坑以及对应的 solution。
首先配置一个密钥,放在 authorized_keys 下,然后配置 config,实现优雅的 ssh <hostname>
的 remote login;
实验环境要求在 podman 下进行(类 docker)。首先拉取对应镜像,podman run -d --init --name beacon docker.io/yguoaz/beacon:latest sleep infinity
,在拉取的过程中可能会遇到 insufficient pids or uids 的错误,可通过 cat /proc/self/uid_map
、cat /proc/self/gid_map
来查看相应的情况。
解决方案——添加参数 --storage-opt ignore_chown_errors=true
。成功拉取 image 后,进入容器,为了编译得到 Clang bitcode ⽂件,需要使⽤ wllvm,通过 python3 -m pip install wllvm
安装。为了通过 tmux 运⾏ Beacon AFL(使 shell 与进程分离,类 nohup),需要使用 tmux,通过apt update; apt install tmux
安装。至此,初期的准备工作基本完成;
可通过查询 NVD 的方式(以 CVE-2017-7578 为例),访问 https://nvd.nist.gov/vuln/detail/CVE-2017-7578
,即可获取该 CVE 的相关信息。重点查看下方的 hyperlink,一般情况下可获得关于源码的指向。而后采用 git clone 的方式,获取源码,并用 git checkout 的方式切换到该 CVE 对应 patch 的前一个 comitID,以对该 CVE 进行下一步测试。注意,若有连续多个 commit 对该 CVE 进行 patch,应回退到最早 commit 之前的版本。
查看源码路径下 README / INSTALL 之类的文件,获取编译该目标软件的方法,注意在编译的过程中要传递 wllvm 环境变量。一种可能的编译方式如下:
当然也可能需要使用 cmake 方案,具体要求参见 README / INSTALL 文件(比较玄学的一点是,对于某类编译方式,有可能一次编译不成功,但是经过多次尝试后就成功了)。编译成功后,得到对应的可执行程序,再使用 extract-bc <target_name>
获取 *.bc 文件;
使⽤ Beacon 提供的⽂件分析⼯具对 bc ⽂件进⾏处理
对于一个 *.bc 文件,需要提供一个分析目标文件,可建⽴⼀个 target.txt ⽂件并写⼊定向⽬标(某⽂件的某⼀⾏)。对于目标的定向,可考虑以下四种角度:
参考 NVD 官网的 Discription 部分,看该 CVE 是由于哪个文件的哪个漏洞函数触发的;
观察 patch 部分的增删;
参考 github 下的 issue;
google 相关信息。
使用 /Beacon/precondInfer <target_name>.bc -target-file=target.txt -join-bound=5
,对漏洞程序进行分析。若遇到 malformed 的错误,可尝试调整 -target-file
与 -join-bound
这两个参数,采用排列组合的方式试错。紧接着,通过 /Beacon/Ins -output=<target_name>.bc -byte -blocks=bbreaches_target.txt -afl -log=log.txt -load=range_res.txt transed.bc
对漏洞程序进行插桩。最后,重新编译插桩后的漏洞程序,clang <target_name>.bc -o <target_name> -lm -lz /Beacon/Test/afl-llvm-rt.o
。因为之前设置了 ./configure --disable-shared
,所以可能导致重编译时,缺少相关的链接文件。此时可以使用 ldd 命令手动查看可执行文件的链接文件,而后手动补齐。也可以通过下载源代码,进一步编译共享库来解决这个问题;
启动一个 tmux 用于持续运行,tmux -u new -s <tmux_name>
,进入模糊测试路径,并准备测试用例文件夹 in。对于 initial input,可参考以下顺序:
而后使用 taskset -ac 4 /Beacon/afl-fuzz -i in -o out -m none -t 9999 -d -- ./<target_bin> @@
,其中,-ac 用来绑定 cpu,可提前使用 htop 命令查看 cpu 的具体使用情况。对于 -- 后面的参数,请参考 aflgo github 仓库中提供的 *.sh。
对于在 24h 内能成功 fuzzing 出 crash 的 CVE,计算其首次触发 crash 的时间。通过时间戳进行计算,具体参考以下脚本:
对于 fuzzing 出的 crash,进一步考察 beacon 对 target.txt 中提到的 vul 路径的定向能力,并计算首次成功匹配 vul 并确认执行的时间,具体参考以下脚本:
其主要思路为,用 pin 记录下所有执行地址,用 objdump -t 的方式提前从符号表中获取对应 func 的 address,最后用 grep 的方式进行匹配。在脚本运行的过程中,请注意程序是否陷入死循环,若出现此类情况,请及时退出并删除 crash,避免浪费大量时间。至此,实验结束。
折腾即真理,在实验过程中反复被虐,不断寻求解决方案,从而拓展能力边界。也许人类进化的本质就是,在遇到某个问题要求的能力超出目前的可达范围,但是踮起脚尖又勉强能够到的情况下,大脑会申请一个进程,不断计算、进化,直到成功求解 / 中途手动 quit 该问题。晚上睡不着估计也是同理,大脑飞速运转,不停申请、调度资源,不允许后台运行,对褪黑素的敏感度下降,导致迟迟无法入眠……做这个实验三周以来,基本没怎么好好睡过觉。 还有几点比较深的感受:将文件夹结构化;降低对事情进展的预期,因为每天都会碰上新问题;对于大量测试用例,注意构建自动化脚本。