Simulation of Built-in PHP Feature for Precise Static Code Analysis

文章题目: Simulation of Built-in PHP Feature for Precise Static Code Analysis.

作者: Johannes Dahse & Toorstem Holz

RIPS研究PHP静态分析几乎绕不开的工具, 一是选择的时间点好,2014年左右PHP还是比较火的。二是工具确实不多。 这篇文章发了14年的NDSS,还是有些东西值得借鉴的。

技术背景

首先, 作者在技术背景部分分析了一些PHP这种highly dynamic lanuage的痛点。

image-20201101153514155

  • 1) 动态类型/弱类型

    脚本语言在变量声明时不用声明类型的确给静态分析带来不少麻烦,而动态类型指的是在运行时类型是可以变得(its variables are not bound to a specific data type).

    1
    2
    $var1 = 1; $var2 = 'test';
    $var2 = $var1 + $var2; //1
    1. variable Variables

    这个貌似是PHP独有的,叫$$特性?

    1
    2
    3
    4
    $name = "x";
    $x = "test";
    echo $$name; //test
    $y = ${getVar()}; //变量名可以是一个表达式ZZ

    1. Dynamic Arrays

      动态数组,PHP的数组是hash table实现的。可以存任何不同的数据类型,而且在初始化的时候key值可以省略在运行时指定。

      image-20201101154933544

注意这里新添加的自动索引了6, 神不神奇?会按照“4”开始算,把"4"改成"664"就是'e'的索引就是666。(还是说脚本语言的不规范性)

    1. Dynamic Constants

      通过define()在于形式动态定一个常量, 通过constant()运行时访问。

    1. Dynamic Function

    function动态调用, 这个说的就太多了。

    image-20201101155917397

第2行向我们讲述了, 不仅变量名可以是表达式, 方法名也可以是表达式; 第三行是一个回调函数,在动态的时候才知道执行啥。另外,还有fun_get_arg()func_get_args()在运行时动态从call site拿参数; create_function()动态创建一个方法体org。

    1. Dynamic Code

      动态执行代码, 通过eval, assert,小马常用到。这些代码在动态运行时才能知道, 给静态分析增加了难度。

    1. Dynamic includes

      这个吐糟也比较多, 大型PHP项目尝尝被分割成若干文件和目录,他们的拼接为静态分析也增加了难度。类似include,使用这种操作,将文件中的代码返回到include处。尤其是现在又加了惰性加载机制。autoload()这种在静态分析目前只有猜了。

    1. Built-in Functinos
    本文关注和要解决的主要challenge。不像Java, PHP的内建函数都是用C写的, 在ZEND执行时调用。大约有228个扩展5701个内建函数, 他们在静态分析PHP代码时无法被hook到,这就导致了分析的不精确。

image-20201101162437988

如图,第二个位置月份可以是字符型, 这里如果要能探测到XSS就要对list(), printf()进行精确建模。

    1. Superglobals

      我们的超全局变量, 需要精确的标明哪些可控。开发者经常忽略FILES和SERVER的安全性。

方法设计

纵览

  1. 还是文件为单位, 首相将散乱在文件中的代码解析成main AST(脚本语言通常没有main方法做入口), 然后将user-defined function们收集起来, 将函数名, 参数等信息存入分析环境, 然后将其函数体构建成separate AST独立于main AST。

  2. 有了 AST, 下一步构建CFG(控制流图), 分析AST如果发现conditional jump , 就开辟一个新的基本块(basic block);并使用basic edge相连接。

  3. 有了CFG, 下一步构建PDG(数据依赖图)咯, 以基本块为单位,每当一个BB产生, 就分析一下他的adta flow

  4. 实现Intra(过程内)和Inter(过程间)分析,当遇到call site 就在环境中找到该方法名并进入其AST进行分析,从而实现inter能力。

  5. 实现污点分析。

(说的挺复杂,开源版的RIPS的基础分析能力还是以分析tokens流并以各种数据结构辅助追踪为主)

创新点

那么本paper novel在什么地方呢:

  • 在污点分析时添加了sanitization tags来表明数据被过滤
  • 为952个内建方法建模,从而更精准的把控整个分析流的变化
  • 在处理include file时,当作functions来处理,而不是直接加入到当前CFG,防止重复分析, 减少开销
  • 根据基本块边来总结他们之间的sanitization影响
  • 实现了后向污点分析,每一个基本块的分析结果,cache到一个变量里。
  • 使用上下文敏感字符分析(context-sensitive stirng analysis)来精炼污点分析结果,基于当前环境。

CFGBuilder

要构建一个CFG, 先定义statements, 划分基本块。

image-20201102203138487

这里作者定义了几种划分基本块的stmt, 配合算法:

image-20201102203339071

中间的循环AST上的每一个点,如果遇到四种stmt的情况。

  • JSTMT, 遇到IF, Switch, Try, Ternary, LogicalOr(后两个没见过), 就递归分析每个分支下的基本块。将入口条件加到基本块头。

  • LSTMT, 生成循环基本块

  • SSTMT, 停止,基本块下的stmts不可达

  • RSTMT, return,到达return或者是探测到程序退出,就不和下一个基本块建立连接。

    这里增量在simluate()方法上,来看。

simulating Basic Blocks

静态分析能干的事,就是编译器或者解释器能干的事。 RIPS为获取基本块内的data flow, 在控制流的基础上进行所谓的simulating,实现simulating:

  1. 制定symbols: 值-val, 变量名-varibale, 常量-constant, 数组-ArrayDimFetch... 每一个symbol都会有几个状态(type, encoding, sanitization),通过观察状态变化做到对类型,编码以及sanitize的敏感。

2)Block Summary: 此过程就是将制定的符号集在块中使用进行后向追踪。生成一些块属性(block Summary):

  * DataFlow - 记录变量/数组的assign
  * Constants - 记录常量的assign
  * GlobalDefines - 记录名称,加入global scope
  * ReturnValue - 记录块中的返回值(每个块一个返回, return和exit后的代码丢弃)
  * registerGlobals - 记录使用extract()或者import_request_varibales()注册变量

在每个块的后向data flow分析后记录以上内容, 生成block summary.

  1. Data Flow Analysis

  2. Simulating Includes and Dynamic Code: php 的includes是一个动态表达式,通过路径去找,找到加入,若无正则匹配。eval同理。

  3. Simulating Built-in Functions:

​ 本文重点。 对621个内建函数进行数据流建模, configured name and effected parameters. 分类如下:

  • Alphanumeric(284): 返回值只有字母数字组成的,effectively sanitize. 如: md5(), strlen()
  • Argument(122): 原样返回实参或部分的, 如: trim(), strrev()
  • escape(20): mysql_real_escap_string()这种, 遇到将symbol中的sanitization属性打上SQLI_SQ 和 SQLI_DO标签(如果没遇到就SQLI_NQ)
  • Substring(6): substr(), chunk_split()这种,返回实参字串的。
  • Encode(18): urlencode(), base64_encode() ...
  • Decode(25): urldecode(), base64_decode()...
  • Callbacks(51): 回调函数,如果调用函数名事字符串,就调用分析。如果是变量,正则猜测。

Intra-produral Analysis

实现intra

Inter-procedural Analysis

实现inter

Simluating Block Edges

块与块之间的simluating, 考虑validation的情况

  • operators(6): isset(), empty()...
  • Type checks(21): is_numeric()...
  • File checks(11): is_file()...
  • Whitelists(3): array_search()
  • Regex(8): 正则

(原来validatation的情况可以放到块与块间,这样就和sanitization的情况分开来去了)。

Taint Analysis

污点分析探测漏洞的过程略。:>

后记

之前看这篇觉得没啥东西, 仔细分析来东西还是挺多的, 毕竟顶会。

他几乎把所有语言特性层和面向过程层的东西讲到了, 缺少OO层的分析,也是后来不上人针对发文的。

(为什么看起来这么高大上的东西开源版本用起来和...一样)