符号执行,从漏洞扫描到自动化生成测试用例

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"背景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ThoughtWorks安全团队曾经在可信Frimware领域做了一些探索和研究。背景大概是这样的:整车制造过程中,常常会引入供应商的部分设备,如车载娱乐系统,但是出于知识产权的原因,这些供应商很难提供完整的源码给整车制造方,因此二进制的固件就成了整车制造环节中的安全隐患,各种漏洞都可能被供应商的零部件引入,存在于车载系统之中,随时可能被攻击者利用而影响整车的安全性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了探测二进制程序中的漏洞,经过一段时间的探索和研究后,把核心技术锁定到了符号执行,利用该技术帮客户搭建了一套自动化的二进制漏洞扫描平台。并且,在后来不断的研究中,我们发现,符号执行也可以用来自动化生成测试用例,为我们更加全面的编写测试用例, 带来新的思路。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"什么是符号执行"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Wikipedia上对符号执行的解释:是一种程序分析技术,其可以通过分析程序来得到让特定代码区域执行的输入。使用符号执行分析一个程序时,该程序会使用符号值作为输入,而非一般执行程序时使用的具体值。在达到目标代码时,分析器可以得到相应的路径约束,然后通过约束求解器来得到可以触发目标代码的具体值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讲的比较绕,举个通俗的例子来说明:假设程序现在是一个王者荣耀中的英雄,这个英雄经过一定的出装就会有一定的战力(攻速,物理伤害,防御等),符号执行的技术就是,给出了一个英雄的战力,可以反推出什么样的出装可以达到这样的战力。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再举个实际的代码例子来说明符号执行:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"void foo(int x, int y)\n{\n int t = 0;\n if( x > y ){\n t = x;\n }else{\n t = y;\n }\n if (t < x ){\n assert false;\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假设当"},{"type":"codeinline","content":[{"type":"text","text":"ty) => t=x\n(x<=y) => t=y"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来,符号执行会通过约束求解,去分析上述的每条路径,通过约束求解分析得,如上的两条路径在任何情况下都不可能达到"},{"type":"codeinline","content":[{"type":"text","text":"tep;\nstruct sctp_transport *transport = NULL;\nstruct sctp_sndrcvinfo _sinfo, *sinfo;\n- struct sctp_association *asoc;\n+ struct sctp_association *asoc, *tmp;\nstruct sctp_cmsgs cmsgs;\nunion sctp_addr *daddr;\nbool new = false;\n@@ -2053,7 +2053,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)\n\/* SCTP_SENDALL process *\/\nif ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP)) {\n- list_for_each_entry(asoc, &ep->asocs, asocs) {\n+ list_for_each_entry_safe(asoc, tmp, &ep->asocs, asocs) {\nerr = sctp_sendmsg_check_sflags(asoc, sflags, msg,\nmsg_len);\nif (err == 0)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于这种漏洞补丁,没有分支上的增减,只是改变了一个函数的入参个数,那么补丁前后的CFG可能是一样的,所以我们就不能仅仅通过CFG来判断补丁是否存在,必须加上在语义上的分析,语义即这个参数对函数的整体影响。这就引出了符号执行的另一步:约束求解,前面我们提到符号执行会对所有路径形成类似方程组的概念,然后使用约束求解器求出到达每个路径的解的集合。如果其中某些变量发生了改变,其最终的解一定是不一样的,以此作为漏洞标识的另一个特征。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"漏洞扫描总结"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以最终,我们是采用符号执行从CFG和语义分析两个维度来唯一的确定一个漏洞的特征,然后用这个唯一的特征去目标的kernel中对比。以此来确定补丁是否已经存在。这个就是我们检测二进制漏洞的关键技术,大致流程如下图:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/b0\/b0af306e735f9e610b5aca62def3892e.png","alt":"图片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在整个过程中,我们会使用开源的符号执行引擎和约束求解器,比如Angr和Z3。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"符号执行的其他应用场景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面是符号执行在漏洞提取和扫描的一个案例,除此之外,符号执行在漏洞挖掘,CTF等方面也有比较广泛的应用。例如如下程序是我用Ghidra逆向的一道CTF的题目:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"int verify(EVP_PKEY_CTX *ctx, uchar *sig, size_t siglen, uchar *tbs, size_t tbslen)\n{\nbyte bvar1;\nint local_c;\nlocal_c = 0;\nwhile(true) {\nif(ctx[(long)local_c] == (EVP_PKEY_CTX)0x0) {\nreturn (int)(uint)(local_c == 0x17);\n}\nbVar1 = (byte)local_c;\nif(encrypted{(long)local_c} != (byte)(((byte)((int)(uint)(bVar1 ^ (byte)ctx[(log)local_c]) \\\n>> (8-((bVar1 ^9)&3) & 0x1f)) | (bVar1 ^ (byte)ctx[(long)local_c]) << ((bVar1 ^9) & 3))+8)){\nbreak;\n}\nlocal_c = local_c + 1\n}\nreturn 0;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以发现,其核心关键是去破解这个加解密的算法(异或,加减等操作),如果人工逆向,可能需要很长时间的推算和尝试,而符号执行则可以自动的去不断尝试每个路径的解,直到算出一个自己需要的值。有兴趣的读者,可以使用angr和z3去做一下这个CTF的破解,非常容易,这里不再赘述。需要说明的是,在破解和CTF中,符号执行往往和IDA\/Ghidra等工具来配合使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一方面是在测试领域,在单元测试中代码覆盖率往往被用于评估代码的测试充分性水平,在软件工业界,人工设计测试用例的方法被广泛使用,即依靠人对程序代码的理解设计测试用例,但对应的人力成本很高,有时候为了降低人力成本且提高自动化程度,随机测试的方法也被常常使用,但一般只能检测到有限的程序行为,容易遗漏软件错误。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在单元测试中,常用的白盒测试的充分性准则大多属于基于控制流的覆盖准则,如语句覆盖,分支覆盖和MC\/DC覆盖等。而测试准则的选取一般根据实际的测试需求而确定,比如,传统软件的测试一般要求实现尽可能高的语句覆盖和分支覆盖,而对于航天,轨交等控制软件一般要求代码满足100%的分支覆盖。而这种同时实施多种测试标准的需求,进一步加大了单元测试的工作量和难度, 使得单元测试在实际软件开发中往往被忽略,最终导致软件缺陷没有在早期被及时发现。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而符号执行的特点是会尽可能的遍历每条路径,每一次符号执行的结果等价于大量的测试案例。符号执行为软件的各种情况自动生成了有效的输入,覆盖率高,可以更加容易检测到程序是否存在缺陷和错误。所以,其实我们可以运用符号执行生成测试用例。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前学术界有不少的论文研究如何使用符号执行自动化生成更好的测试用例。也有一些有意思的demo,可以让您体验:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"C语言:https:\/\/github.com\/Sajed49\/C-Path-Finder"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java语言:https:\/\/github.com\/kaituo\/sedge"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"总结"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上是我们对符号执行的一些探索,欢迎您与我们一起进行更加深入的研究。随着大家对安全的越来越重视,基于符号执行的漏洞扫描,自动测试,fuzz测试等越来越受到人们的重视。2019年美国《国防法》National Defense Act的H.R.5515—517 就推荐使用二进制分析和符号执行工具来增强关键软件系统的安全。目前,ThoughtWorks安全团队正在积极探索符号执行在安全领域的威力,通过自动生成测试用例,再结合模糊测试(fuzz)工具,集成到DevOps的环境中,在整个软件的开发周期内持续的发现安全漏洞,为软件安全保驾护航。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文转载自:ThoughtWorks洞见(ID:TW-Insights)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文链接:"},{"type":"link","attrs":{"href":"https:\/\/mp.weixin.qq.com\/s\/4IYgE3fybBg7vJVlrB6SDg","title":"xxx","type":null},"content":[{"type":"text","text":"符号执行,从漏洞扫描到自动化生成测试用例"}]}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章