Bochspwn漏洞挖掘技术深究(1):Double Fetches 检测
虽然现在技术文章很少人看,大家都喜欢聊安全八卦,但技术文章输出是一种很好的学习方式。更重要的是,专业的文章是给专业的人看的,并非为了取悦所有人。
对于应用程序的代码插桩,有现成的Pin和DynamoRIO插桩框架,在Fuzzing中可以用来实现代码覆盖率的反馈驱动,这已经被应用到winafl,效果很好。除了挖洞,在逆向工程领域应用也很广泛。
上面都是针对应用层的,内核层的,上面的Pin和DynamoRIO就派不上用场了,对于这种系统内核级的指令插桩,有时就会采用虚拟化技术为实现,比如通过Qemu或Bochs虚拟机。
ProjectZero的j00ru大神就用bochs的插桩API为实现针对内核double fetches的监测,项目称为bochspwn,后来又采用污点追踪方式检测未初始化漏洞导致的内核信息泄露,叫bochspwn-reloaded。
Bochs Instrument API 文档参考:http://bochs.sourceforge.net/cgi-bin/lxr/source/instrument/instrumentation.txt ,在编译bochs时指定插桩代码目录:
1 | ./configure [...] --enable-instrumentation="instrument/myinstrument" |
下面是bochspwn中用到的API:
1 | // Bochs初始化CPU对象时的回调函数 |
bx_instr_initialize用来加载配置信息,针对不同的系统环境设置不同的数据结构偏移地址,用来提供需要的进程/线程等重要信息:
1 | [general] |
Bochspwn的核心功能实现就在于bx_instr_lin_access
与bx_instr_before_execution
两个函数。先看下bx_instr_before_execution
的实现逻辑:
- 忽略实模式real mode
- 忽略无关的系统调用中断指令,仅允许
int 0x2e
与int 0x80
- 获取当前进程/线程ID相关的信息,当发现漏洞时方便重现
1 | void bx_instr_before_execution(unsigned cpu, bxInstruction_c *i) { |
再看下bx_instr_lin_access
实现逻辑:
- 忽略仅读写指令
- 检测CPU类型(32位或64位)
- 判断当前指令地址pc是否为内核地址,判断访问的线性内存地址是否为用户层地址
- 检测读取的内存长度是否处于0~16字节之间,长度大小范围在config.txt中配置,仅处理此范围内的指令操作
- 通过上述条件之后,就代表可能存在内核漏洞,然后反汇编指令,然后填充日志记录信息
1 | void bx_instr_lin_access(unsigned cpu, bx_address lin, bx_address phy, |
信息记录方式都是通过invoke_system_handler函数去处理自定义系统事件,目前主要支持4种操作系统(windows\linux\freebsd\openbsd),macOS还没搞过,原作者是说想继续实现macOS,这个值得尝试开发下:
1 | const struct tag_kSystemEventHandlers { |
最后就是输出记录的信息,比如作者发现的CVE-2018-0894漏洞信息:
1 | ------------------------------ found uninit-copy of address fffff8a000a63010 |