[afl-training] date
date 的这个漏洞也是使用 AFL 发现的。通过查看 date 的 man 手册,可以看到 date 可以从命令行、日期相关系统调用、环境变量和一些文件中读取输入。此次 challenge 是如何对一个程序的环境变量进行 fuzz,个人还是比较重视这个 challenge,因为我的毕业设计是打算对 IoT 固件中的 CGI 程序进行模糊测试,而 CGI 程序大多是从环境变量以及标准输入 STDIN 中获取数据,然后处理完毕后通过标准输出 STDOUT 输出,做完这个 challenge 应该就可以开始进行毕业设计的总体实现了。
编译 date
首先对 date 的源码进行编译,和之前的 challenge 类似,需要使用 afl-clang-fast 以及开启 AFL_USE_ASAN=1 编译选项。
进入 challenge 中的 date 目录,下载源码,并编译时必要的依赖。
1 | git submodule init && git submodule update |
编译 date
1 | cd coreutils |
运行编译出来的带有 bug 的 date 程序
1 | ./src/date |
目前是已知 TZ 环境变量存在 bug,那么运行 poc,ASAN 报错发生堆溢出
1 | TZ="aaa00000000000000000000aaaaaab00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ./src/date --date "2017-03-14 15:00 UTC" |
harness
那么问题就是如何对环境变量进行 fuzz。在每一个 challenge 中都有一个 HINT.md 文档,作为对当前挑战的提示。对于如何 fuzz 环境变量,HINT.md 提出了三个方案:
- 在源码中找到所有读取环境变量 TZ 的地方,然后替换为从 STDIN 中读取
- 修改 main 函数,在运行之初就设置 TZ 环境变量从 STDIN 中读取
- 使用 LD_PRELOAD 环境变量对 getenv 函数进行劫持,这样就可以通过标准输入传递到环境变量的值
在 ANSWERS.md 中,推荐使用的是第二个方案,因为第一个方案需要对代码中每一个读取环境变量的地方进行修改,很难确定每个地方都替换了为了 STDIN;第三个方案虽然重用性比较高,但是对于入门而言,需要花费的功夫还是比较多的。我在毕设中应该会使用到第三种方案,因为这样可以最大限度不对 CGI 的程序做出修改。
使用第二个方案的一个原因也是,date.c 代码中,只有一个使用到了 getenv(“TZ”),那么在 main 函数运行之初,就提前设定好 TZ 环境变量从 STDIN 中读取,从而实现 fuzz 环境变量的值从标准输入中读取。在 main 函数中增加如下:
1 | static char val[1024 * 16]; |
然后重新编译:
1 | make clean |
然后重新运行程序,可以看到每次 date 运行前都要从标准输入先获取 TZ 环境变量,修改成功
开始 fuzz
设置初始种子,可以就用上面的 Europe/London 作为初始种子:
1 | mkdir input |
fuzz 前需要注意,在教程中使用的是固定日期,并且如果使用 ASAN,需要设置内存限定:
1 | afl-fuzz -m none -i ./input -o output -- ./src/date --date "2017-03-14 15:00 UTC" |
运行了 1 个小时,玩了一会儿游戏,一共挖出 30k 个 crash,但是只有 3 个 unique crashes。
将 crash 传入到 date 运行,ASAN 报错如下:
的确是发生了溢出