论文笔记——FUGIO- Automatic Exploit Generation for PHP Object Injection Vulnerabilities

PHP反序列化链自动生成的研究

论文题目:FUGIO: Automatic Exploit Generation for PHP Object Injection Vulnerabilities

论文出处:USENIX 2022

论文作者:Sunnyeo Park(KAIST),Daejun Kim(KAIST) ,,

论文下载地址

会议视频地址

代码地址

FUGIO:

Pasted image 20230526171614

0x01 Intraduction

0x02 Background

0x03 Motivation and Challenges

现存的工具只关注检测潜在的反序列化漏洞,需要使用POP来检测误报,构造POP链需要大量的手工操作和专业知识。

Dahse提出了一种利用静态分析识别可用POP链的方法,但为了减少误报,仍需解决可达性分析问题,即找到一个合适的输入来利用一个可能利用的POP链。当可能利用的POP链很多时,这变成非常艰巨的工作。如Contao CMS至少有26180条POP链,手工分析这些链是不可能的。

对POI漏洞自动化利用需要实现如下目标:

  1. 发现一个POI漏洞;
  2. 在触发识别的POI漏洞时,识别出可用的gadgets组成的POP链;
  3. 为识别出可利用的POP链生成输入对象。

作者考虑到当前的Burp、Acunetix、sonarsource等工具已经可以挖掘POI漏洞,核心贡献关注在生成可利用对象,关注上面的后两个目标,解决第二个和第三个目标面临的挑战如下:

识别POP链

AEG工具应该可以识别用户输入位置所有可加载类的gadgets组成的POP链。

  1. (C1-1)PHP的动态特性使得识别可加载的类和它们的gadgets很难。自动加载机制(autoload)使得可以基于触发开发者指定的回调函数加载任意存在的类。然而,很多PHP CMS严重依赖动态生成的PHP类,然而仅仅考虑静态调用的类会导致未考虑POP链中可用的gadgets,产生漏报。
  2. (C1-2)当评估可利用性时,单纯的连接可加载的gadgets的方法会产生很多大量的POP链。尤其是长链时,因为它会呈指数级增长。

    利用生成

    AEG工具应该生成一个具有多个属性值的利用对象,使其能够在不中断的情况下执行已标识的链。生成一个合适的输入使应用执行到目标语句。

符号执行无疑是最好的方法,但是这种方法需要根据我们的符号来建模PHP内置函数的语义,需要大量的工程努力;作者实验中30个web应用使用了460个内建函数,然后随着PHP解释器的发展,将需要更多的内置函数来执行符号执行,因此作者选择了模糊测试,意味着存在漏报。

Fuzzing

  1. (C2-1)在大型PHP应用程序上进行无状态模糊处理时,很难建立高吞吐量。当目标应用很大时,在应用上执行每个生成的输入是一个很慢的过程。作者选择的30个应用平均一个请求执行时间是0.6s。比SOTA慢非常多。因为目标应用会执行很多与POI漏洞无关的模块,进一步,也可能在模糊测试的过程中,产生副作用,导致目标应用不能正常工作。然后,在每次执行完将程序恢复到原始状态是非常费时的。
  2. (C2-2)通过模糊测试生成一个具有多个属性值的可利用对象是困难的。大量模糊测试将输入建模为字节流,然而POI漏洞自动利用需要执行面向对象的编程,需要更改多个属性并塑造对象层次结构,如何识别出可利用对象需要的属性?如何突变?

0x04 Overview

解决(C1-1)利用动静结合分析合并所有静态声明和动态加载生成的gadgets,解决(C1-2)通过粗粒度的过程间分析(修建不必要的POP链)和深度有界的宽度优先搜索,枚举可能的POP链。
解决(C2-1)通过合成一个PUT测试程序模拟触发POI漏洞的执行环境,解决(C2-2)利用分支覆盖率、运行时引用错误和提示来生成潜在的POP链。

Pasted image 20230526171614

0x05 Design

5.1 POI Detector

运行爬虫提取每个页面的<a><form>标签,生成对应的请求模板,对于这个请求模板中的每个GET、POST和COOKIE参数,检测器注入一个预定义的序列化字符串,该字符串表示我们的测试PHP对象,从而生成一组测试请求;在每个请求中,一个输入参数保存我们的预定义字符串。然后,检测器发送每个测试请求,并观察一个反序列化用户输入的PHP内置调用站点是否被我们的测试对象调用。

为了实现这个功能,FUGIO使用runkituopz,HOOK了26个预定义的涉及反序列化用户输入的PHP内置函数,如unserialize、is_file、file_exists等,如果这些反序列化相关内置函数被调用而且包含参数包含测试对象的实际参数时,就报告为一个潜在的POI漏洞,并将这处调用发送给动态分析器。

5.2 Static Analyzer

静态分析计算用户自定义类和函数的静态摘要,用于生成POP链和PUT测试程序。

解析每个PHP文件到AST,然后分析函数、类、interfaces和traits的定义。
对于类,提取如下信息,包括类名、命名空间、父类、implemented interfaces、used traits和类自己的定义,存储这些信息到定义该类的PHP文件的类摘要中。还包括类的每个已定义的属性和成员方法的名称和可见性(visibility)。

当解析函数和成员方法定义时,静态分析器提取它的函数原型并计算函数摘要,包括调用者-被调用者关系,对于函数和成员方法定义中的每一个函数调用(如$receiver->method()),分析器提取目标的method名称和它的receiver类候选对象。当调用显式地使用$this时,静态分析器会精确地推断出receiver为目标方法的所有者和该所有者的父类的所有者。否则,它将一组包含目标的method的类作为receiver的候选对象,计算所有可能类的保守集。当存在来自涉及new关键字的新实例化对象的过程内数据流到receiver如($receiver = new ClassA), 分析器使用这个类作为receiver,每个文件摘要中的调用者-被调用者关系后面用来连接gadget构建POP链。

对于每个成员方法和函数,静态分析器计算从它的形参和所有者的属性到函数体中每个调用的参数的流不敏感的过程内数据流。函数摘要中的这些数据流用来删除在攻击中不能通过调整注入对象的属性值来改变后续gadgets的真实参数因而不可利用的中间的gadget 。

PS:思考
收集类名、包含的属性和成员方法、调用者-被调用者关系、成员函数体中过程内数据流(成员函数形参、类属性到函数体内调用的数据流)
member method和function的区别是什么?
(类/函数)摘要的具体格式是什么样?

5.3 Dynamic Analyzer

为哪些动态生成的,没有在函数源代码中静态定义的类和函数计算函数摘要和类摘要。这些摘要随后也会被用于生成链的POP链标识符和PUT所使用。

给定一个POI漏洞,动态分析器收集静态分析器收集不到的额外信息。为此,FUGOI创建一个文件来安装hook ,使用runkit和uopz扩展来hook php内置函数,如unserialize、file_exists和fopen等,作者使用.htaccess文件将扩展注入到web应用中。

当给定的POI漏洞触发后,对静态收集得到的所有类调用class_exists函数,枚举测试出哪些类是可以动态加载到的。因为PHP自带的autoload机制可以回调指定的函数来加载不存在的类。

动态分析器随后分析动态加载的函数、类、interfaces和traits,当这些函数类等的体没有被静态定义时。分析方法与静态分析方法一致。作者提到这里分析的类和函数时动态定义的,不存在源码中,dahse的论文没有考虑这些动态生成的函数和类。

最后,当hook被调用时,动态分析器存储环境变量($_ENV和$_SERVER)和全局变量($_GLOBAL)。这些变量用来构建PUT在下一步模拟触发给定POI漏洞的执行环境。

PS:思考
存在动态定义的而且源码中不存在的类吗?可以找出示例看看。

5.4 POP链识别

基于从静态和动态分析获取的信息,pop链识别器输出一个可用的POP链列表和一个用于执行模拟测试的PUT。

5.4.1 POP链识别

POP链是一系列gadget,当目标漏洞利用发生时,它反映了从魔术方法到敏感接收器的堆栈跟踪。基于不同的敏感sink,攻击者可以发起不同类型的攻击,作者共指定了26种可以导致文件创建、文件修改、文件删除、命令执行和代码执行的敏感sink。

对于每个敏感Sink的触发,FUGIO利用包含调用的函数或者方法的函数摘要检测攻击者是否可以改变调用的真实参数,它检测从形参m和类的属性值m是否存在过程内数据流。如果存在,FUGIO在生成POP链时考虑该函数,否则,FUGIO排除这个sink函数,因为攻击者没有其他方式通过注入的对象直接修改这些参数。

Pasted image 20230531095857
如这个例子,对应save()和close()这两个函数,FUGIO检测到从filename属性和到file_put_contents()第二个参数和unlink()第一个参数的过程内数据流。

链识别器需要生成POP链,每个POP链由沿着从一个魔术方法到每个目标敏感sink函数的路径上的gadget组成,一个原生的方法是通过深度优先搜索从魔术方法到目标敏感sink构造所有可用的链。然而一个魔术方法的入口遍历被调用者的下游可能不会遇到任何sink,从而浪费计算资源。此外,当调用链中存在循环时,该算法不会终止。

作者设计了一个算法,以宽度优先的方法构造一棵深度有界的调用树,如下如所示:

Pasted image 20230526171943
特别地,对于每个目标sink的调用,FUGIO的链识别器计算一个调用树,调用树的根节点是体现sink调用的方法。然后,对于每个表示一个函数或方法m的叶子节点,迭代地附加新节点,其中的每个子节点对应m的一个潜在调用者。

链识别器构建这个树,直到它的高度到达一定的高度,这个高度是审计员指定的一个值。作者在7.4节评测设置这个值为7。通过这种高度有界的树搜索,FUGIO可以枚举POP链,即使在调用gadget中存在循环。

添加叶子节点:当将gadget(m)的潜在调用者附加到每个叶子时,链识别器不仅考虑了开发人员打算为m设计的真实调用者,而且还考虑了攻击者为通过操纵对象属性强制执行m的调用者。具体地说,链识别器收集所有具有函数调用的摘要,找其中(1)目标被调用者名称与m相同,(2)实际参数的数量与m的形式参数数目相同。这些收集的函数摘要集合成为m的潜在调用者。从潜在的调用者中,链标识符进一步删除了调用语句具有静态确定性的接收器,它们不指示m的所有者类。????提醒大家,通过静态分析,FUGIO已经为每个调用计算出了一个保守的receiver集(5.2)。

考虑到链识别器在附加m的潜在调用者利用静态信息,当潜在的调用者是静态、不可决定的,它会错过调用边。对于反射性调用,如$receiver->$method,链识别器无法确定其可能的调用者。因此缺乏包含此调用边的链。作者在7.3.2讨论了由于这些反射调用的漏报。可以看下这里是否可以改进。

生成链:在构建完每个sink的调用树后,链识别器寻找表示魔术方法的叶子节点。对于每个叶子节点,它计算一条到根节点的路径,从而发出一个树节点链,每个树节点链对应一个POP gadget。

在将识别出的POP链传递到下一步之前,链识别器执行一次过程间数据流分析删除那些从gadget属性到敏感sink真实参数没有任何数据流的链,因为攻击者无法更改这些链最后的sink的真实参数。作者利用位于这个POP链中每个gadget的函数摘要,计算POP链中的过程间数据流。

5.4.2 PUT生成

FUGIO合成一个php中的PUT,比原来的程序相对较小,但是包含了利用生成所必须的所有gadgets。FUGIO首先从分析器计算的摘要中提取所有的类和函数定义。为了避免类和函数名可能发生的冲突,FUGIO为每个定义发出一个文件。作者将这个生成的文件称为定义文件。

FUGIO然后生成PUT的头和body部分,在头部分,FUGIO设置所有的由动态分析提供的环境变量和全局变量。FUGIO然后写body部分到去包含在利用POI漏洞时已加载和可加载的所有gadgets。body部分以unserilize调用结尾,它以一个输入的序列化字符串作为参数,后期提供给fuzzer。PUT从shell命令行获取输入的字符串,通过PHP交互器运行PUT。因此fuzzer通过PUT中输入触发unserialize来模拟利用POI漏洞执行环境。

反馈插桩:在构造反馈驱动的fuzzing,FUGIO在PUT插桩来获取fuzz测试输入每一个对象的三种反馈。(1)包含执行的条件表达式和调用方法的执行trace,(2)POP链中执行的gadget数目,(3)从条件表达式值获得的提示。

为了得到前两种反馈,在每个函数入口,每个条件表达式之前和之后和用户自定义函数的每次调用插桩。具体来说,每个检测的代码发出其代码行和文件的哈希值,因此留下执行过的标记。

这步还将插入提示属性值的代码,提示用来生成准确的属性值。如list1中第5行的$this->logtype需要等于”TEMPORARY”,靠随机生成这个属性值是不可能的,为了实现提示,代码报告常量和类型检测内建函数,如出现在执行语句中的is_string()和is_int(),这些常量和内置函数用来生成属性值,称为属性提示。

5.5 POP Chain Fuzzer

给定一个PUT和一个POP链,fuzzer在PUT上构造反馈驱动的fuzzing,生成利用对象。算法1描述了模糊测试的总体过程。
Pasted image 20230601164022

给定一个POP链和一个PUT,模糊器会在给定的时间段内启动一个模糊测试。模糊测试过程从准备一个初始种子池开始(1-3行)。它然后重复下列过程直到发现一个一个注入的对象执行了POP链中的敏感sink并且带了攻击payload。fuzzer挑选、变异、执行、分析结果,如果变异后的种子增加了分支覆盖率,把这个种子加入到种子池。进一步如果变异后的种子比原种子执行更多的gadget,模糊器派生新的种子比原种子在POP链深一层。它还通过利用常量和基于条件语句中推理出的属性类型给种子的属性值赋值。

种子优化:在挑选一个种子去变异的时候,作者优先选择达到给定链的深层gadget或者到达链条最后一个gadget的安全敏感sink的种子。
初始种子输入是给定POP链的第一个gadget对应的类对象,对于每个执行的种子,fuzzer存储它的执行结果:(1)种子的挑选次数;(2)它的属性树的hash;(3)POP链中执行gadgets的最大深度。基于这些信息,fuzzer优先执行具有更深执行的gadget。

作者定义diff表示POP链的长度与POP链中已执行的gadgets的最大深度的差值。然后作者使用公式1来分配目标gadget深度的概率;执行的gadget的深度越大,被选择目标深度的概率越大。

Pasted image 20230602083221

在选择一个目标target目标深度后,fuzzer然后在种子池中已经达到这个目标深度的种子中选择一个种子。

当种子池中包括到达sink的种子时,FUGIO将1.0的概率分为0.9和0.1,然后将0.9的概率均匀地分布给到达sink的种子,0.1的概率也均匀地分布给种子池中剩余的其他种子。当没有种子到达sink,fuzzer把相同的概率均匀地分配给目标gadget深度的种子。随着种子选择数量的增加,fuzzer降低了每个种子出现的概率,从而增加被选择较少的种子的机会。
输入生成:生成注入的对象需要(1)设计反映给定POP链的多个类的层次结构;(2)为多个类的属性分配适当的值,这些值又利用将攻击载荷到达敏感sink。在生成和编译注入的对象时,作者利用一个称为属性树的数据结构。它的root根节点表示一个类对象,这个类对象持有魔术方法,这个魔术方法为给定POP链的入口gadget。后代的每个子节点表示一个类对象属性,它包含属性名称、可见性、类型和值。当一个属性类型是一个类对象时,这个节点持有这个属性可能具有的类候选对象。此外,此节点将成为子树的父节点,该子树表示在POP链中具有另一个gadget的另一个类对象。

fuzzer将生成一个属性树,并通过创建一个实例化该对象的PHP文件,将此树转换成一个注入对象。在这个PHP文件中,FUGIO定义了此树中使用的所有类、设置属性值并序列化创建的类,如listing2所示。通过执行这个文件,模糊器生成输入,并将输入输入PUT。
Pasted image 20230605204945

变异:fuzzer变异选定种子的属性树,fuzzer会访问属性树中的每个属性,并检查其类型。当它的类型是对象时,fuzzer从这个属性的候选类中随机挑选一个类。否则,fuzzer随机从PHP中的string、interer、boolean、file、array和reference类型中选择一个。

对于string,interger,boolean和file类型,fuzzer分配一个随机的选定类型的值给这个属性。对于array类型,fuzzer随机地设置array的大小,并随机地给array分配键和值。对于引用(reference)类型,fuzzer识别它的owner和静态和动态分析得到的它的其它属性。它然后随机挑选它其他属性中的一个并分配它的引用到目标引用属性。在变异完属性后,属性树被转换到PHP文件中,然后fuzzer将从这个文件生成的输入,输入到插桩后的PUT中。

反馈:fuzzer进行反馈驱动的模糊测试,利用给定输入的执行结果来派生新的种子,新的种子更有希望达到给定POP链中的敏感sink。fuzzer利用的四种反馈分别是:(1)分支覆盖率;(2)gadget达到的深度;(3)属性提示;(4)参考(reference)错误。

对于分支覆盖率,当变异后的输入覆盖了新的分支,fuzzer添加变异后的种子到种子池中。对于第二种反馈,fuzzer利用gadget深度,当突变种子的执行使POP链中的原始种子到达的深度更深时,fuzzer将这个突变的种子添加到种子池中,并更新了所到达的gadget的深度。

当观察到一个属性被用于条件语句中,而且使用比较操作符和特定的内置类型检测函数如is_string(),is_int(),is_array()时,fuzzer为这个属性存储推断的类型和条件语句中出现的常量操作数。对于每个提示属性,它生成一个提示的种子,其中提示的属性的值被设置为推断的值,或被推断的类型随机突变。

此外,fuzzer在利用receiver调用方法时(如$receiver->method())观察参考错误,当观察到的错误是由于receiver丢失的属性或不正确的对象时,fuzzer将在当前输入的树中附加丢失的属性节点,或使用从具有目标方法调用名称的类中选择的值分配一个对象。

Exploit oracle:fuzzer利用exploit oracle来确定生成的输入对象能够利用POI漏洞。当变异的种子已经到达敏感sink时,fuzzer报告给定的POP链是probably exploitable。为了确定被识别probably exploitable的对象的可利用性,fuzzer检测生成的输入是否可以控制敏感sink中真实参数。Oracle将输入对象中的每个属性值与接收参数进行比较,从而生成一组候选属性,模糊器向其注入有效负载。
对于每个候选属性,fuzzer将其属性值设置为一个依赖于目标敏感接收器的攻击有效负载。如敏感sink是echo,这会导致XSS漏洞,即将攻击payload设置为<script>alert(1);</script>。在案例中,敏感sink导致文件删除,作者设置攻击payload为存在的文件路径。

PS:将输入对象中的每个属性值与接收参数比较,是否存在不准确的地方,看下代码实现。

最后,fuzzer检测注入的对象是否触发了敏感sink,而且sink的参数中带有真实攻击payload。FUGIO hook了PHP中的26个敏感函数调用,并检测每个sink是否被包含攻击有效载荷的实际参数调用。如果是这样的,fuzzer报告给定的POP链是exploitable并终止模糊测试。请注意,清单2展示了一个最终的输出,一个PHP片段,它定义了生成的利用对象并打印该对象的序列化字符串。FUGIO还能够通过将这个序列化的字符串填充到检测器发现的GET、POST或COOKIE输入参数中来生成攻击HTTP (S)请求。

总之,fuzzer生成一个攻击字符串,并使用这个输入执行PUT,该输入将被反序列化为一个利用对象。利用oracle检查这个注入对象是否调用给定的POP链中的一系列gadget,并使用攻击有效负载调用接收函数。

Manager:FUGIO并行运行POP链识别和POP链fuzzer。一旦POP链识别器在访问每个敏感sink计算出一组链,FUGIO通过为每个POP链调用fuzzer来管理fuzzing。

FUGIO在选择POP链时,优先考虑长度较短的POP链。作者实现了这个模糊策略,以便于在生成利用对象时偏袒于短的POP链。这样,在FUGIO检测长的链之前如果一个短的利用链被发现,FUGIO终止fuzz过程,因为这需要更多的计算资源。作者将执行POP链识别和POP链fuzz的比率设置为3比1,在所有的POP链识别完成后,所有的CPU内核被分配给fuzzer。

0x06 FUGIO Implementation

FUGIO实现用了两万行python和php代码。作者使用runkit和uopz hookPHP内置函数来识别对象注入点。然而这些扩展不支持hook eval,作者实现了这个功能来提取动态生成的函数和类。为了合并这些hook功能,作者在执行每个php文件之前,使用.htaccess文件实现了hook。

PS:实现了提取动态生成的函数和类和hook eval的关系?

在进行静态分析和给PUT插桩时,作者利用了PHP-Parser,作者使用RabbitMQ实现不同模块之间的通信。
源码开源:https://github.com/WSP-LAB/FUGIO

0x07 Evaluation

7.2评价生成利用对象的效率
7.3与先前的研究和一个开源工具对比
7.4演示几个参数对利用生成的影响程度

7.1 Experimental Setup

对30个PHP应用进行测评,对于每个应用程序,作者都准备了一个已知的POI漏洞,从而要求FUGIO生成利用对象,这些对象会触发导致文件删除/修改/创建、命令注入或远程代码执行的漏洞。在这30个应用中,8个应用和Dahse用来实验的相同,还包含了21个来自于PHPGGC的应用。在PHPGGC中,12个包是php库,作者使用这里的每个库制作了一个简单的PHP应用,并在这个应用中注入了一个POI漏洞。对于剩余的9个应用,作者利用公开的POI漏洞,CVE-2018-20148wordpress, CVE-2019-6339drupal,https://wpscan.com/vulnerability/9567f575-529d-4d66-980c-73cba6726673,这些benchmark的选择标准如下:

  1. 存在漏洞的版本仍然可以访问
  2. 易受应用程序的大小并不trivial

实验环境:在两颗Intel Xeon Gold6238 2.10GHz,384G内存的linux工作站上运行,对于每个web应用,准备了一个docker容器。

7.2 Performance of FUGIO

作者对30个应用中的每个应用fuzz5次,每个fuzz持续12个小时。对于每条链,作者设置fuzz最多100秒,并且最多组合7个gadget。在7.4评估这些参数的灵敏性。

下表总结了评测结果,第三列表示识别的POP链,第四列表示进行模糊测试以产生利用的POP链的数量,Covered SInks列表示检测到的POP链中唯一敏感sink的数量。

Exploitable Chains(E)列表示可利用POP链的数量,Probably Exploitable Chains(PE)列表示可以成功达到敏感sink,但是生成的利用不能通过exploit oracle。请注意,这些列中的每个单元格都代表了五次模糊试验的中值;最小值和最大值都在方括号内。此外,括号内的数字表示在五次模糊试验中报告的唯一链的数量。

Pasted image 20230606102331

True Positive Chains列表示作者手动确认的可利用的链,加号两边对应的分别是E和PE中可利用的链。这两个数字的总和产生了FUGIO在五次模糊试验中创造的可利用对象的总数。最后两列展示了识别POP链和运行FUGIO话费的时间。

从这30个应用中,FUGIO报告了共68个可利用的链,作者手动验证这些链,确认了FUGIO没有产生误报。在66条PE链中,有26条链确实是可被利用的。FUGIO从27个脆弱的应用程序中生成利用对象。

FUGIO没有成功地为GLPI、Vanilla和Yii生成利用。在GLPI中,FUGIO没有找到任何E链,因为它实际上没有可利用的链。这意味着攻击者能够注入输入对象,但无法利用GLPI中的漏洞。在Vanilla中,FUGIO错过了一个触发LFI漏洞的可利用链。这是因为FUGIO不支持计算LFI的sink。在Yii中,FUGIO识别了可利用的链,但模糊器不能为它们生成一个利用对象。生成此漏洞需要将包含适当索引及其相应类对象的数组值分配给该漏洞对象的属性。FUGIO无法在一个模糊的超时时间内生成一个满足这些条件的利用对象。

PS:Yii,对于复杂的链还是不能解决。

FUGIO接近识别出1000万条POP链,触发1637个sink。识别出的链范围在0到300万条(joomla),这些统计结果证明需要自动生成利用对象。

作者进一步分析了为什么FUGIO把26个可用的链报告了了可能可用的链。

  1. FUGIO不会在获取文件资源的PE链上执行利用oracle,因为攻击者不能将文件资源作为序列化的字符串注入。然而,我们发现,通过利用具有注入文件名的现有fopen调用站点,9个PE链是可利用的。 ????
  2. 当注入的有效负载出现在目标汇聚的实际参数中并没有任何损失时,攻击oracle才报告E链。然而有4个链是可以利用的,即使攻击者可以注入部分的攻击字符串。对于剩下的13条链,FUGIO无法确定对象属性,以便在给定的模糊测试超时时间内注入攻击有效负载。

7.3 Comparison to State-of-the-Art Tools

跟Dahse的工作和PHPGGC进行了对比。
Dahse等人提出了报告可利用链的静态工具。另一方面,FUGIO不仅报告可利用的链,还报告利用的对象。在7.3.1,作者对FUGIO的可利用链识别能力与Dahse的工作进行了细粒度的比较。

PHPGGC是一个对指定版本PHP开源应用公开利用链生成利用对象的开源工具。因为PHPGCC列出了公开的可利用的POP链,作者使用这些链还衡量FUGIO的漏报。不同于PHPGCC,FUGIO是一个通用的AEG工具,用于对任意PHP应用组装gadget并报告利用对象。

Experimental setup:对于每个应用,作者运行了5次FUGIO,超时时间为12小时。设置FUGIO对每条链的模糊测试时间最长100秒。在7.3.1,作者设置了FUGIO来识别长度小于9的POP链,Dahse等人也是同样的设置。在7.3.2,作者考虑了长度最多为9的链,这是PHPGGC中列出的最大可利用链长度。

7.3.1 Comparison to Dahse et al.

Target classes:Dahse等人通过包含文件的堆栈静态检测一个注入对象的类是否是可加载的。然而当应用中至少存在一个自动加载的回调函数,他们假设所有存在的类都是可以加载的。这已经不是一个有效的假设,因为这个bug已经在PHP 5.4.24和5.5.8中被修补过了。另一方面,FUGIO通过动态调用所有存在自动加载回调来检测它们的可用性来检测可加载的类。为了进行公平的比较,当至少有一个自动加载器存在时,作者将所有用户定义的类视为可用的gadget,从而对可用的gadger做出相同的假设。

Sensitive sinks:Dahse等人检测到的链分为6种类型,文件删除FD,文件创建FC,文件修改FM,sql注入SQLi,本地文件包含LFI,XML外部实体注入XXE。作者制定了26种敏感sink生成利用。
Pasted image 20230606152033

作者排除了导致LFI和XXE的sink,因为这些漏洞在PHP5.4中不可复现。对于SQLi,可以识别POP链;然而,模糊器无法到达敏感的接收器,因为这样的链需要一个数据库帐户或一个已经连接到数据库的实例。作者把这个问题留给未来的工作吧。
PS:反序列化导致sqli确实很少。

Pasted image 20230606153528

表2展示了FUGIO和Dahse识别可利用链数量的对比。因为Dahse论文中没有详细介绍链的细节,作者只对比了数量。

PS:除了OWA,其余的效果都不如Dahse

FUGIO比Dahse在Contao、Piwik和Joomla缺失6个E链,由于以下原因无法生成利用对象:

  1. 四个链需要通过复杂的条件来达到sink函数或控制sink函数的参数。
  2. 有两个链需要特定的操作系统环境和特定的文件或目录的存在才能到达sink函数。
    这些限制源于模糊测试的不可靠性质;作者相信,更先进的模糊优化和计算资源将减少漏报的数量。

需要注意的是,Dahse等人的静态方法报告了10个误报,这是由于静态未解析的调用站点阻碍了它们的污点数据流分析。这意味着需要审查者人工检查所有报告的可利用性。包括这些误报,相反,FUGIO没有误报。

7.3.2 Comparison to PHPGCC

Additional setup:当模糊器生成至少一个利用时,终止fuzz,以探索调用其他sink的不同链。然而,PHPGCC列出了多个共享相同sink的链。因此作者设置对于对应的sink,在fuzz所有枚举的链前不停止fuzz测试。

表3展示了对比实验结果,第三列是FUGIO识别出来的链数,第四列表示对于这些识别出来的链生成的利用数,最后一列显示了FUGIO发现的新的可利用链的数量,以及它们的利用对象。

PS:最后一列为啥比前面的多?
第三列表示的是啥?

Pasted image 20230606155200
对于PHPGGC中的39个可利用的链,FUGIO报告了34个链和22个利用这些链的对象。此外,FUGIO还发现了32个新的可利用链及其利用对象,总共产生了54个可利用链及其利用对象。

在PHPGGC的总共39条链中,FUGIO识别出了34条POP链;作者分析了这5个漏报的根本原因。在ZendFramework, Guzzle, 和 Drupal7中,FUGIO没有找到四个链,因为这些应用程序使用反射调用来连接两个gadget。

PS:反射调用具体示例,为啥没检测出来?文中举了一个ZendFramework的例子。

剩下的一个漏报利用了PHP内置的gadget,作者说这不在他们的范围内;FUGIO专注于在目标应用程序中组装用户定义的gadget。

在34个已识别的链中,FUGIO生成了22个E链的利用对象。缺失的链条有两个原因。

  1. 9个:其中的几个属性的数组值包含其他对象。因为这些对象需要FUGIO将一个持有适当索引及其相应对象的数组值赋给一个对象属性,所以FUGIO不能在给定的模糊超时时间内生成这些复杂的对象。
  2. 3个,由于测试其他链的时间预算不足,FUGIO没有进行模糊测试。当对这三条链进行模糊测试时,FUGIO产生了可用的利用。

作者强调FUGIO报告了PHPGGC没有列出的新的32个E链。这些结果表明,fuguo能够帮助开发人员通过AEG找到可利用的链,这可能由于困难的面向属性的编程而被遗漏。

PS:哪来的新的32个链?

7.4 Hyperparameters

作者评估了FUGIO中两个参数的有效性,分别是超时时间和最大链长度。

超时时间:作者从30个应用中的每个sink,最多采样了10条链,总共采样的链数为13582。对于每条采样的链,作者运行fuzzer5次,每次不超过300s。fuzzer生成共408个E链,和662个(PE+E)链。对于每个PE或E链,作者测量了生成利用的时间,然后,作者计算了在特定时间内生成利用对象的链的数量,从0到300秒,增量为10秒。结果如图5a。

PS:E链:可以利用的链,PE链:可能可以利用的链。
Pasted image 20230606165451
然后,作者计算了模糊器在指定时间内生成利用对象的链占在300秒内生成利用对象的总链的百分比。然后,作者将模糊超时设置为百分比大于70%时的时间。对于可利用的链(E),100秒时百分比为70.1%,而对于可能可利用和可利用的链(PE+E)50秒时百分比为70.2%。因此,我们在所有实验中将模糊超时设置为100秒(0x07)。

PS:为什么是70%?

链长度:当识别POP链时,链识别器将调用树的高度作为一个参数,意味着FUGIO生成POP链的最大长度。同上面超时时间的实验,将链的最大长度从1到10,并针对链的最大长度进行条件采样,如链最大长度为3时,从链长度为1,2,3的范围内采样。

FUGIO为采样链进行了5次模糊测试,使用了100秒的超时。对于每个最大链长度,作者计算了生成的E链以及PE链和E链的总数。图5b展示了实验结果。
Pasted image 20230606192445

当将最大链长从7更改为8时,链数减少。这是因为一个模糊的超时预算限制了一个模糊的所有有希望的链来生成利用对象。因此,作者将最大链长设置为7。

7.5 Field Test

对最新版本的WordPress 5.4.2 with WooCommerce 和 Concrete5 8.5.4进行了测试。在WordPress中,FUGIO为7个敏感sink识别了39条POP链,并报告了一个可利用链。在Concrete5中,FUGIO在201个敏感sink的5016条链中发现了4条可利用的利用链。三个链及其可利用的对象使攻击者能够删除任意文件,而剩下的一个链及其利用对象允许攻击者使用其选择的参数调用任何用户定义的函数。

Disclosure:Wordpress通知这个漏洞已经在2018年被报告了,Concrete5分配了CVE-2021-40102。

7.6 Case Studies

Pasted image 20230606213229

Pasted image 20230606213237

0x08 Discussion and Limitations

反序列化威胁广泛存在于Python,Java,Ruby,.NET等,有提出用JSON,XML,YAML替换序列化,但是这几种格式也存在漏洞,如.NET和Java中的JSON库等。白名单和黑名单也是一种被动的方法,这种方法通过利用每种语言支持的特性或在反序列化黑名单类时引发错误来限制允许或不允许反序列化哪些类。然而,这种方法需要大量的工程成本来指定允许的(去)序列化的类。

FUGIO也有限制,FUGIO仅仅组合从目标PHP应用提取的链,没有考虑PHP内部的类。在枚举POP链时,FUGIO也无法覆盖哪个目标调用者是静态不可确定的反射调用。考虑到所有现有的目标gadget,会导致巨大数量的链进行模糊测试。计算该目标被调用者的可能值的复杂静态分析是减少假阴性的一种方法。由于模糊测试的性质,当一个目标链存在大量的条件时,发现漏洞可能需要多个活动或更长的超时时间。

Finding vulnerabilities in web applications
AEG:

许多符号执行研究试图验证各种类型的漏洞,如跨站点脚本、SQL注入或文件包含,但之前的研究没有讨论针对POI漏洞生成漏洞。此外,由于需要建模数千个内置功能,这些约束解决方法需要大量的工程工作。作者选择通过模糊测试而不是符号执行来解决这个问题。

0x10 Conclusion

作者提出了FUGIO,第一个针对POI漏洞的AEG工具。作者提出了一系列的静态分析、动态分析和模糊技术来计算POP链和生成利用。FUGIO报告了来自30个真实PHP应用程序中已知POI漏洞的68个利用对象。FUGIO还报告了两个先前未知的POI漏洞,并生成了可利用的对象,证明了FUGIO在显著减轻费力的面向属性的编程负担方面的有效性。