总览
"TChecker: Precise Static Inter - Procedural Analysis for Detecting Taint - Style Vulnerabilities in PHP Applications" 由香港中文大学的 Changhua Luo、Penghui Li 和 Wei Meng 撰写。论文介绍了 TChecker 工具,用于检测 PHP 应用中的污点式漏洞,该工具通过精确的上下文敏感的过程间静态污点分析,克服了现有工具的局限性,在漏洞检测方面表现出色。
背景介绍
污点式漏洞
污点式漏洞是指在 Web 应用中,当用户提供的数据(即污点)没有经过充分的净化处理,就被用于应用的关键操作(即接收污点的位置,称为 sinks)时所产生的安全漏洞。常见类型包括跨站脚本攻击(XSS)和 SQL 注入(SQLi)等。
动态方法vs静态方法
由于 "污点式 "漏洞的严重性和普遍性, 必须采用防御技术来减轻这类威胁,保护相关方的安全。人们提出了许多方法来检测野外的污点式漏洞。这些方法一般可分为动态方法和静态方法。
动态方法是在已部署的网络应用程序中注入攻击有效载荷并检查相应的输出。然而, 由于现代网络应用程序的动态特性和导航复杂性, 动态方法的代码覆盖范围通常有限。因此, 这些方法无法揭示它们无法触及的应用程序代码中的漏洞, 并且存在较高的假阴性。此外, 动态方法通常需要手动配置应用程序, 因此可扩展性较低, 不能轻易应用于大量应用程序。
另一方面, 静态污点分析是完全自动化的、可扩展的, 而且可以实现较高的代码覆盖率。因此, 静态分析已被广泛采用, 并在检测污点式漏洞方面显示出巨大潜力。
现有研究的问题
现有的PHP应用程序静态污点分析解决方案存在高误报率和高误判率的问题。现有解决方案存在三个固有局限:
缺乏上下文敏感分析:
-
问题表现
- 许多污点式漏洞在 PHP 应用中是上下文相关的,但现有工作无法实现全面的上下文敏感分析,导致无法精确推断方法调用的目标函数。
-
原因分析
- PHP 是动态类型语言,接收对象通常在定义时不指定类型注释,其类型在运行前难以确定。常用的通过匹配被调用函数名来确定目标函数的方法不准确,可能导致过度污点(匹配过多目标)或污点不足(保守地不匹配目标)。
基于不合理假设简化分析:
-
问题表现:
- 现有工具为简化污点分析做出一些可能不总是成立的假设,导致分析不精确,产生误报和漏报。
-
原因分析
- 例如,RIPS - A 假设 PHP 应用中的调用站点总是调用相同的目标函数,但实际情况是调用站点在不同上下文可能调用不同函数。其他开源工具假设如果调用站点的任何参数被污染,其返回值就被污染,这些假设不符合实际情况,影响了分析的准确性。
对高级 PHP 特性支持不足:
-
问题表现
- 现有工具不支持一些常用的高级 PHP 特性,导致数据流分析可能提前终止,产生漏报。
-
原因分析
- PHP 支持动态常量(
constant($con)
)、可变变量($$var
)、动态包含(require $a)等复杂特性,但现有开源污点分析工具没有对这些特性进行建模。由于无法处理这些特性,在分析过程中可能无法正确跟踪数据流向,从而遗漏一些漏洞。
- PHP 支持动态常量(
举例说明
漏洞描述
- 在这段代码中,
<img>
标签的src
属性被定义为base_url()
函数的返回值。用户控制的输入$_POST['image']
被用作该函数调用的参数,并且最终在没有经过净化处理的情况下被输出。这使得攻击者可以通过$_POST['image']
参数构造恶意负载来执行 XSS 攻击。
现有工具检测情况分析
-
PHPJoern
- PHPJoern 无法检测到这个漏洞,原因是它不会传播从函数返回的污点,即不会将函数返回语句中的污点进行传播,所以无法识别这个漏洞。
-
RIPS
- RIPS 能够检测到这个漏洞,但同时也报告了大量的误报。这是因为它存在两个问题:一是它采用了部分匹配函数名的方式来确定调用目标,可能导致过度估计调用图,从而产生误报;二是它以一种不考虑上下文的方式进行污点分析,只要一个函数的任何参数被污染,它就认为函数的返回值被污染。在这个例子中,即使
uri
在base_url()
函数中已经被正确净化,但由于$_POST['image']
作为参数被污染,它仍然会认为base_url()
函数的返回值被污染,从而导致误报。
- RIPS 能够检测到这个漏洞,但同时也报告了大量的误报。这是因为它存在两个问题:一是它采用了部分匹配函数名的方式来确定调用目标,可能导致过度估计调用图,从而产生误报;二是它以一种不考虑上下文的方式进行污点分析,只要一个函数的任何参数被污染,它就认为函数的返回值被污染。在这个例子中,即使
-
RIPS - A
- 由于其源代码不可用,无法直接应用进行检测。但是从原理上分析,检测这个漏洞对它来说具有挑战性,因为它进行的是过程内分析,不足以推断出第 5 行接收对象的类型,可能会错误地将污点传播到不正确的目标函数,从而也可能产生漏报。
TChecker 工具介绍
设计思路
-
精确的调用目标识别:通过对 PHP 对象进行迭代的过程间数据流分析来推断类型,从而精确识别方法调用的目标,构建精确的调用图。
-
上下文敏感的污点分析:根据调用上下文确定目标函数,传播污点,避免过度污点或污点不足。
-
对 PHP 特性的支持:建模 PHP 对象和面向对象编程特性,支持动态特性如动态包含。
工具架构
整体架构概述
给定 PHP 应用的源代码,TChecker 通过在每个调用站点推断调用目标来增量式地构建其调用图,然后执行一个全程序的上下文敏感的过程间污点分析,以识别污点式漏洞。
架构主要组成部分
-
Constructing Call Graph(构建调用图)
-
这是 TChecker 架构中的一个关键部分。调用图是许多安全应用(如漏洞检测、沙箱等)中的基本组件,但在 PHP 中,找到方法调用和变量函数的目标函数一直是一个未解决的问题。TChecker 采用了新的算法来解决这个问题,它分两个阶段增量式地构建调用图。
-
Identifying called function names(识别被调用函数名):这是构建调用图的第一个阶段,TChecker 对接收对象和变量函数名进行向后的数据流分析,以找到每个调用站点的函数名。这个过程涉及到对方法调用的接收对象类型的推断以及对变量函数调用的变量值的推断。
-
Connecting call targets(连接调用目标):在识别了被调用函数名之后,TChecker 将一个调用站点与其调用目标进行连接。这个过程包括匹配调用函数名和所有的函数定义,然后过滤掉在匹配包含通配符的调用函数名时引入的无效调用目标候选。
-
-
-
Taint spec(污点规范)
- 定义了污点的来源(sources)、接收污点的位置(sinks)以及净化操作(sanitizers),这些信息是 TChecker 进行污点分析的依据。例如,它将外部输入(如
$_GET
)视为污点来源,将数据库操作、内容生成操作和循环终止条件中的变量视为接收污点的位置,并考虑相应的净化操作(如htmlspecialchars()
、mysql_real_escape_string()
)。
- 定义了污点的来源(sources)、接收污点的位置(sinks)以及净化操作(sanitizers),这些信息是 TChecker 进行污点分析的依据。例如,它将外部输入(如
-
Taint analysis(污点分析)
-
这是 TChecker 的另一个核心功能,用于执行静态的、上下文敏感的过程间污点分析,以找到可能被攻击者控制和利用的敏感操作。
-
Intra - Procedural taint propagation(过程内污点传播):TChecker 对每个类似赋值的语句从右向左传播污点信息。它首先识别类似赋值的语句,然后分析赋值操作数的污点状态,根据不同情况判断操作数是否被污染,并进行相应的污点传播操作。
-
Inter - Procedural taint propagation(过程间污点传播):TChecker 分析调用语句并在函数边界之间传播污点。这个过程比较复杂,因为污点可能通过多个函数边界传播,而且每个调用语句都需要分析,因为其调用目标可能定义新的污点源或使用被污染的全局变量。为了提高效率,TChecker 会跳过一些不太可能在当前上下文被调用或不使用被污染变量的调用目标的分析。在这个过程中,包括选择目标函数、预处理目标函数和分析目标函数等步骤。
-
-
-
Bug report(漏洞报告)
- 根据污点分析的结果,如果发现污点数据能够在未经过净化的情况下被用于敏感操作,TChecker 就会生成漏洞报告。
工具实现
-
基于 PHPJoern:用 7.3K 行 Java 代码实现,使用 php - ast 解析 PHP 源代码为 ASTs,利用 PHPJoern 构建控制流图和数据依赖图。
-
对 PHP 特性的处理
-
内置函数:建模如 substr () 等字符串函数,识别回调并分析对污点传播的影响。
-
条件语句和循环:合并条件语句分支的污点,将循环视为条件语句处理。
-
数组:将数组视为哈希表,采用常见做法建模,在特定情况下污染数组元素。
-
工具评估
-
数据集和对比工具:选择 17 个 PHP 应用作为数据集,对比 PHPJoern 和 RIPS。
-
检测结果
-
总体表现:TChecker 报告 284 个漏洞(含误报),其中 131 个为真阳性,精度为 46.1%,检测出 18 个新漏洞。
-
与 RIPS 对比:TChecker 检测出 50 个 RIPS 未发现的漏洞,精度更高,RIPS 存在过度污点和未分析接收类类型等问题。
-
与 PHPJoern 对比:PHPJoern 误报最少但真阳性也少,不支持对象属性和部分方法调用。
-
-
误报和漏报分析
-
误报原因:包括未建模所有消毒器、应用的预期特性、工具实现问题(如数组近似过度)以及报告死代码中的漏洞。
-
漏报情况:存在对某些应用中已知漏洞漏报的情况,主要由于对消毒代码建模不足。
-
-
性能分析:TChecker 分析时间较长,但能报告更多真阳性漏洞,性能可接受。
-
案例研究:展示了在 Stock - ManagementSystem 和 osCommerce2 中检测到的新漏洞,体现 TChecker 通过跟踪对象属性污点和确定目标函数的优势。
性能优化
构建调用图阶段的性能优化
-
避免重复分析调用站点
- 设计新算法,只有当调用语句在新的上下文执行时才进行分析,而不是每次遇到都分析。例如,在增量式构建调用图的过程中,TChecker 分两个阶段进行,并且迭代执行这两个步骤。通过添加新的调用目标函数(在新的上下文)到调用图中,可以发现新的数据流关系,从而进一步确定更多函数名变量的值或更多接收对象的类,进而找到更多新的调用目标。这种方式避免了对相同调用站点在相似上下文中的重复分析,提高了效率。
-
利用已有分析结果进行目标函数筛选
- 在连接调用目标时,通过一系列的步骤筛选出真正的调用目标,避免无效的分析。例如,在确定调用函数名时,考虑类继承关系,通过解析
extends
关键字构建继承树来提取类信息,以便准确匹配目标函数。在验证调用目标时,进行函数原型检查,去除不符合条件的无效调用目标候选,如静态函数调用非静态函数、参数数量不匹配、访问修饰符不符合要求等情况的调用关系都会被过滤掉。
- 在连接调用目标时,通过一系列的步骤筛选出真正的调用目标,避免无效的分析。例如,在确定调用函数名时,考虑类继承关系,通过解析
污点分析阶段的性能优化
-
选择性分析调用目标
-
在过程间污点传播过程中,为了避免对所有可能的调用目标进行分析带来的巨大开销,TChecker 采取了一些策略来选择性地分析调用目标。
-
缩小潜在目标函数范围:通过观察发现大多数模糊的调用目标是方法,且调用具有数据依赖关系的特点。TChecker 优先选择那些在同一文件中其他函数(包括构造函数)被调用过的目标函数,如果同一文件中没有,则在子文件夹中选择,从而得到一个子集的调用目标,避免了过度污点和提高了分析效率,虽然可能会导致少量的污点不足,但在实际效果上证明是有效的。
-
跳过不必要的分析:TChecker 会对一个调用目标进行预处理,检查它是否使用任何污点变量,如果没有,则可以跳过对该被调用函数的污点分析。具体做法是找到所有使用外部变量的类似赋值语句,并收集相关函数,以及收集包含使用污点源的类似赋值语句的函数。当给定一个调用目标时,检查它是否在这些集合中,如果不在且其参数没有被污染,则跳过对它的分析。这种选择性的污点分析思想在其他语言中也被证明可以提高分析效率。
-
-
数据分析
各指标含义及解读
-
代码行数(LLoC)
- 表示每个应用的代码规模,以行数为单位。不同应用的代码行数差异较大,从几千行到十几万行不等,如 Webchess 只有 1,569 行,而 MediaWiki 有 178,616 行。
-
真阳性(TP)和假阳性(FP)数量
-
真阳性:指工具正确检测出的实际存在的漏洞数量。例如,TChecker 在 Ecommerce - Codelgniter - Bootstrap 应用中检测出 14 个真阳性漏洞,而 PHPJoern 检测出 8 个,RIPS 检测出 10 个。
-
假阳性:指工具错误报告的实际上不存在的漏洞数量。如 TChecker 在 MediaWiki 应用中报告了 8 例假阳性,PHPJoern 报告了 6 例,RIPS 报告了 54 例。
-
-
检测时间(Time)
- 展示了每个工具对相应应用进行漏洞检测所花费的时间。不同工具在不同应用上的检测时间有所不同,例如在 MediaWiki 应用中,TChecker 花费 91 分钟,PHPJoern 花费 11 分钟,RIPS 花费 4 分钟。
-
仅 TChecker 发现的漏洞数量(UP)和仅其他工具发现的漏洞数量(UN)
-
UP:体现了 TChecker 相对于其他工具的优势,即 TChecker 能够发现而其他工具未能发现的漏洞数量。例如在 WeBid 应用中,UP 的值为 17,表示 TChecker 比 PHPJoern 和 RIPS 多发现了 17 个漏洞。
-
UN:表示其他工具能够发现而 TChecker 未发现的漏洞数量。在所有应用的评估中,UN 的值均为 0,说明 TChecker 能够检测出 PHPJoern 和 RIPS 所报告的所有漏洞。
-
-
新漏洞数量(NP)
- 指 TChecker 发现的在互联网上没有相关漏洞报告且存在于应用最新版本中的漏洞数量。例如在 osCommerce2 应用中,NP 的值为 1,表示 TChecker 在该应用中发现了 1 个新漏洞。
总结
通过表格中的数据可以看出:
-
检测能力:TChecker 在检测真阳性漏洞方面表现出色,总共检测出 131 个真阳性漏洞,比 PHPJoern 多 60 个,比 RIPS 多 50 个,并且能够发现 18 个新漏洞。
-
精度比较:TChecker 的精度为 46.1%,虽然略低于 PHPJoern 的 47.9%,但远高于 RIPS 的约 6%。
-
检测时间:TChecker 的检测时间相对较长,这是因为它进行了更全面的数据分析,但它能够报告更多有价值的真阳性漏洞,所以在实际应用中这种时间成本是可以接受的。
-
TChecker 表现较好的原因:
-
全面的分析方法:TChecker 采用精确的上下文敏感的过程间静态污点分析方法,能够更准确地跟踪污点传播路径,识别漏洞。例如,它通过迭代的过程间数据流分析来推断对象类型,从而精确识别调用目标,构建精确的调用图,这有助于它在不同应用中找到更多的真阳性漏洞。
-
对 PHP 特性的支持:TChecker 花费了大量精力对 PHP 的复杂特性进行建模和处理,如对象属性、动态包含等。这使得它在处理涉及这些特性的应用时具有优势。例如,在一些应用中将用户输入保存到对象属性中,TChecker 能够跟踪污点在对象属性中的传播,从而检测到相关漏洞,而其他工具可能无法做到这一点。
-
-
PHPJoern 精度较高但真阳性较少的原因:
-
选择性污点传播策略:PHPJoern 采用选择性地传播污点的策略,只跟踪局部变量和函数参数中的污点,这种策略使得它报告的误报数量相对较少,精度较高。
-
对特性支持不足及方法调用限制:它不支持一些 PHP 特性,如对象属性,并且对于方法调用有一定限制,只考虑具有唯一被调用函数名的函数调用。这导致它在一些应用中无法找到正确的调用目标,从而错过一些漏洞,使得真阳性数量较少。
-
-
RIPS 表现较差的原因:
-
缺乏过程间分析及不准确的污点策略:RIPS 没有进行全面的过程间分析,无法推断出在其他函数作用域中分配的变量的类型或值。同时,它采用的污点分析策略不准确,例如它简单地认为如果一个函数的任何参数被污染,其返回值就被污染,这种过度污点的策略导致它报告了大量的误报,并且在一些情况下可能会遗漏漏洞。
-
对 PHP 特性处理能力有限:RIPS 在处理一些 PHP 特性时存在问题,如无法正确处理动态包含和一些复杂的对象属性情况,这也影响了它的漏洞检测能力。
-
未来展望
PHP 静态分析方面
-
完善对 PHP 特性的支持
- TChecker 目前仍未能完全支持一些 PHP 特性,如动态数组、变量函数等,并因此产生了一些误报。未来需要开发新的算法和实现方式来进一步完善对这些特性的静态建模,以提高分析的准确性。
-
研究简化建模的影响
- 许多现有研究在静态分析中采用了一些简化假设,未来有必要研究这些简化建模对不同特性在不同场景下的影响,以便更好地理解和改进静态分析技术。
调用图应用方面
-
扩展应用场景
- 文章证明了精确的调用图有助于在 PHP 应用中发现更多污点式漏洞,并且调用图在其他安全场景中也有应用。未来可以进一步探索调用图在更多领域的应用,例如在 TChecker 基础上进一步实现数据库语句建模,用于检测二阶漏洞,以及探索其与动态方法的结合应用,就像 Navex 等研究中所展示的那样。
自动化验证方面
-
实现符号执行验证
- 静态分析通常会在漏洞检测中产生误报,虽然 Navex 已经展示了使用符号执行进行自动化漏洞验证的可行性,但由于其代码不完整,本文无法应用。未来计划在 PHP 应用中实现符号执行来验证和利用 TChecker 发现的漏洞。
-
解决多目标函数挑战
- 在实现符号执行时,需要解决 TChecker 中一个调用站点可能有多个调用目标的问题,而常规的符号引擎通常只考虑一个目标函数。未来可以通过对每个目标函数建模路径约束,并将满足不同路径约束的输入提供给程序来解决这个问题。