《ODDFUZZ Discovering Java Deserialization Vulnerabilities via Structure-Aware Directed Greybox Fuzzing》论文笔记

文章发布时间:

最后更新时间:

ODDFUZZ: Discovering Java Deserialization Vulnerabilities via Structure-Aware Directed Greybox Fuzzing

  • Introduction

    现有技术的存在的问题挑战:

    1. 静态分析难以在精确度和召回率之间进行平衡,Java 运行时多态特性导致任意的重写方法都能用在调用链上,在分析时不可避免地盲目枚举可能的调用链就极容易遇到路径爆炸问题
    2. 现存 fuzz 思路的无效性:

      1. 注入的对象 payload 拥有多级类层次结构及其属性应该满足一定的控制流或数据流约束,fuzz 对此没有先验知识。
      2. fuzz 是基于覆盖指导,以代码覆盖率为指标,而不是以目标 sink 点为指向

      思路:

    • 执行轻量级的污点分析,识别可能的候选调用链
    • 对这些调用链的数据约束建模成树,并利用它来执行具有结构意识的 fuzzing
    • 采用 step-forward mutation strategy 和 a hybrid feedback fuzz策略

      (基于 Java fuzz 框架 JQF 实现的)

    • Background

      • 反序列化机制

        序列化和反序列化的机制支持字节流的形式跨平台传输数据以及永久性存储,同时保存每一个对象所具有的属性

        反序列化 open 的特性支持能够调用多态特性的方法,以及支持基于反射调用的行为,使得路径更加多样化

      • POP 链:反序列化的利用机制

        攻击者可以去操纵数据流和控制流

      • 威胁模型:

        假设:目标 Java 应用存在攻击者可控的反序列化入口点

        1. 构建好的序列化对象会注入到这些不受信任的入口点
        2. 目标应用会反序列化对象并自动调用攻击者可控的调用链
        3. 注入的序列化对象当中的恶意 payload 将会流入安全敏感的调用点 sink

          防御机制一般就是黑白名单,可被绕过

          Untitled

      • 定向灰盒 fuzzing

        为什么用基于定向的不用基于覆盖的?

        工作流程:静态分析阶段;fuzzing loop 阶段

        Untitled

        能使得 distance 变小的新种子将会被保留下来用于下一轮 fuzz

  • Motivation

    当前最先进的调用链查询工具 GadgetInspertor 只能检测出很少的可利用调用链,想要能获得更高的召回率(识别更多潜在调用链)以及精度(证实更多可利用的调用链),需要处理三个挑战:

    A. 运行时多态问题

    virtual 方法调用无法基于声明类型来决定具体的函数。

    最基本的就是利用 CHA 算法直接找出所有显式和因式方法调用,然而会收到路径爆炸问题的影响;

    现有技术:

    GadgetInspector 可以计算从方法参数到返回值和方法调用的数据流,并根据 类继承层次结构 和 方法重写层次结构 枚举所有可用的方法;但是其未考虑运行时多态性,攻击者可控属性可以从一个受污染的参数传播到其未被跟踪的子类参数,从而产生漏报。

    B. 结构化输入如何构建

    构建的输入需要满足语法(生成的对象是可以序列化/反序列化的)和语义(生成的序列化对象符合调用链的控制流和数据流约束)的要求

    现有技术:基于生成的 fuzzing。

    SerHybrid 通过执行指针分析生成堆访问路径(符合数据流约束,可以知道流经到 sink 点的变量所指向的对象堆访问路径),为未出现在堆路径中的字段属性分配随机值,以根据它们的类型生成有效的注入对象以供执行,根据它们的类型生成有效的注入对象以供执行。

    Untitled

    缺陷在于控制流约束的实现并不高效,字段属性被分配随机值,很难去给这些受到控制流约束的字段赋予恰当的值(对象)

    C. 以目标为导向的 fuzzing

    代码覆盖率的变化并不能使得更近 sink 点,因为控制流稍微变化,都可以改变代码的覆盖率,但是可能会离 sink 点越来越远

    Untitled

    现有技术:基于目标 fuzzing,

    DGF 优先调度能使得执行路径更靠近目标点的种子

    更靠近怎么测量?利用种子执行路径上所有基本块的距离的算术平均值

    缺陷:这样的种子距离可能存在偏差,可能并不完全对应于预期执行的gadget 链的执行路径,因为并不是每个块都会驱动种子对象执行到在已确定的链中期望的目标 sink。此外,不同种子对象的执行跟踪可能会有很大差异,并且只能在运行时得知,因为对种子对象属性的修改可能会激活多个gadget的执行。因此,期望能得到一种能有效评估生成注入对象质量的基于目标的 fuzz 反馈机制

  • ODDFuzz 设计

    A. 整体

    两部分模块组成:

    • Identifier 模块,利用待测试程序的编译文件作为输入实行轻量级的污点分析,自动化枚举所有潜在的调用链
    • Validator 模块,基于识别出的潜在调用链构建语法上有效的注入对象作为种子来 fuzz; fuzz loop 结合前向突变策略和混合的反馈机制(种子距离 + 调用链覆盖率)指导突变注入对象朝预期的 sink 点移动;当生成的注入对象到达 sink 点时,就报告产生了可利用的调用链

      Untitled

      B.污点分析部分

      构建可利用 gadget 的前提:攻击可控的污点对象是否能从入口点 source 传播到 sink 函数。

      如果可利用的 gadget 存在,那么一定存在这样一条调用路径。

      提出:轻量级基于摘要的污点分析

      第一步,生成方法摘要信息

    1. 抽离出所有 classpath 上方法的参数和 this 变量作为摘要
    2. 追踪每个方法中变量之间的信息流传播,主要关注:

      • 赋值
      • load x = o.f
      • store o.f = x
      • Call

        与参数有数据依赖关系的变量也会加入到方法的摘要当中

**第二步,调用链识别**

1. 指定可利用 magic(source 16) 方法和安全敏感调用点(sink 30) 列表

    ![Untitled](ODDFUZZ%20Discovering%20Java%20Deserialization%20Vulnerabi%20969d466604e14480b464d5a0f649092d/Untitled%205.png)

2. 基于之前计算得到的方法摘要信息,识别怀疑的 gadget

    一旦遇到一个 magic method 在 PUT 的 classpath 当中,就从这点开始执行深度遍历DFS,基于摘要信息来串联可利用的 gadgets

    为了避免死循环,设置候选调用链的最大长度作为阈值。

    如何处理多态特性?当遇到 call 函数的 caller 是被污点标记的对象时,就执行 CHA。其余和普通的基于调用图的污点分析一致

    知道遇到 sink 函数,或者调用链遇到阈值就停止。


C. 结构感知的定向灰盒模糊 fuzzing

![Untitled](ODDFUZZ%20Discovering%20Java%20Deserialization%20Vulnerabi%20969d466604e14480b464d5a0f649092d/Untitled%206.png)

- 结构化的种子生产

    必要条件:

    1. 设计嵌套对象层次结构,以反映给定 gadget 链的执行流程
    2. 赋予多级子对象恰当的属性值以便使其能到达 sink 点

    挑战:精心设计复杂对象结构的属性布局需求对于传统的 fuzzing 方案十分吃力

    解决方案:采用分层的数据结构 **属性树,其中根结点代表具有1个或多个gadget 的类对象**

    构建模块:`input generator`

    具体步骤:

    1. 初始化每个 gadget 中涉及到的类,利用反射获取所有的成员属性以构建属性树。
    2. 如果其中一个属性是另一个属性树根节点代表的对象或者继承的对象,并且该对象为 gadget 链中的下一节点,那么我们就合并两棵树。
    3. 类似地,如果其中一个属性是个接口类,而另一棵树的根节点是其实现,那么也将其进行合并
    4. 直至不再有分离但是有关联的属性树存在,迭代结束

    ![Untitled](ODDFUZZ%20Discovering%20Java%20Deserialization%20Vulnerabi%20969d466604e14480b464d5a0f649092d/Untitled%207.png)

    接着,fuzzer 就会遍历属性树将其初始化为注入对象,对于对于没有后继的节点就直接设置为 `null`

- 具有导向性的种子选择

    由于注入对象的调用路径动态时决定,导致整个突变策略是 sink-unawareness。如果没有清晰的反馈信息来指导将会陷入语义的盲目性,降低 fuzzer 的性能。