1. 概述
Karonte是一个用与静态分析固件设备中多二进制程序漏洞的工具,主要由Python写成。
github地址:https://github.com/ucsb-seclab/karonte
docker镜像地址:https://hub.docker.com/r/badnack/karonte
Karonte发表在2020年的S&P顶会上。
这里主要通过阅读源码和论文来记录这个工具的大致流程。
2. Karonte Overview
这是Karonte的一个大致流程图,结合docker镜像,图中的Firmware固件二进制程序是在docker镜像中的,并不在该GitHub仓库中,而Karonte的结果是存放在一个Log文件中的。整个流程分为5步:
- 固件预处理:Karonte的输入由固件样本组成,这一步是使用binwalk解压固件镜像。
- 发现边界二进制文件:指出程序中哪儿引用了攻击者控制的数据。
- BDG图构建
- 多二进制数据流分析
- 不安全的交互检测
3. 整体流程
定义了一个Karonte类,有一个方法为run,定义Karonte类得时候会从预设的配置文件JSON读取配置信息,可以自定义Log文件的位置,否则会随机生成具有Karonte前缀的Log文件。
Karonte中实例化了一个类BorderBinariesFinder,用于寻找边界二进制文件,其中固件地址(self._fw_path)是通过Karonte类的实例化时传入的。
如果没有找到任何边界二进制文件,则退出,提示结果存放在Log文件中。
找到了边界二进制文件,则实例化类BinaryDependencyGraph用于构建二进制依赖图,里面就包含关键的计算解析值(parsing score)的算法,下面会详细说。
最后将构建好的BDG传入实例化的BugFinder,同时还有分析的前后继承关系。开始寻找Bug,将结果存储到Log文件中去。
4. BDG算法
CPF:通信范式查找器,用于对进程间的通信进行建模,主要功能有Data Key Recovery 、Flow Direction Determination、Binary Set Magnification。
BDG:二进制依赖图,是对数据流进行建模后形成的一个非连通循环有向图。
给定一组边界二进制文件,KARONTE构建一个二进制相关性图(BDG),它是一个有向图,该图对那些处理受攻击者控制的数据的二进制文件之间的通信进行建模。 通过利用通信范例查找器(CPF)模块的集合来迭代地恢复BDG,这些模块能够推理出不同的进程间通信范例。
BDG算法的具体描述见上图,文字描述如下:
其中的参数fw
代表解压后的固件样本,B
代表前面通过计算parsing score
得到的边界二进制文件(Border Binary),int_locs
是一组内存比较所在的程序点。
首先,对于B中的每一个二进制文件b,我们考虑既属于int_locs
,也属于b的locs
(位置,这个通过get_locs
得到);
然后对于locs中的每一个loc,利用静态污点分析引擎来引导从包含loc的函数的序言开始的符号路径探索(对应函数explore_paths
),当分析到loc的时候,我们将引用的内存位置buf污染,即,将内存位置与network-encoding关键字进行比较(对应函数get_buf
和apply_taint
)。
在路径探索的每个步骤中(即,针对每个访问的基本块),我们调用每个CPF模块,该模块分析当前路径并使用污点信息(由污点分析引擎在路径探索过程中传播)来检测是否二进制b正在共享某些污染数据d。如果CPFp匹配(即,它检测到所分析的二进制文件依赖于通信范式p共享某些数据),则我们利用CPFp恢复使用中的通信范式实例的所有详细信息。更准确地说,CPFp通过p恢复用于共享数据的数据键k,并推断k的二进制文件的角色(即setter或getter)(对应函数 find_data_key_and_role
),并在固件样本中查找可能通过此通道进行通信的样本(对应函数 get_new_binaries
)。
然后将新发现的二进制文件添加到整个二进制文件集中进行分析。注意,当计划对这些新二进制文件Bnew中的任何一个进行分析时,分析必须知道最初在何处应用污点。换句话说,我们必须检测共享数据在这些新二进制文件中最初引入的位置。因此,对于每个新添加的二进制ba,CPFp还将检索引用数据密钥k的程序点int_locs_new
,并将它们添加到int_locs
中。最后两个操作由功能更新二进制文件执行。最后,对于每个分析的二进制b,我们考虑在某个键k上与b匹配的每个CPF(cp),并使用cp来检索b对k的作用(例如,setter)。
然后,我们在b与其他与k的b作用相反的其他二进制文件(例如getter)之间创建一条边。
最后,回到例子1:
第三行是一个内存比较的位置,推测p
可能会用于内存比较,从函数parse_URI
(第1行)开始对它指向的内存位置进行污染,并引导过程内污染分析探索,并通过遵循该控件来传播污染该程序的流程。 当污点浏览到达execve
函数调用(第13行)时,环境CPF范式检测到正在执行另一个二进制文件,并且setenv
函数用于设置数据键QUERY_STRING
。 因此,环境CPF范式确定分析中的二进制文件是QUERY_STRING
的设置器。 然后,环境CPF范式扫描固件样本并根据相同的数据密钥找到其他二进制文件,并将它们添加到二进制文件集中进行分析。 最后,对于每个新添加的二进制文件,环境CPF检索引用数据键QUERY_STRING
的代码位置(例如,对函数getenv("QUERY_STRING")
的调用)。
5. 总结
程序中内存比较的信息不可避免地需要硬编码,因此可以根据这些硬编码的信息来确定内存比较的位置。而在固件的漏洞分析过程中,针对没有源码的二进制文件,其中的Log信息也是硬编码的信息,或许可以通过这些信息来定位关键函数的入口,进一步分析。
6. 其他
karonte运行截图:
结束:
Role、buf、roleinfo定义:
CPFs的定义:
maybe, to be continued……