code representation-CPG

之前提到过许多代码漏洞检测方法都会用到图表示和图神经网络,但对这些图的来源开始缺少探索,今天来探索下。

一.背景

CPG(代码属性图)是2014年提出来了,不过现在很多对漏洞的研究依旧是基于它的。CPG是结合AST(抽象语法树),CFG(控制流图),PDG(程序依赖图)3种代码表示的一种联合表示方法。并且在这篇文章里,作者试图通过图的遍历(graph traversals)来挖掘漏洞。挖掘的漏洞包括缓冲区溢出(buffer overflows),整数溢出(integer overflows),内存泄漏(memory disclosures),格式化字符串漏洞(format string vulnerabilities)。

作者认为图表示可以允许安全人员写出更复杂的漏洞模式(更复杂的规则匹配),来针对不同种类的漏洞进行检测,作者的贡献如下

  • code property graph(CPG):一种结合了AST,CFG,PDG 3种程序表示的综合图表示。
  • Traversals for vulnerability types:基于CPG可以写出图的遍历算法来挖掘程序中潜在的漏洞,CPG的诞生促成了更有效率的漏洞模式的产生。
  • Efficient implementation:将CPG导入到图数据库之后,遍历变得更有效率。

二.Representations Of Code

为了解释程序的属性,在程序分析编译器设计领域已经发展了各种不同的代码表示方法。虽然这些表示主要是为了分析和优化代码而设计的,但它们也适合于描述这篇文章所探讨的代码。不过作者集中关注3种表示:
AST,CFG和PDG。

以如下代码举例

void foo()
{
	int x = source();
	if (x < MAX)
	{
		int y = 2 * x;
		sink(y);
	}
}

这个代码包括4个statement。分别是

  • int x = source();
  • if (x < MAX)
  • int y = 2 * x;
  • sink(y);

2.1 AST

AST全称抽象语法树(abstract syntax tree)。通常由代码解析器(code parser)或者编译器(compiler)产生。AST也是很多其它表示(code representation)得基础。

AST的非终端结点(inner node)表示operator(运算或者赋值操作)。而终端结点(leaf node)表示operand(常量或者identifier)。AST可能会适用于code transformation这样的任务,也在检测相似代码片段上取得一定效果。但是AST缺乏控制流和数据依赖信息。上面代码的AST如下

在这里插入图片描述

2.2 CFG

控制流表示了每个statement的执行顺序以及需要满足的condition(if分支)。CFG的每个结点表示1个statement。结点之间通过有向边连接。而AST中的边是无向边。

通常,AST经过2个步骤可转换为CFG:

  • 用structured control statements(if, while, for等)建立初步的CFG
  • 再用unstructured control statements(goto,break等)来修正。

CFG可以用在许多安全应用上,比如检测已知恶意代码的变种以及指导fuzz testing tools。并且已成为了程序理解逆向工程的基础。尽管如此,CFG缺乏数据流信息。

上述代码的CFG如下

在这里插入图片描述
可以看到前面的1行statement这里为1个结点

2.3 PDG

程序依赖图(PDG)最初是用在程序切片(program slicing)任务当中的。

PDG有2个部分:

  • 数据依赖(data dependency edges):表示变量之间的依赖,比如下面的图中if (x < MAX)int y = 2 * x;数据依赖于 int x = source();,依赖变量是x。而 sink(y); 数据依赖于int y = 2*x; 依赖变量为y
  • 控制依赖(control dependency edges):表示对条件之间的依赖,比如下图中 C t r u e C_{true} Ctrue 表示条件为真执行。即 if 代码块中所有的statement 会控制依赖于 if 条件中的statement。这点要和CFG区分开来。

下图是上面代码的PDG
在这里插入图片描述

三.Property Graphs And Traversals

每种程序表示(AST,CFG,PDG)都从不同的角度表示程序。而CPG就是要结合这几种表示。作者在这里引入属性图(property graph)的概念,这在许多图数据库(ArangoDB,Neo4J,OrientDB)中是结构化数据的基础表示。

关于属性图的介绍。

3.1 属性图

属性图 G G G 定义如下:

  • G = ( V , E , λ , μ ) G = (V,E, \lambda, \mu) G=(V,E,λ,μ) 是一个有向图,并且每条边都有label。
  • V V V 是结点集合。
  • E ⊆ ( V × V ) E \subseteq (V \times V) E(V×V) (乘号是笛卡尔积)是一个有向边集合。
  • λ : E → Σ \lambda : E→ Σ λ:EΣ 是一个给边打标签的函数。 Σ Σ Σ 是边的标签集合。
  • μ : ( V ∪ E ) × K → S \mu: (V \cup E) \times K→S μ:(VE)×KS。给结点和边分配属性会用到 μ \mu μ 函数。 K K K 是属性key集合, S S S 是属性value集合。

一个简单的属性图示例如下:
在这里插入图片描述其中每个结点的属性key均为 k k k, 而属性值有 x , w x, w x,w 2种。( ε \varepsilon ε 不算)。挖掘属性图信息的主要步骤是graph traversals。

3.2 属性图的遍历

对于给定的结点集合 X X X V V V 的子集), 这里用到以下函数:
F I L T E R p ( X ) = { v ∈ X : p ( v ) } FILTER_p(X)=\{v \in X:p(v)\} FILTERp(X)={vX:p(v)}

p ( v ) p(v) p(v) 是一个bool函数,判断结点 v v v 是否满足一定条件,所以这是个简单的过滤函数。

对于属性图遍历有如下操作:
O U T ( X ) = ∪ v ∈ X { u : ( v , u ) ∈ E } OUT(X) = \underset{v \in X}{\cup} \{u:(v,u)∈E\} OUT(X)=vX{u:(v,u)E}

O U T l ( X ) = ∪ v ∈ X { u : ( v , u ) ∈ E    a n d    λ ( ( v , u ) ) = l } OUT_l(X) = \underset{v \in X}{\cup} \{u:(v,u)∈E \; and \; \lambda((v, u)) = l \} OUTl(X)=vX{u:(v,u)Eandλ((v,u))=l}

O U T l k , s ( X ) = ∪ v ∈ X { u : ( v , u ) ∈ E    a n d    λ ( ( v , u ) ) = l    a n d    μ ( ( v , u ) , k ) = s } OUT_l^{k, s}(X) = \underset{v \in X}{\cup} \{u:(v,u)∈E \; and \; \lambda((v, u)) = l \; and \; \mu((v,u),k)=s \} OUTlk,s(X)=vX{u:(v,u)Eandλ((v,u))=landμ((v,u),k)=s}

O U T ( X ) OUT(X) OUT(X) 返回返回 X X X 中结点所有的邻居结点, O U T l ( X ) OUT_l(X) OUTl(X) 返回 X X X 中通过类别 l l l 的边的可达结点。 O U T l k , s ( X ) OUT_l^{k, s}(X) OUTlk,s(X) 返回 X X X 中通过类别 l l l 的边并且属性 k : s k:s k:s 的可达结点。

四.Code Property Graphs

CPG定义 G = ( V , E , λ , μ ) G=(V, E , \lambda, \mu) G=(V,E,λ,μ)

  • V V V 表示结点集合,包括了AST叶子结点和statement。
  • E E E 为边集合,包括CFG,AST,PDG的边。
  • λ \lambda λ 为边分类函数,AST边可能只有一个类。CFG边可能有 ε \varepsilon ε t r u e true true f a l s e false false 几类。PDG可能有 D x D_x Dx D y D_y Dy C t r u e C_{true} Ctrue 等类。

上述代码的CPG示例
在这里插入图片描述

可以看到

  • 与AST对比以下,整个函数的AST被切分成4个statement的AST,并用CFG和PDG的边串起。
  • 与CFG相比,每个statement用它的AST来表示而不仅仅是token序列,并且多了PDG的边。
  • 与CFG同理,每个statement用它的AST表示而不是token序列,并多了CFG的边。

五.Traversals For Well-Known Types Of Vulnerablities

5.1 example

作者用了下面的c代码举例

[...]
if (channelp) 
{
	/*set signal name (without SIG prefix)*/
	uint32_t namelen =_libssh2_ntohu32(data+9+sizeof("exit-signal")); 
	channelp->exit_signal = LIBSSH2_ALLOC(session, namelen + 1);
	[...]
	memcpy(channelp->exit_signal,data + 13 + sizeof("exit_signal"),namelen);
	channelp->exit_signal[namelen] = ’\0;
	[...]
}
[...]

channelp->exit_signal = LIBSSH2_ALLOC(session, namelen + 1);namelen 是用户可控参数,所以会导致漏洞。

作者从以下几个方面分析漏洞:

  • Sensitive operations:敏感操作包括调用受保护的函数( protected functionality),缓冲区复制数据,而示例中每个statement中的算术运算(比如加法)需要多加关注,这需要访问AST。
  • Type usage:同时变量类型呀需要多加关注,如果namelen是16位而不是32位变量就不会出现漏洞。这也需要检查AST。
  • Attacker control:检测哪些data source处于用户控制之下很重要,这个示例中,_libssh2_ntohu32的返回值就是用户可控的。PDG中的数据依赖可以对此进行建模。
  • Sanitization:许多程序由于缺乏数据校验而导致漏洞,在示例中,如果对namelen进行校验,确保它的值在合适范围内,那么漏洞不会发生。CFG则可以在此派上用场。

作者定义了如下3种漏洞,不过论文对这些漏洞的描述过于抽象,我就简单说下

5.2 Syntax-Only Vulnerability

在CPG中,statement内部的问题可以通过AST解决。而statement之间的相互作用就需要CFG和PDG了。

AST层面主要有如下问题:

  • Insecure arguments
    不安全的参数,主要出自函数调用参数,比如格式化字符串漏洞(printf函数)。其中格式化字符串一个必备条件就是传递的第一个参数不是常量(%s等等)。

  • Integer overflows
    整数溢出常常发生在内存分配(malloc)的算术运算中(+*),比如LIBSSH2_ALLOC(session, namelen + 1);的第二个参数。所以在遍历AST时重点访问malloc类函数调用中的这些算术运算结点。

  • Integer type issues
    问题主要出现在赋值操作中,左边的数据类型宽度要小于右边的宽度(比如左边short,右边int),这在遍历AST的时候可能分别需要遍历赋值运算符的左右子树。

5.3 Control-Flow Vulnerability

引入CFG可以对更多的漏洞类型建模,比如

  • Resource leaks
    当资源被分配(allocate)但并没有被释放的时候,会导致系统爆内存,进而使得无法被外部访问。在CFG中,从1个分配内存空间的函数调用(malloc)开始,找不到释放这个指针的函数(free)。当然,分配内存的函数必须要返回一个指针。

  • Failure to release locks

  • Use-after-free vulnerabilities
    内存被释放后没有置为NULL,导致可能被再次利用。

5.4 Taint-Style Vulnerability

  • Buffer overflow vulnerabilities
    缓冲区溢出漏洞大多是没有对输入数据进行校验导致的。在许多linux内核代码中,当系统从get_user读取外部输入数据作为第3个参数传递给copy_from_user或者memcpy函数时会触发该漏洞。所以遍历的时候需要检查get_user的第一个参数和copy_from_usermemcpy)的第3个参数。

  • Code injection vulnerabilities
    在C语言中,注入类漏洞通常在CPG中存在从recv第2个参数到system第1个参数的路径,并且中间没有检查是否校验字符串有没有分号。

  • Missing permission checks
    没有对用户可控数据进行检查,确保他们有足够的权限。

六.参考文献

Yamaguchi, F. , Golde, N. , Arp, D. , & Rieck, K. . (2014). Modeling and Discovering Vulnerabilities with Code Property Graphs. IEEE Symposium on Security and Privacy. IEEE.

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值