ASan
Address Sanitizer(简称ASan)是一个用于C/C++的内存错误检测。开启之后,会在目标代码的关键位置,如:malloc(), free(),栈上buffer分配等处检查代码,一旦发生内存访问错误,如堆栈溢出、UAF、double free等等,它就会发出错误的报告。它可以检测到一以下的问题:
- use after free
- heap buffer overflow
- stack buffer overflow
- gloable buffer overflow
- use after return
- use after scope
- initialization order bugs
- memory leaks
由于有些内存访问错误并不一定会造成程序崩溃,如越界读,因此在没有开启ASan的情况下,许多内存漏洞是无法被AFL发现的。所以,编译目标二进制代码时,开启ASan,也是推荐的做法。对于使用afl-xxx编译来说,只需要设定环境变量AFL_USE_ASAN=1即可。
不过,由于开启ASan后fuzzing会消耗更多内存,所以这也是需要考虑的因素之一。对于32位程序,基本上800MB即可;但64位程序大概需要20TB!所以,如果要使用ASan,建议添加CFLAGS=-m32指定编译目标为32位;否则,很有可能因为64位消耗内存过多,程序崩溃。
如果使用了ASan,还需要注意为afl-fuzz通过选项-m 指定可使用的内存上限。一般对于启用了ASan的32位程序,-m 1024即可。
ASan原理
ASan的原理是利用额外的内存标记可用的内存。这部分额外的内存被称作shadow memory(影子区)。ASan将1/8的内存用作shadow memory。使用特殊的magic num填充shadow memory,在每一次load/store(load/store检查指令由编译器插入)内存的时候检测对应的shadow memory确定操作是否是valid。连续8bytes内存使用1 byte shadow memeory标记。如果8 bytes内存都可以访问,则shadow memeory的值为0;如果连续n(1<=n<=7)bytes可以访问,则shadow memory的值为n;如果8 bytes内存访问都是invalid,则shadow memory的值为负数。以UAF漏洞作为例子分析,观察shadow memory的变化。
程序开始申请了一个对,大小为0x14,内存的起始地址为:0xf6100be0

第一次___asan_report_store4,此时shadow memory的布局如下:

在0x3ec2017c这个地址上有3 bytes的shadow memory是可访问的。但是随着后面操作的检测,会发现shadow在不断的变化 。
第二次___asan_report_store_n

第三次___asan_report_store1

进行___asan_report_load8

然后调用free函数释放内存
|
|
此时shadow memory的布局改变了,原来的0400被fdfd填充了,检测到shadow memory发生了异常

第二次调用___asan_report_load8是就会把shadow memory poison的信息report,如果此时在使用这块free的空间就会报错

ASan算法
ASan的内存检测的原理之一是编译时插桩。在编译时会插入检测代码,检查每个内存访问的影子状态,ASan会在局部数组、局部变量、全局变量前后填充特定字段,然后在程序运行过程中,ASan会对这些特定字段进行检查,确定是否发生内存越界。为了检测对全局和堆栈对象的溢出访问,ASan会在这些对象的周围创建毒性的区域(redzones)。
对于全局变量,在编译时创建redzones。并且在运用程序启动是将redzones的地址传递到运行时库。运行时库函数会标记并记录地址以进一步报告错误。
为了捕获堆栈缓冲区溢出,AddressSanitizer检测如下代码:
Original code:
Instrumented code:
|
|
当检测8字节内存访问时,ASan计算相应影子字节的地址,加载该字节,并检查其是否为零:
|
|
当检测1,2或4字节访问时,检测则稍微的复杂:如果阴影值为正(即,只有8字节中的前k个字节是可寻址的),我们需要比较k与这个字节的后3位。
|
|
64位
Shadow = (Mem >> 3) + 0x7fff8000;
[0x10007fff8000, 0x7fffffffffff] | HighMem
[0x02008fff7000, 0x10007fff7fff] | HighShadow
[0x00008fff7000, 0x02008fff6fff] | ShadowGap
[0x00007fff8000, 0x00008fff6fff] | LowShadow
[0x000000000000, 0x00007fff7fff] | LowMem
32位
Shadow = (Mem >> 3) + 0x20000000;
[0x40000000, 0xffffffff] | HighMem
[0x28000000, 0x3fffffff] | HighShadow
[0x24000000, 0x27ffffff] | ShadowGap
[0x20000000, 0x23ffffff] | LowShadow
[0x00000000, 0x1fffffff] | LowMem
ASan的用途
- use after free
example1.c
|
|
编译:afl-gcc uaf.c -o uaf -m32 -fsanitize=address
直接运行,它会自动打印检测到的内存错误。
|
|
根据打印的结果可以看到,程序提示uaf.c第31行有个heap-use-after-free的错误,而且在最后还有个summary,把出错的代码位置和相应的栈信息打了出来。
- double-free
example2.c
|
|
在上面的代码上稍微改了下,使其存在double free漏洞。使用相同的命令编译程序,运行观察程序的结果:
|
|
在程序的第34行存在double-free漏洞
- off-by-one
example3.c
|
|
编译后运行会提示存在heap-buffer-overflow漏洞
|
|










































