静态分析之殇

0x00 前言

计算机高级语言带来了软件开发的便利性, 其解决的问题是:如何使用更接近人类的交流方式与机器进行交流。 因此不可或缺的是一个从人类阅读的语言到机器阅读的语言之间的一个桥梁。 这个转化过程叫做编译。 在编译过程中,从信息的高级语言形式,到机器码形式我们可以看到还有几层中间语言:AST, IR...其实汇编语言就很像机器语言了,可以看成他的一层壳。但是语义还不是很方便人类提取,高级语言解决了这个问题。 再看高级语言往汇编去的路上。 有AST, 为什么要有这一层,我想,因为AST是一种将语法结构化的数据结构。 而高级语言和机器语言是给人和机器去看的, 而中间语言是要被处理的。 而计算机处理一种信息最方便的方式就是将他结构化,对信息中的每一个最小单元进行定位。 而树状结构是层级的,方便与遍历token之间的关系,从而检查语法, 解析语义。 但是程序是用来执行的, 他是一种强逻辑性语言,没有歧义。 因此程序中每一条执行流都是明确的。 而如果要分析程序中的控制流, 和数据流等,AST这种结构就不太合适了。一个是因为,流分析往往是以基本块为单位的,基本块的最小单位是一条statement,而一条statement表示成AST树状结构很复杂, 也没有必要(这样就成了在树上画图,结构会很复杂,见属性图)。对于流分析,这种statement展开是没有必要的,而此时,AST也要向汇编码汇聚,所以我猜这样才出现了IR。

可以把软件分析分作两个阶段:1. Complie 阶段和2. Run-time阶段,在compile的分析都是静态阶段, 此时,程序还没有产生Activation Records。

0x01 流

静态分析, 就是自动化的去分析程序的流。 我们常见的流有一个过程内的控制流和数据流,以及过程间的调用。这些流会在程序中构造一张张的图。 控制流图, 数据流图, 调用图, 类图...。每一张图对应一种不同的层级关系或者功能。有几点需要注意:

  1. 控制流图唯一,数据流图不唯一。 控制流图往往取决于AST结构。 AST结构定了,控制流图就定了(这里我觉得我可能 有误解)。而数据流图会对分析的目的不同而不同,可以正向构建也可以后向构建。只要目的达成, 就有意义。通常过程内的流图,叫做CFG, 而过程间的(加上Call Graph)的大流图,叫做ICFG

  2. 常见的数据流分析问题有reache definition, live variable和available expression。

0x02 问题

敏感问题

其实有各种各样的敏感问题,就是指的静态分析不能cover所有情况而引起的误报。

流敏感

流敏感, 指的是一个程序的执行流上,每条语句上相同的变量可能是不同的。 这是静态下所不能覆盖所有情况的(理论上),比如一个变量初始化声明后被外部输入影响了,静态怎确定?所以纯静态只能做到相对的减少这些误报。实现流敏感一般是需要基于控制流图。

路径敏感

路径敏感,是指对执行流上的分支的, 程序实际会走那条路径,纯静态在理论上也是不能确定所有情况的。

上下文敏感

上下文是过程间的调用, 一个过程被调用,在不同的调用点(call site)其返回值也不同。

Field/Index 敏感

一个复合数组, 在理论上是可以无穷维度的, 因此静态怎么去分析? 传统的静态分析做法是, 将数组标识符看作一个变量, 而不考虑下标的影响。(静态分析解决此类问题也可以叫数组敏感哈(随便你怎么叫))

  • field敏感问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Foo foo = new Foo();
    foo.ParamA = GetUserInput();
    foo.ParamB = String.Empty;

    Execute(foo.ParamB);
    class Foo
    {
    public string ParamA;
    public string ParamB;
    }

    source点,和sink点不是一个field

  • index敏感问题:

1
2
3
4
string [] myArray = new string[len];
myArray[0] = GetUserInput();
myArray[1] = String.Empty;
Execute(myArray[1]);

数组敏感中可以看到user_input传到了索引0中,而执行的是下标1的元素。

Object 敏感

字段这种存储在object上的内容,他的生存周期不是一个函数栈, 因此他可以经由不同函数操作。实现字段敏感可使用安德森算法(流不敏感,上下文不敏感)先做个指针分析,找到每个object指针指向的内容(提高指针分析的精度对后续精度的提升目前来看可能不大)。

解决各种敏感问题,常见的思路,一个在每个program point 建立记录信息,这是静态思路。 还有就是动静结合的方式。

1
2
3
4
5
6
7
8
9
Foo foo1 = new Foo();
foo1.ParamA = GetUserInput();
Foo foo2 = new Foo();
foo2.ParamA = String.Empty;
Execute(foo2.ParamA);
class Foo
{
public string ParamA;
}

动态问题

调用

一个数据被传到一个过程中,理论上, 是可以抛出去无限层的, 每一层的返回值都不一定包含原来的信息了。因此静态很难确定, 通常的做法也是去限定层数。

循环问题

对于循环和递归,理论上,是可以有无限层的, 这也导致了,很难确定,一个变量的具体值。 对于循环,对于不同分析,需要不同考虑,比如污点分析, 可能不需要考虑回边情况。 因为循环的回边,如果没有因外的话,是回到了前必经节点,也就是说, 对信息流传播不会有影响,因为不会引入新的节点(至少我现在是这么想)。

对于递归,要做全程序分析,是不得不处理的,同循环一样,去掉自己到自己的调用边,还有的处理方式是, 在调用点建summary(啊呀具体我忘了,之后补充)。

指针分析与动态特性

这是大问题, 在java中,指针分析加反射就够受的了。 在脚本语言中, 还有各种动态特性(具体可以看RIPS14年NDSS那一篇built-in函数建模的)。这个, 再议~

0x03 web安全

这一张的题目起的比较大,这里指的其实是信息流安全, 就是web中的信息流漏洞,如各种漏洞。通常静态使用污点分析的方式去做。

数据流分析

前后向

作为一种数据流分析,没有标准, 前向和后向流分析都可以。后向分析,有时候可以排除路径爆炸,但是后向很难知道数据边上具体传的是啥。

算法

通常使用的是迭代数据流算法,即在控制流上对program point进行数据流推算,然后迭代收敛,到达不定点。

图可达性算法, 将数据流做成有向图结构,在图上作分析。[2002 Mohen]

CHECK

除了污点分析自身的各种敏感问题,和静态分析很难cover的复合结构和调用问题》 还有各种CHECK的检查问题。

在我看来, CHECK可以分为两大类: validation和sanitization。 validation就是通过分支,将不安全的输入引导一个敏感函数不可达的分支中。 而sanitization就是通过对用户输入变形, 从而让威胁失去意义。 sanitization通常有很多叫法,一般的,在输入端做的叫sanitization, 而在输入数据走完逻辑后输出的地方叫做escaping, 反正意思都是一样就是转译。

不论是validation还是sanitization都要有规则, 其中都可以用黑白名单实现。而sanitization中除了黑白名单规则,还可以进行编码,如htmlspecialchars, htmlenities,将敏感符号转义成html实体编码。

还有一种通常叫cast, 就是强制类型转换(int)$a。

以上是对数据流进行直接check, 还有间接check。

1
2
3
4
5
6
7
8
9
10
<?php
$action = $_GET['action'];
switch($action)
case "login":
echo $action;
break;
case "logout":
echo $action;
break
?>

如这个简易的路由。 这时看起来并没有显示的安全检测, 但是其实是隐式的限制,数据是在一个指定的域里才可以进入危险分支。

再如,用户输入必须在数据库中存在, 才能进入危险分支, 这种在web非常常见。

最后就是一些用户身份验证, 和表单验证。 这些常常在路径中截断控制流。 因此我们做静态分析时,很难达到一个精准的状态。

触发

静态分析到了漏洞, 不行,不一定能出发, 这也是很多扫描器和AGE工具比静态分析工具好使的原因。 在触发环节, 你可能要满足各种验证条件和访问顺序才能把控制流引过了, 且污点数据依旧有效,这具有很大的不确定性。

在web中,要达到这一点,需要考虑:

  1. 资源加载问题, 通常对于PHP这一种脚本语言, 加载代码是自动化的。 而且现在的代码通常是结构化mvc,实现自动化加载,总之。你要找到你的污点信息流在客户端的操作入口。需要分析路由(多入口web能相对简单一些)。
  2. 函数可达性, 确定资源被加载了, 就要进入该过程。
  3. 块可达性, 确定信息流入口的基本块是可达的。

Reference

https://medium.com/swlh/sensitivities-in-static-code-analysis-3cab7b07dea