"迈向实用的二进制代码相似性检测:通过补丁语义分析进行漏洞验证"
0x0 Abstruct
二进制代码相似性检测方法可以有效地搜索二进制软件中代码共享引入的重复出现的漏洞(1day)。然而,这些方法存在较高的误报率(FPR),因为它们通常将修补的函数视为易受攻击的函数,并且当使用不同的编译设置编译二进制文件时,它们通常不能很好地工作。为此,提出了一种名为Robin的方法,通过过滤掉已修补的功能来确认重复出现的漏洞。Robin由轻量级符号执行提供支持,以解决可能导致漏洞相关代码的函数输入集。然后,它使用相同的输入执行目标函数,以捕获易受攻击或修补的行为,以进行修补函数过滤。实验结果表明,Robin对 10 个不同软件的 287 个真实漏洞分别在不同编译器和编译器优化级别上实现了高精度补丁检测。基于准确的补丁检测,Robin显着降低了先进漏洞检测工具的误报率(平均降低了 94.3%),使其更加实用。Robin还检测到 12 个新的潜在易受攻击的函数。
关键词:补丁;相似性检测;
- 1day特征:代码重用和共享。
0x01 Introduction
在针对检测1day漏洞方面,静态方法由于动态方法,因为产生的误报率(FRR)更低。二进制代码相似性方法的研究目的是检测更少的被忽视的漏洞。主要思想是通过从易受攻击的函数中提取各种签名,找到相似的函数并将它们视为潜在的易受攻击的函数。该方法能够有效覆盖整个程序的所有功能,然而,现有的方法更多地侧重于提高功能匹配结果的准确性,以精确检测包含漏洞的候选功能。漏洞匹配结果是补丁和漏洞功能的混合题,很难能区分,需要专家更加仔细和费力的确认。
基于现有功能匹配工具可能容忍席位的补丁代码更改的局限性,以及当前补丁检测方法的缺点,提出了补丁检测方法应具备的四个关键能力,以有效验证二进制漏洞。
- C1 即使在没有源代码级调试信息的情况下也能够检测二进制函数。
- C2 有效处理大型二进制程序的可扩展性。
- C3 精确识别已修补的功能和易受攻击的功能。
- C4 通过考虑语义差异而不仅仅是语法差异,跨不同编译优化准确检测补丁。
ROBIN是一种机遇语义的漏洞确认工具。
对于 C1, Robin通过使用差异技术在二进制级别比较易受攻击的函数和已修补的函数来识别修复漏洞的修补代码。此外,Robin对二进制函数采用符号执行来提取补丁的语义特征,从而能够检测目标二进制函数中的补丁或漏洞。Robin利用一种称为恶意函数输入(MFI )的轻量级符号执行技术来有效地生成函数输入,驱动从函数入口点到修补函数或漏洞中的补丁代码的执行易受攻击的函数中的代码。对于 C3 和 C4,Robin集成了一个功能监视器,用于检查输入是否触发目标函数中相同的易受攻击的行为(例如,空指针取消引用( NPD ))或相同的修补行为,以确认漏洞或补丁的存在。(验证阶段)
此外,Robin从执行跟踪中捕获语义行为,并使用行为摘要(即语义特征)来确定目标函数是否存在漏洞或已修补。与 Fiber 对源代码和句法特征匹配的依赖相比,Robin仅从二进制文件生成补丁签名,并利用 MFI 提取可能的漏洞语义,而不需要句法信息。这使得Robin能够在不同优化级别的补丁存在检测中实现更好的性能。(补丁缓解优化)
总之,此项研究工作做出了以下贡献:
- 我们对基于功能匹配的漏洞检测工具进行了一项研究(第2.1节),以证明它们无法区分补丁和漏洞。
- 在这项工作中,我们创造性地提出了MFI,这是一种精心设计的函数输入,可将函数执行引导至已修补或存在漏洞的代码。此外,构建MFI并将其用于补丁检测不需要来自源代码的调试信息,这使得我们的工具在二进制补丁检测中更具可扩展性,因为补丁源代码并不总是可用。
- 我们通过使用 MFI 来检测补丁的存在并验证不同优化中的漏洞来实现原型Robin,并将其开源 [ 10 ]。
- 我们对 287 个现实世界的漏洞对Robin进行了评估,结果表明Robin可以在每个函数 0.47 秒内以 80.0% 的准确度识别目标修补函数。
- 我们对函数匹配输出的候选函数进行补丁检测。结果显示,检测后,前10名结果中功能匹配工具的FPR显着降低,平均降低了94.27%。此外,Robin还检测到 12 个新的潜在漏洞函数。
0x02 Assumptions
这项工作基于这样的假设:至少有一个不同的函数输入可以驱动执行到修补函数中的缓解点和易受攻击函数中的漏洞点。我们以图1中 CFG 上的函数执行为例。首先,我们假设一个函数输入我存在,它将执行到修补函数中的缓解点 (P2)(右侧的 CFG)。该函数输入未通过安全检查 (P1),并导致执行到错误处理块 (P2)。显然,修补块(P1)之前的修补函数中的代码和易受攻击块(V1)之前的易受攻击函数中的代码是相同的(即块C1、C2和C3)。因此,输入我还将漏洞函数中的执行驱动到C3,然后引导执行到漏洞点(V1)。我们选择块 P2 而不是 P3 作为缓解点,因为通过块 P2 的执行与易受攻击函数中的执行轨迹最不同。
0x03 ROBIN
3.1 ROBIN工作流程
Robin的核心是通过欠约束的符号执行构建MFI。Robin使用 MFI 生成语义特征,以证明存在漏洞代码或修补代码。在离线模式下,Robin将易受攻击的函数及其修补版本作为给定漏洞的输入。它根据这两个函数创建MFI和补丁签名。对于在线检测,Robin利用生成的 MFI 和签名来报告目标二进制函数是否已修补、存在漏洞或与漏洞无关。
首先 ➀,Robin比较易受攻击的函数和已修补函数之间的差异,以找到所有补丁块。其次,对于每个补丁块,Robin都会找到一条可行路径,其中包含构建函数输入,可以将函数驱动到补丁块。第三,它通过执行易受攻击和修补的函数来为每个函数输入生成签名,并根据函数签名选择最有特色的函数输入作为 MFI。最后➃,Robin将 MFI 提供给目标二进制函数来执行,并提取语义签名来确认漏洞或补丁。
3.2 补丁本地化
为了生成MFI,Robin首先需要确定修复漏洞的修补函数中的缓解点(例如,图1右侧CFG上的红色块)。由于缓解点是补丁块或补丁块的后续块,Robin首先通过比较易受攻击的函数和修补函数之间的差异来定位所有补丁块。此外,由于编译器在编译时会引入意外的变化,从而影响比较结果,因此我们在比较之前进行归一化以提高准确性。
3.2.1 CFG 标准化。
CFG规范化旨在减少编译器引入的副作用的影响。副作用,例如指令地址偏移和跳转目标地址的变化,使得指令在语法级别上不同,即使它们是从相同的源代码编译而来的。在比较函数时,这些编译器引入的更改被视为噪音。在这项工作中,Robin应用了四种不同的指令规范化规则:(i) 寄存器替换、(ii) 内存引用替换、(iii) 常量替换和 (iv) 地址替换。The examples are listed below:
(i)Register replacement replaces all register names with “REG”: “push ebp” -> “push REG”.
(ii)Memory reference replacement replaces all memory references with “MEMACC”: “mov eax, [ebp-0x10]” -> “mov eax, MEMACC”.
(iii)Address replacement replaces the immediate addresses with “ADDR”: “call 0x400567” -> “call ADDR”.
3.2.2补丁块选择(PBS)
标准化后,Robin通过比较标准化的 CFG 来识别补丁块。为了获得更精确的差异结果,我们在Robin中应用了篮子算法[ 56 ] 。该算法首先计算 CFG 中所有块的哈希值。然后将具有相同哈希值的块放入同一个篮子中。它通过检查每个篮子来选择补丁块。如果篮子中的基本块的数量是偶数,则算法将认为它们是匹配块并跳过对桶的检查。对于其余的篮子,它根据其上下文(即前驱和后继)来匹配块对。不匹配的被视为一个补丁块。
Robin无法在补丁块中生成 MFI有两种情况。首先,补丁过于简单,因为它只改变了条件,没有添加错误处理逻辑,这导致没有缓解点。其次,当补丁块位于函数入口处时,Robin无法收集任何约束来解决函数输入,因为约束是从分支执行构建的。例如,在清单 1 中,当执行从块 P1 移动到块 P3 时,会构建分支约束“[ebp+pktmp] == 0”。因此,如果补丁块位于函数入口处,则不会发生分支执行,并且Robin无法获得分支约束来构建函数输入。针对这两种场景,我们为补丁块集设计了两种扩展策略:
S1。如果补丁块包含条件检查并且其后继块之一是错误处理块,Robin会将错误处理块添加到补丁块集中。
S2。如果补丁块集中仅包含一个补丁块并且该补丁块位于函数入口处,Robin会将其任何后继块添加到补丁块集中。
请注意,如果修补函数的 CFG 仅由一个块组成,则将其视为修补块。
3.3函数输入构建
Robin寻找通向补丁块的可行路径(如第 2.2.1节中定义),并从每个路径构建一个函数输入。与模糊技术[ 51 ]相比,符号执行在获取目标代码位置的路径方面更加高效和有效。这是因为模糊测试会生成随机输入,并希望这些输入能够引导补丁块的执行。符号执行可以直接遍历以补丁块结尾的路径,而无需提供具体输入。与模糊测试相比,符号执行的执行尝试更少。因此,Robin利用符号执行技术来构建函数输入.
3.3.1可行路径搜索(FPH)。
给定一个补丁块,Robin使用轻量级符号执行技术来搜索从函数入口块到它的可行路径。例如,在图1中,由绿色折线连接的基本块序列是Robin找到的可行路径。由于我们只在函数内执行符号执行,因此我们采用欠约束符号执行。欠约束的符号执行执行程序内的任意函数,有效地跳过从程序主条目到目标函数的昂贵路径前缀。然而,即使在约束不足的符号执行下,由于路径爆炸和耗时的约束求解,遍历所有可能的执行路径对于该任务来说仍然是低效且无效的[ 27 ]。为了最大限度地减少时间成本,我们采用向下钻取的轻量级符号执行来促进路径搜索。
首先,我们进行静态分析,选择补丁块和函数入口之间的最短路径作为候选路径。我们使用符号执行技术来执行它们,通过找到每个符号变量的具体值来解决路径约束。例如,如果路径约束包含“x > 1,x < 5”和变量X是一个整数,解为X可以是 2、3 或 4 中的任意一个。如果路径约束包含冲突条件(例如,“x > 1
“ 和 ”x < 0”),然后是符号变量X是无解的,路径是不可行的路径。如果发生这种情况,我们将选择另一条最短路径并继续在新路径上进行约束求解。
其次,如果所有最短路径都不可行,我们利用向后切片技术来修剪函数 CFG 以获得更多有效路径。为了进行切片,我们选择检查块中的所有变量作为切片起点,并通过反向数据流分析找到所有相关变量(寄存器值或内存值)。然后,我们从 CFG 中删除不包含相关变量的基本块,并从部分 CFG 中删除剩余的块。我们反复搜索部分CFG中的可能路径,并应用相同的路径可行性验证方法来检查它们是否可行。
最后,如果前两个步骤没有给我们一条有效的路径,我们会执行一个耗时的盲符号执行来找到从函数入口到补丁块的路径。盲符号执行是对目标函数中所有可能路径的探索,而无需在寄存器或内存空间中指定任何具体值。为了加快执行速度,采用了循环检测器,通过限制循环执行的最大次数来避免符号执行陷入循环。注意到Robin将避免在路径搜索期间单步执行函数调用,这将在3.4节中解释。通过向下钻取的方法,我们可以进行有效的可行路径搜索来选择补丁块。例如,在图1中,我们可以为每个补丁块P1、P2和P3找到一条可行路径。因此,这三个块都被选择用于下一步。
标签:Binary,Towards,Semantic,函数,路径,补丁,修补,漏洞,Robin From: https://www.cnblogs.com/ICE-9/p/17936793tag:缓解