符号实施,从漏洞扫描到自动化生成测试用例
发布时间:2021-06-09 09:53:07 所属栏目:安全 来源:互联网
导读:背景 ThoughtWorks安全团队曾经在可信Frimware领域做了一些探索和研究。背景大概是这样的:整车制造过程中,常常会引入供应商的部分设备,如车载娱乐系统,但是出于知识产权的原因,这些供应商很难提供完整的源码给整车制造方,因此二进制的固件就成了整车制
背景
ThoughtWorks安全团队曾经在可信Frimware领域做了一些探索和研究。背景大概是这样的:整车制造过程中,常常会引入供应商的部分设备,如车载娱乐系统,但是出于知识产权的原因,这些供应商很难提供完整的源码给整车制造方,因此二进制的固件就成了整车制造环节中的安全隐患,各种漏洞都可能被供应商的零部件引入,存在于车载系统之中,随时可能被攻击者利用而影响整车的安全性。
为了探测二进制程序中的漏洞,经过一段时间的探索和研究后,把核心技术锁定到了符号执行,利用该技术帮客户搭建了一套自动化的二进制漏洞扫描平台。并且,在后来不断的研究中,我们发现,符号执行也可以用来自动化生成测试用例,为我们更加全面的编写测试用例, 带来新的思路。
什么是符号执行
Wikipedia上对符号执行的解释:是一种程序分析技术,其可以通过分析程序来得到让特定代码区域执行的输入。使用符号执行分析一个程序时,该程序会使用符号值作为输入,而非一般执行程序时使用的具体值。在达到目标代码时,分析器可以得到相应的路径约束,然后通过约束求解器来得到可以触发目标代码的具体值。
讲的比较绕,举个通俗的例子来说明:假设程序现在是一个王者荣耀中的英雄,这个英雄经过一定的出装就会有一定的战力(攻速,物理伤害,防御等),符号执行的技术就是,给出了一个英雄的战力,可以反推出什么样的出装可以达到这样的战力。
再举个实际的代码例子来说明符号执行:
void foo(int x, int y)
{
int t = 0;
if( x > y ){
t = x;
}else{
t = y;
}
if (t < x ){
assert false;
}
}
假设当t<x时,是我们程序的漏洞,我们要使用符号执行判断是否有达到t<x这个分支的可能。符号执行的方法就是在给定的时间内,生成一组输入,以尽可能多的探索所有的执行路径,在分析时,该程序会使用符号值作为输入,而非具体的值,去探索每一条分支。比如该程序在符号执行完后,会生成如下类似的方程组:
(x>y) => t=x
(x<=y) => t=y
接下来,符号执行会通过约束求解,去分析上述的每条路径,通过约束求解分析得,如上的两条路径在任何情况下都不可能达到t
使用符号执行进行漏洞扫描
那我们是如何把符号执行运用在自动化漏洞扫描的场景上?
首先要说明的是,我们要扫描的对象是Linux kernel,对于kernel来说有很多已知的CVE漏洞,我们的任务就是去发现二进制的kernel上是否存在这些CVE漏洞。思路如下:
通过一些简单的逆向,得到该内核的版本。
有了内核版本,就可以得到内核的源码,以及该内核版本对应的所有CVE漏洞和补丁。
给内核源码打上所有的CVE补丁,在二进制层面diff前后的补丁,对每个补丁提取唯一的特征(漏洞指纹)。
用漏洞指纹与目标kernel进行对比,扫描得到最终的漏洞列表。
由上述可知,提取漏洞的唯一特征是最重要的一步,接下来介绍如何使用符号执行来提取漏洞指纹。
首先介绍两个基本的概念BB(basic block)和CFG(control flow graph):BB是指从汇编的角度来看程序,一段连续的汇编指令就是一个BB,这段连续的汇编仅仅包含一个入口和一个出口,换句话说,BB内部不会有分支和跳转。由此我们可以得出,一个程序,是由一堆的bb组成的,它们之间有复杂的调用和跳转关系,最终形成了一张图,这个图就是CFG。例如下图是一个简单的CFG:
有了这两个概念,我们就可以对漏洞进行唯一的特征描述了。
漏洞指纹特征
由上面可知,CFG其实表示了一段程序执行的所有路径,而符号执行的第一步就是去探索所有的执行路径。如果您了解过内核的CVE漏洞,就会发现内核很大一部分的CVE漏洞补丁,就是在一些关键的代码上加了一些if分支和判断。例如CVE-2019-19252的补丁如下:
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
index 1f042346e7227..778f83ea22493 100644
--- a/drivers/tty/vt/vc_screen.c
+++ b/drivers/tty/vt/vc_screen.c
@@ -456,6 +456,9 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
size_t ret;
char *con_buf;
+ if (use_unicode(inode))
+ return -EOPNOTSUPP;
+
con_buf = (char *) __get_free_page(GFP_KERNEL);
if (!con_buf)
return -ENOMEM;
该补丁只是在vcs_write的函数中添加了一个if判断,对于这类补丁,在使用符号执行生成CFG的时候,前后肯定会出现一个明显的差异,因为多了一个分支,整个的程序流图也就多了一个分支。对于这种类型的补丁,使用CFG就可以作为漏洞的特征,通过对比发现,前后的CFG不一样,就说明漏洞存在。
![]() (编辑:帝国网站管理系统) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |