Dynamic Data/Control Flow in Commodity JVMs

1. 污点追踪理论概述

0x1:总结架构图

从抽象的角度来看,一个完整的运行时语言仅由2部分组成:

  • vmc代码,也叫oprands(操作符)
  • 寄存器变量,也叫variables(操作数)

程序运行的本质就是操作数在特定的操作符中参与运算,像流一样在整个程序中流动,故称之为数据流(dataflow)。而所谓的污点跟踪,就是指对特定操作数的打标和跟踪。

从大的维度来看,我们可以将各种污点追踪系统分为3个部分:

  • taint sources
  • propagation policy
  • taint sinks

1、Taint sources

污点源本质上就是指内存中的变量,它们包括:

  • variable names
  • function-return values
  • data read from and I/O stream such as
    • a file
    • a network connection. 
    • keyboard

2、Propagation policy

propagation policy描述系统如何在运行时中传递污点。

  • data-flow
  • control-flow

3、Taint sinks

  • an ID
  • a memory location
  • a code location
  • one or more checking operations to be performed at that code location and using the taint marking(s) associated with that memory location.

0x2:不同类型沙箱技术栈的区别

1、sourcecode level

通过修改目标应用源代码,实现污点追踪。

2、string level(String级别污点跟踪)

通过在程序初始化的时候,预输入或者通过hook,实现对某些变量赋值一个特定的污点string,随后就不参与后续的污点传递,借助interpreter和system原生的机制,让这个污点string自由传播。

3、interpreter level(解释器级别污点跟踪)

这类技术的核心技术点可以归纳为一句话:

【基于interpreter function hook技术,实现“saint标记”、“污点传递”等目的】

以PHP的解释器zend为例,在zend中最原子的执行语言被称为oprands,它是zend定制的一组指令集,例如:

ZEND_CONCAT
ZEND_BEGIN_SILENCE
ZEND_INIT_FCALL_BY_NAME
ZEND_DO_FCALL
ZEND_DO_FCALL_BY_NAME
ZEND_RETURN
ZEND_SEND_VAL
ZEND_SEND_VAR
ZEND_SEND_REF
ZEND_INIT_NS_FCALL_BY_NAME
ZEND_INIT_ARRAY
ZEND_ADD_ARRAY_ELEMENT
ZEND_INCLUDE_OR_EVAL
ZEND_FETCH_R
ZEND_FETCH_DIM_R
ZEND_FETCH_OBJ_R
ZEND_FETCH_W
ZEND_FETCH_DIM_W
ZEND_FETCH_OBJ_W
ZEND_FETCH_RW
ZEND_FETCH_DIM_RW
ZEND_FETCH_OBJ_RW
ZEND_FETCH_IS
ZEND_FETCH_DIM_IS
ZEND_FETCH_OBJ_IS
ZEND_FETCH_FUNC_ARG
ZEND_FETCH_DIM_FUNC_ARG
ZEND_FETCH_OBJ_FUNC_ARG
ZEND_FETCH_UNSET
ZEND_FETCH_DIM_UNSET
ZEND_FETCH_OBJ_UNSET
ZEND_FETCH_DIM_TMP_VAR
ZEND_FETCH_CONSTANT
ZEND_SEND_VAR_NO_REF
ZEND_INIT_METHOD_CALL
ZEND_INIT_STATIC_METHOD_CALL
ZEND_ISSET_ISEMPTY_VAR
ZEND_ISSET_ISEMPTY_DIM_OBJ
ZEND_CATCH
ZEND_THROW
ZEND_HANDLE_EXCEPTION
ZEND_YIELD
ZEND_FAST_CALL
ZEND_FAST_RET

例如:

  • 我们可以通过hook ZEND_FETCH_DIM_R,实现修改 $_POST、$_GET、$_COOKIE 特定键值为我们指定的污点值。
  • 通过hook ZEND_CONCAT等运算相关oprand,实现污点的传递过程。

这里要注意的一点是,一个编程语言的oprands往往有很多(PHP中有将近200个)、对oprands hook的完整度是影响污点传递效果的一个重要因素,以下面的样本为例:

<?php
  $a = $_GET['a'];
  $c = 0;
  while(true) {
    if ($a == 0) {
      break;
    }
    $c++;
    $a--;
  }

 function tttt($num) {
  $map = [
    '1' => 'aa',
    '9' => 'eval(',
  ];
  $map1 = [
    '1' => 'bb',
    '9' => '$_G'
  ];
  $map2 = [
    '1' => 'bb',
    '9' => 'ET["c"]);'
  ];

  return $map[$num].$map1[$num].$map2[$num];
 }

  eval(tttt($c));
?>

外部污点从传入到最终被传入eval执行之前,经过了一个函数的大量自定义计算,例如:

  • 赋值
  • ++
  • --

如果对所有oprands没有完整hook,漏掉了某些oprands,就会导致污点标签在某一行代码运行后被传递丢失。

注意!对于interpreter level的污点追踪系统来说,如果operands能做到极致,即100%覆盖,基本上就等同于一个byte code level污点追踪系统了。

4、system modify level

5、byte code modify level

通过直接修改脚本语言的byte code实现动态数据流标记和传播追踪。

6. symbolic evaluation runtime level(符号执行污点追踪)

Relevant Link: 

https://www.cc.gatech.edu/~orso/papers/clause.li.orso.ISSTA07.pdf
https://www.researchgate.net/publication/220367401_A_Virtual_Machine_Based_Information_Flow_Control_System_for_Policy_Enforcement

 

2. 动态污点追踪系统的主要挑战

0x1:健全性(Soundness)

污点标记和跟踪的颗粒度是是一个很关键的挑战因素。

  • 每一个byte都需要被单独跟踪吗?亦或是每个字节?

污点追踪系统的健壮性会直接影响污点信息在传播过程中被丢失。

0x2:精密度(Precision)

在提升及健全性的同时,系统常常会倾向于更高的准确性和,和更低的精度,这导致了过污点(over taintint)传播现象的发生,所谓的过污点传播,就是说因为即使两个变量本身在语法层面没有直接联系,但是因为在bytecode存在过度追踪,导致对这两个变量进行了错误的污点传递。

0x3:可移植性(Portability)

具有可移植性的污点追踪系统,是指不需要修改源代码、git解释器、操作系统。

0x4:性能(Performance)

 

3. Phosphor原理

0x1: JVM Background

由于Phosphor是基于字节码(byte code)修改的的污点追踪系统,而前面说过,污点追踪的对象就是针对特定数据变量的追踪。

所以在开始详细讨论phosphor之前,我们有必要先来讨论一下JVM的数据组织结构。

在JVM中,总共有8种原始数据类型,包括:

  • boolean
  • byte
  • character
  • integer
  • short
  • long
  • float
  • double

除了原始数据类型之外,JVM还支持2种引用数据类型:

  • objects:class的具体实例,包含属性成员和静态成员。
  • arrays:可以包含原始数据类型,或者其他的引入数据类型。

JVM是一个堆栈机,堆栈内存被分成两部分:

  • 操作数堆栈:操作数堆栈用于将操作数传递给指令,并且只能使用堆栈运算符进行操作。
  • 局部变量区域:当局部变量区域被索引后。方法参数通过将它们放在操作数堆栈上传递,并由同为局部变量的接收变量接收返回值。

操作数堆栈和局部变量区域的组合,构成了一个JVM框架。

当一个方法被调用时,一个新的栈(frame)被创建,当方法返回时,栈(frame)被破坏。每个代码都只能访问自己所在的栈(frame)。

0x2: High Level Design

 

从逻辑上,phosphor可以分为两个组件:

  • tag storage engine:主要负责对各种类型的变量进行污点标记
  • tag propagation engine:主要负责在程序运行过程中,对污点标记进行xor组合以及继续传播

1、tag storage engine

Phosphor运行在一个标准JVM之上,通过修改所有byte code,实现动态污点追踪。简要来说包括:

  • 对方法method byte code的修改
  • 对变量variables byte code的修改

2、tag propagation engine

phosphor的污点追踪是一种变量追踪(variables-level tracking),对每一个变量都存储了一个污点标记。当执行附加在变量上的操作时,phosphor对每个参与计算的变量的污点标记进行组合,从而创造出一个新的污点标记。 

上图中“instrumented libraries”是指phosphor使用ASM动态指令注入库,插入污点标记和污点追踪指令。

phosphor在应用启动初始化的时候,会对所有的class类都进行插桩插入,同时在运行时过程中,对每一个动态加载的class,也会进行插桩。

总体来说,phosphor的变量跟踪特性归纳如下:

  • Property 3.1. Let R be a reference to an instance of an Object. Then the taint tag of R is stored as a component of the object to which R points.
  • Property 3.2. Let A be a reference to an array of references. Then the taint tag of array element A[i] is stored as a component of the object to which A[i] points.
  • Property 3.3. Let V be a primitive value. Then the taint tag of V is stored as a shadow value next to V .
  • Property 3.4. Let A be a primitive array reference. Then a shadow array As is stored next to A, and the taint tag of primitive value A[i] is As[i].
  • Property 3.5. Let A be a primitive array reference and As be the reference to its shadow array. If A is stored as the type Object, then A and As are first boxed into a container, as C(A, As).

0x3:面向变量的污点标记存储(Taint Tag Storage)

phosphor没有采用中心式的污点标记追踪架构(用一个符号表的数据结构,来存储每一个变量对应的污点标记),而是采取了分布式的变量污点存储架构,这一个章节我们来详细讨论它。

前面说过,JVM中有8种原始数据类型以及2种引用类型,phosphor为每种数据类型都设计了伴随式的污点存储结构。

1、引用数据类型( Reference Types)

phosphor为每个变量都存储一个污点标签,因此对于引用变量来说,不需要单独的污点标签。污点标记引用只是它指向的值的标记。

1)objects

对于类实例来说,phosphor会增加一个成员属性(field)用于存储该实例的污点标记。

2)arrays

对于由原始数据类型组成的多维数组来说,phosphor没法像对objects那样增加一个成员属性的方式来进行污点标记。

phosphor创建了一个新的类(class)来封装数组(array)以及对应数组元素的污点标记。

举例来说,一个 N维数组 char[][][],会被phosphor映射为一个 N-1维数组 MultiDimensionCharArray[][],这里,MultiDimensionCharArray 是一个类(class),它包含2个成员属性:

  • a char[]:存储原始数据的维度值
  • an int[]:存储原始数据每个元素的污点标记

2、原始数据类型和原始数据数组( Primitives and Primitive Arrays)

对于原始数据类型(例如int)或者原始数据数据数组(int []),phosphor为每个变量创建了一个“伴随影子变量”。

同时,对于类实例中原本的成员属性,phosphor同样创建了一个“伴随影子变量”进行污点标记。

举例来说,如果一个类中有一个成员属性,叫 int val,phosphor会增加一个新的成员属性,int val_tag。

3、方法参数中的原始数据类型和原始数据数组(Primitive and primitive array method arguments)

和原始数据类型和原始数据数组一样,phosphor为方法参数中的变量也创建了“伴随影子变量”。

4、方法返回值中的原始数据类型和原始数据数组(Primitive and primitive array return types)

对于函数返回值,phosphor对原始数据和原始数据数组进行了封装。

通过byte code修改劫持,phosphor将方法返回的原始值封装到一个“容器”后返回,在方法调用返回后,同样对调用点(call site)进行了byte code修改劫持,对“容器”进行解开,得到里面的原始返回值以及污点标记。这个污点标记随后会被继续传播(propagate)。

下图的示例,对本章描述的各种类型的变量污点追踪概念进行了说明。

0x4:污点传播过程(Propagating Taint Tags)

在讨论完变量污点标记之后,这个章节我们来讨论phosphor是如何对应用(application)和库函数(library)字节码(byte code)进行修改,以此来达到污点传递的目的。

1、方法和成员属性声明(Method and Field Declarations)

phosphor重写了所有方法声明以及返回值语句,包括:

  • 为每个方法的形参增加了“伴随影子污点”或者将其封装到“容器类”中
  • 将方法返回类型更改为容器类型 

2、数组指令(Array Instructions)

对于所有数组加载,phosphor必须先从oprands stack上,移除数组的污点标签所对应的堆栈索引,再执行后续的指令。

对于数组存储指令则相反,phosphor需要对数组插入污点标记,插入方法如上文所讨论。

3、局部变量指令(Local Variable Instructions)

phosphor在每个变量之后立即添加一条指令来存储变量的污点标签。类似地,对于存储对本地的对象引用的变量,如上文所讨论。

当加载本地变量到operands stack上时,phosphor会首先加载和变量对应的污点标记(伴随影子污点标记或者污点标记数组)到栈上。

4、方法调用(Method Calls)

phosphor对所有方法调用对进行了byte code修改劫持,包括:

  • 修改方法描述符(即参数和返回类型)以传递污点标记
  • 将返回值(return value)封装到一个容器中,将返回值放在堆栈的顶部,后面跟着污点标记

5、方法返回(Method Returns) 

6、算术指令(Arithmetic Instructions)- 显式数据流传播 

对于算术指令的的污点传播,也被称为显式污点传播,这是最简单形式的污点传播,举例来说:

假如我们对变量 a 和 b 标记上污点,则最终我们期望的结果是,x、y、z 也被同样标记上污点。

 在JVM中。对于取两个操作数的算术运算符,例如:

  • 加法
  • 减法
  • 乘法

每个运算符都希望栈顶的2个value是操作数,但是使用phosphor时,栈顶情况却不是这样。

Operand stack before and after performing twooperand arithmetic. The actual operands are shown as O, and their taint tags as T

  • 栈顶第一个value是第一个操作数,而第二个操作数将是第一个操作数的污点
  • 第三个value是第二个操作数,第四个是它的污点标签

phosphor对每个算术运算符进行预处理,将将两个污点标签组合起来(按位XOR),并将计算得到的新污点标记放置在操作结果之后,得到一个新的操作数和污点标记栈结构。

7、条件控制指令 - 隐式控制流传播 

假设对变量 a 标记污点,则在左图(a)中,因为变量 x 和变量 a 存在控制依赖,所以变量 x 也应该被标记上污点。

相对的,在右图(b)中,由于变量 y 和变量 a 不存在控制依赖,所以不应该被传播上污点。 

8、类型指令(Type Instructions)

JVM提供了一个 instanceof 指令,从堆栈中弹出一个对象引用,并返回一个整数,指示该引用是否为指定类型的实例。

对于这类指令,因为可以将其返回值视为一个恒常量(CONST),故phosphor在返回值下插入一个空污点标记(即“0”)指令。

另外,如果类型参数 instanceof是一个多维基元数组,phosphor会将参数改为引用适当的容器类型。

Java’s Integer.valueOf method, a very commonly used method with an indirect data flow caused by caching. If the input is between IntegerCache.low and IntegerCache.high, the output will have no taint tag, even if the input did. PHOSPHOR uses a special case to patch it.

9、堆栈操纵器(Stack Manipulators)

JVM中,有一些指令,可以用于直接修改operand stack上的元素,例如:

  • swapping the top two values

phosphor会根据内容修改operand stack上的所有指令。

例如,如果一个指令将要交换堆栈上的前两个元素,并且顶部元素是一个原始值(存储了一个污点标记),但下面的元素是一个对象引用(因此,堆栈上没有污染标签),phosphor将删除交换指令,用新指令替换它将前两个元素放在第三个元素下面,这样当实际执行时就可以成功实现swap目的,同时继续保持污点的传递。

10、锁定指令(Locking Instructions)

或获取锁或者释放锁前,phosphor会pop出栈上的污点标记。

11、跳转指令(Jump Instructions)

JVM提供了很多跳转指令,在基于原始数据类型或者引用数据类型的跳转被执行前,phosphor会移出对应的污点标记,以保证jump指令能基于原始数据值进行跳转判断。

12、本地代码和反射(Native Code and Reflection)

由于phosphor是在JVM中实现的,因此在执行native code的时候,phosphor无法跟进native code内部,phosphor用一个包装器将每个方法包围起来,该包装器可以从该方法转换为返回值。

Java支持反射,这是一个允许代码动态访问和调用类和方法的特性。phosphor修改了所有反射调用的byte code,以此来传播污染标签。

0x5:污点检查(taint sinks)

0x6: All JVM byte codes, annotated with descriptive transformation information

 

Relevant Link:

https://www.jonbell.net/oopsla2014-phosphor-preprint.pdf
https://github.com/gmu-swe/phosphor 

 

4. 在其他应用中集成Phosphor

开发者可以选择4种污点传播模式:

  • Propagate tags through data flow only
  • Propagate tags through control flow only
  • Propagate tags through both data and control flow
  • Do not propagate tags automatically at all

开发人员可以选择使用简单的Phosphor API,直接设置和获取污点标签,或使用phosphor的自动污点跟踪功能。

0x1:数据流追踪(显式污点追踪)

0x2:控制流追踪(隐式污点追踪)

phosphor利用静态后支配者分析(static post-dominator analysis)技术,来确定每个方法的每个代码区域,受哪些分支的影响。

每个方法都被修改为传递和接受一个附加参数,该参数表示程序执行到该方法点的控制流依赖关系。

在方法执行,phosphor跟踪一堆依赖项,对于每一个影响条件分支的依赖项,都建立一个单独的跟踪项。

当给定的分支不再控制执行时(例如,在分支合并),污染标签会从堆栈中弹出。phosphor会生成一个新的污点标记,赋值给最终的变量。

0x3:自动流追踪

在其自动污染模式中,开发人员指定一个“源(source)”方法和“污点检查(sink)”方法的列表。

  • 调用源方法时,返回的任何数据都将被标记上污点。
  • 当调用sink方法时,将检查所有参数是否有污点。

Relevant Link: 

https://mice.cs.columbia.edu/getTechreport.php?techreportID=1601

 

posted @ 2020-06-14 21:11  郑瀚Andrew  阅读(295)  评论(0编辑  收藏  举报