结合GNN和专家知识检测智能合约漏洞

Combining Graph Neural Networks with Expert Knowledge for Smart Contract Vulnerability Detection

原文链接:https://ieeexplore.ieee.org/document/9477066/

摘要

主要思想是将源代码的控制和数据流语义转换为了契约图(contract graph),图中的节点表示关键变量和函数调用,用有向边表示它们之间顺序执行的关系,为了凸显图中的关键节点,还设计了节点消除的阶段来规范化契约图。然后,提出了一种全新的时态消息传播网络(temporal message propagation network)来从归一化的图中提取图特征,并将图特征与设计好的专家模式相结合,产生最终的检测系统。实验表明,在可重入性、时间戳依赖性和无限循环漏洞检测的准确率分别达到89.15%、89.02%和83.21%,比最先进的方法有了显著的提高。

1. 介绍

智能合约:是在区块链上运行的程序,可以将规则写入源代码中来管理资产,规则将在执行过程中得到严格的遵守,使得合同条款能够自动执行。由此产生的安全隐患:各领域的智能合约持有相当价值的虚拟币,攻击者可以利用漏洞窃取巨额虚拟币,破坏了基于智能合约的应用程序的信任。产生隐患的可能因素:首先,一些编程语言是不严谨的,其次,智能合约一旦部署就不可变,因此开发人员需要预测合约未来可能遇到的所有状态,这是相当困难的。

传统防御方法的缺点:传统漏洞检测方法有静态分析和动态执行,都依赖于专家定义的模式,有出错的风险,而且难以覆盖一些复杂的模式,攻击者很容易绕过模式检查。一个可能的解决方案:请专家标记合同,然后把这些带有标签的合同放入深度神经网络进行训练,预测可能存在的特定漏洞类型。该方案的缺点:要么将源代码视为文本序列,要么没有突出数据流中的关键变量,导致语义信息的不足,使得训练结果不好。

2. 相关工作

2.1 智能合约漏洞检测

早期的智能合约漏洞测试工作是通过形式化的方法来检测的,将代码或字节码作为验证系统的输入,尽管提供了强大的形式验证保证,但仍然是半自动化的。另一项相关工作是依赖于通用测试和符号执行模型。还有研究人员提出使用动态执行的漏洞检测方式,需要一个手工制作的代理合约与测试的合约进行交互,这种方法使得它们无法自动化检测。最近,也有人提出将合同的源代码转换为合同图,并构造图神经网络作为检测模型,但仍然处于起步阶段,准确度不令人满意。

2.2 图神经网络

研究领域有图分类、程序分类和图嵌入等,现有的方法主要有两类:(1)基于谱的方法,像CNN这样的神经网络推广到图结构数据上(2)基于空间的方法,采用信息传播来定义图卷积

3. 问题陈述

我们要开发一个在功能级别上检测漏洞的全自动方法,要估计每个智能合约函数f的标签,1表示有特定漏洞,0表示是安全的。

3.1 回退机制

在智能合约中,每一个函数由唯一签名标识,签名由函数名称和参数类型组成。在一个函数调用,被调用函数的签名被传递到合约中,如果签名和合约的函数匹配,则跳转至被调用的函数,否则将跳转到回退函数,即一个没有名称和参数的函数,可以任意编程。转账被认为是一个空签名,也会调用回退函数。

3.2 三种可能的漏洞

关注这几个漏洞的原因是,在实际攻击中,区块链因这三个漏洞造成的损失超过了1亿美元,出现这三个合约漏洞函数个数也相对较多。

可重入性漏洞(reentrancy)

举一个简单的例子,如fig 1所示,银行合同的提款功能由可重入漏洞。Step1,攻击者现在银行存取10以太币;Step2,攻击者调用取钱函数提取10以太币;Step3,当银行调用call.value函数向攻击者发送10以太币时,会自动调用攻击者的回退函数payable;Step4,在回退函数中,攻击者再次调用取钱函数,由于当前余额还没有设置成0,所以银行认为攻击者合同里还有10以太币,因此Step5,银行再次向攻击者转账10以太币,以此重复10次,最终攻击者将获得100以太币。

在这里插入图片描述

时间戳依赖漏洞(timestamp dependence)

当智能合约使用块时间戳作为触发条件来执行某些关键操作时,存在时间戳依赖漏洞。例如,使用未来区块的时间戳作为种子来生成随机数以确定游戏的赢家,挖矿者可以利用漏洞在短时间内自由设置未来区块的时间戳来操控游戏赢家来获取非法利益。

无限循环漏洞

即for或while循环条件永真的情况。

4. 解决方法

总体框架如fig 2所示,包括三个阶段:(1)安全模式提取阶段,从源代码中获取漏洞特定的专家模式;(2)合同图构建和规范化阶段,从源代码中提取控制流和数据流语义,并突出关键节点;(3)漏洞检测阶段,该阶段使用时间图神经网络将归一化合同图转换为图特征,并将模式特征和图特征相结合以输出检测结果。

在这里插入图片描述

4.1 专家模式提取

4.1.1 Reentrancy

可重入漏洞一般被视为call.value调用的调用,即一个函数可以通过一系列调用链最终调回自己,根据现有的研究,设计了三种子模式。

  • callValueInvocation:检查一次调用中是否存在call.value
  • balanceDeduction:检查用户余额在使用drawback函数后是否被扣除,如果在每次银行转账前扣除余额,就可以避免偷钱
  • enoughBalance:在转账到用户前检查余额是否充足
4.1.2 Timestamp dependence
  • timestampInvocation:检查函数中是否存在对block.timestamp的调用
  • timestampAssign:检查block.timestamp的值是否被赋值给另一个变量或者作为参数传递给了一个函数
  • timetampContamination:检查block.timestamp是否可能阻塞了一些关键操作
4.1.3 Infinite loop
  • loopStatement:检查函数是否拥有循环语句
  • loopCondition:检查结束循环的条件是否可以满足
  • selfInvocation:判断函数是否调用自身以及调用是否在if语句中,即判断自己是否无止境的调用自己

他们实现了一个开源工具,用来提取出合约中的专家模式,其中简单的子模式可以通过关键字匹配直接提取,复杂一点的通过语法分析获得,再复杂一些的如时间戳污染时通过污染分析获得的。

4.2 合约图的构建与规范化

我们将智能合约制定为合约图,节点代表不同的程序元素,边来表示程序元素之间的控制和数据流,并考虑它们之间的时间顺序,还设计了节点消除过程,来规范化合约图并突出重要节点。

4.2.1 合约图的构建
4.2.1.1 节点构造

由于函数中不同程序元素在检测漏洞方面并不重要,因此提取出了三类节点,即核心节点、普通节点和回退节点。

  • Core nodes:核心节点是对检测特定漏洞至关重要的关键调用情况和变量,比如:
    • reentrancy:
      • (i) 对货币转移函数或内置call.value函数的调用
      • (ii)与用户余额相对应的变量
      • (iii)可以直接影响用户余额的变量
    • timestamp dependence:
      • (i)对block.timestamp的调用
      • (ii)由block.timestamp分配的变量
      • (iii)对以block.timesamp为基数种子的随机函数的调用
    • Infinate loop:
      • (i)所有循环语句,如for和while语句
      • (ii)循环条件变量
      • (iii)函数自调用
  • Normal nodes:用于给在检测漏洞方面其辅助作用的调用情况和变量建模,例如对于时间戳依赖漏洞,不调用block.timestamp的函数和与block.timestamp间接相关的变量视为节点。
  • Fallback nodes:模拟攻击契约的回退函数,该回退函数可以与被检测函数交互,例如fig 3展示了这些节点的建模

在这里插入图片描述

函数withdraw建模为核心节点 C 1 C_1 C1,因为其中包含call.value,然后按照代码的时间顺序,将关键变量Balance[msg.sender]视为核心节点 C 2 C_2 C2,而变量amount被建模为普通节点 N 1 N_1 N1,对call.value的调用被提取为核心节点 C 3 C_3 C3,并且回退函数用节点F表示。

4.2.1.2 边的构造

由于节点在时间上彼此密切相关,而不是孤立的,为了捕获节点之间的语义依赖,我们构建了三类边,即控制流、数据流和回退边,每条边描述了被测函数可能穿过的路径,边的时间编号表示在函数中的顺序,table 1展示了语义边的种类。

在这里插入图片描述

  • Control flow edges:为条件语句或安全语句(如if、for、assert、require)等构造控制流边缘,这可以保留源代码程序所反应的编程逻辑,控制流边缘如fig 3红色箭头所示。
  • Data flow edges:数据流边涉及到变量的访问或者修改,例如Balance[msg.sender]-=amount对amount变量的访问,在fig 3中用橙色箭头表示。
  • Fallback edges:为了明确地建模回退机制,构建了两条回退边,第一条表示建立连接,第二条表示连接结束,如紫色虚线表示。
4.2.1.3 节点和边的特征

fig 4说明了边和节点的特征。

在这里插入图片描述

  • **边的特征:**表示为元组(Vstart,Vend,Order,Type),分别表示开始节点、结束节点、时间顺序和边缘类型。

  • 节点特征:对于不同节点具有不同特征:对于Invocation Node Feature 由四元组(ID、AccFlag、Caller、Type),构成其中ID、Caller、Type分别表示标识符、调用方地址、节点类型,AccFlag表示指定函数是否具有访问权,AccFlag='LimitedACC’表示函数具有有限访问权限,而AccFlag='NoLimited’表示没有限制。对于Fallback Node FeatureVariable Node Feature只有ID和Type两个属性。

4.2.2合同图的规范化

大多数GNN在传播信息时对每一个节点都是一视同仁的,忽略了一些节点的重要程度,而且不同合约函数会产生不同的图,这会阻碍GNN的训练,因此,提出了节点消除过程来规范化合约图。

4.2.2.1 节点消除

移除每一个正常节点 N i N_i Ni和回退节点 F i F_i Fi,并将该节点的特征传递到最近的核心节点上,对于具有多个最近核心节点的普通节点,其特征将会被传递到所有临近核心节点。连接到要删除的节点的边将保留下来,但是其起点或终点会移动到相应的核心节点。

4.2.2.2 特征聚合

在移除普通节点后,核心节点将会通过特征聚合其相邻普通节点来更新。准确来说,更新的特征分为三个部分:

  • 自我特征:核心节点 C i C_i Ci本身的特征
  • in特征:正常节点 P j P_j Pj合并到核心节点 C i C_i Ci,并且具有边 P j → C i P_j\rightarrow C_i PjCi的正常节点 P j P_j Pj的特征
  • out特征:正常节点 Q j Q_j Qj合并到核心节点 C i C_i Ci,并且具有边 C i → Q j C_i\rightarrow Q_j CiQj的正常节点 P j P_j Pj的特征

删除节点后聚合的特征也将会用于建模变量和调用情况。

4.3 漏洞检测

提出了漏洞检测网络CGE(Combining Graph feature and Expert patterns)。首先将提取到的子模式传递到前馈神经网络(FNN)来获得专家模式特征 P r P_r Pr;然后通过时间消息传播网络从规范化的合同图中提取出图特征 G r G_r Gr;最后使用融合网络来组合图形特征 P r P_r Pr G r G_r Gr并输出检测结果,如fig 5所示。
在这里插入图片描述

4.3.1 安全模式特征的提取

用one-hot向量来表征特定漏洞的子模式,并在每个向量上加一位,来指示被测函数是否具有子模式。对于一个特定漏洞的所有子模式的向量将拼接起来形成一个最终的向量x,将x作为输入,以函数是否具有特定漏洞作为标签,利用前馈神经网络 φ ( x ) \varphi(x) φ(x)来提取高维语义特征 P r ∈ R d P_r\in \R^d PrRd

4.3.2 合同图特征的提取

使用时间消息传播网络进一步获取合约图的语义特征,包括消息传播阶段读出阶段。在消息传播阶段,网络遵循边的时间顺序,沿着边依次传递信息,然后通过使用读出函数生成特征 G r G_r Gr,这个读出函数聚集了合同图所有节点的最终状态。

4.3.2.1 消息传播阶段

我们将归一化合约图表示为 G = { V , E } G=\{V,E\} G={V,E},其中V由核心节点组成,E由所有边组成,表示为 { e 1 , e 2 , ⋯   , e N } \{e_1,e_2,\cdots,e_N\} {e1,e2,,eN},其中 e k e_k ek表示第k个时间边。消息沿边传递,每个时间步长一条边,每个节点 V i V_i Vi的隐状态 h i 0 h_i^0 hi0用自己节点的特征初始化,然后在第k时间步骤中,消息流经第k个时间边缘 e k e_k ek,并更新 e k e_k ek的结束节点的隐藏状态 h e k h_{e_k} hek

更具体而言,消息 m k m_k mk的计算是基于开始节点 e k e_k ek的隐状态 h s k h_{s_k} hsk和边类型 t k t_k tk
x k = h s k ⊕ t k m k = W k x k + b k x_k=h_{s k} \oplus t_k\\ m_k=W_kx_k+b_k xk=hsktkmk=Wkxk+bk
其中 ⊕ \oplus 代表连接,矩阵 W k W_k Wk和偏置系数 b k b_k bk是待训练的参数,初始信息 x k x_k xk包含边 e k e_k ek和它的起始节点,它将用 W k , b k W_k,b_k Wk,bk变换成一个嵌入向量。

在收到信息后,边 e k e_k ek的终止节点利用传入的消息和其当前状态的信息来更新它的隐状态 h e k h_{ek} hek
h ^ e k = tanh ⁡ ( U m k + Z h e k + b 1 ) h e k ′ = softmax ⁡ ( R h ^ e k + b 2 ) \begin{array}{r} \hat{h}_{e k}=\tanh \left(U m_k+Z h_{e k}+b_1\right) \\ h_{e k}^{\prime}=\operatorname{softmax}\left(R \hat{h}_{e k}+b_2\right) \end{array} h^ek=tanh(Umk+Zhek+b1)hek=softmax(Rh^ek+b2)
其中 U , Z , R U,Z,R U,Z,R是参数矩阵, b 1 , b 2 b_1,b_2 b1,b2是偏置向量。

4.3.2.2 读出阶段

在连续遍历G中所有边之后,我们通过读取所有节点最终的隐状态来提取G的特征,假设 h i T h_i^T hiT是第i个节点最终的隐状态,他们发现最终的隐状态 h i T h_i^T hiT和原始状态 h i 0 h_i^0 hi0之间的差异在漏洞检测任务中是有用的,考虑:
s i = h i T ⊕ h i 0 g i = softmax ⁡ ( W g ( 2 ) ( tanh ⁡ ( b g ( 1 ) + W g ( 1 ) s i ) ) + b g ( 2 ) ) o i = softmax ⁡ ( W o ( 2 ) ( tanh ⁡ ( b o ( 1 ) + W o ( 1 ) s i ) ) + b o ( 2 ) ) G r = F C ( ∑ i = 1 ∣ V ∣ o i ⊙ g i ) \begin{aligned} s_i &=h_i^T \oplus h_i^0 \\ g_i &=\operatorname{softmax}\left(W_g^{(2)}\left(\tanh \left(b_g^{(1)}+W_g^{(1)} s_i\right)\right)+b_g^{(2)}\right) \\ o_i &=\operatorname{softmax}\left(W_o^{(2)}\left(\tanh \left(b_o^{(1)}+W_o^{(1)} s_i\right)\right)+b_o^{(2)}\right) \\ G_r &=F C\left(\sum_{i=1}^{|V|} o_i \odot g_i\right) \end{aligned} sigioiGr=hiThi0=softmax(Wg(2)(tanh(bg(1)+Wg(1)si))+bg(2))=softmax(Wo(2)(tanh(bo(1)+Wo(1)si))+bo(2))=FC i=1Voigi
其中 ⊕ \oplus 代表连接, ⊙ \odot 表示点积, W j , b j ( 1 ) , b j ( 2 ) , j ∈ { g , o } W_j,b_j^{(1)},b_j^{(2)},j\in \{g,o\} Wj,bj(1),bj(2),j{g,o}是参数。

4.3.2.3 通过Pr和Gr进行漏洞检测

首先使用卷积层和最大max pooling池化层对 P r , G r P_r,G_r Pr,Gr进行滤波,然后将滤波过后的特征向量连接起来,并将其传递到由三个全连接层和一个sigmoid层组成的网络中,最后得到预测结果 y ^ \hat y y^,该过程可以描述为:
X r = ψ ( P r ) ⊕ ψ ( G r ) y ^ = sigmoid ⁡ ( F C ( X r ) ) \begin{array}{r} X_r=\psi\left(P_r\right) \oplus \psi\left(G_r\right) \\ \hat{y}=\operatorname{sigmoid}\left(F C\left(X_r\right)\right) \end{array} Xr=ψ(Pr)ψ(Gr)y^=sigmoid(FC(Xr))
卷积层给不同的语义向量元素分配不同的权重,最大池化层突出了重要元素以避免过拟合,全连接层和非线性层用来产生最终的估计值。

5. 实验

数据集来自以太坊智能合约(ESC)和VNT链智能合约(VSC),在ESC上进行了可重入性和时间戳依赖性漏洞的实验,而在VSC上评估了无限循环漏洞。漏洞检测系统由三个主要组件组成,(1)自动代码提取工具:从源代码中提取安全模式和合同图,(2)用于规范化合同图的规范化工具,(3)通过组合模式特征和图形特征来输出结果的CGE网络。其中(1)(2)使用python实现,(3)使用tensorflow。源代码可以参考https://github.com/Messi-Q/GPSCVulDetector.

参数设置:学习率l=0.002,dropout=0.2,批大小 β \beta β=32,泛化系数 λ \lambda λ= 1 0 − 4 10^{-4} 104。对每个数据集,随机选80%作为训练集,剩下的为测试集。

在这里插入图片描述

reentrancy漏洞:首先将CGE方法和现有的五种关于可重入漏洞检测任务的方法进行了比较,如Table 2所示,测试的函数集合一定包含有call.value,没有调用的会经过关键字匹配排除在训练集之外。传统的非深度学习方法在reentrancy漏洞中的正确率都不高,而CGE达到了89.15%,比传统方法高了12%左右,证明了将图神经网络和专家模式相结合的框架在reentrancy漏洞检测中有巨大潜力。他们分析传统方法精度和召回率低的原因可能是(1)它们严重依赖简单和固定的模式(2)智能合约代码中丰富的数据依赖性和控制依赖性在这些方法中没有细粒度的细节。

Timestamp dependence漏洞:最先进的传统方法slither在时间戳漏洞的准确率也只有74.20%,可能原因是大多数传统方法是基于判断函数里是否有timestamp语句来检测漏洞。CGE在四个指标方面都有最佳性能。

Infinate loop漏洞:CGE在各项指标上都是优于其他方法,可能原因是智能合约中考虑到了关键变量和程序元素之间丰富的依赖性。

在这里插入图片描述

fig 6展示了CGE与传统方法和神经网络方法在三种漏洞的检测的指标对比,不管是何种指标 accuracy, recall, precision, and F1 score,还是何种漏洞,CGE表现的都比传统的和神经网络检测方法要优秀。

文中还表明现有的方法还无法检测共享变量重入漏洞,即当传递函数与另一个函数共享内部变量时,重入攻击也是可能的,而CGE成功的检测到了漏洞。

table 2中还显示一些神经网络训练的结果还不如最先进的传统方法,这表明盲目的将源代码视为一个序列并不适合于漏洞检测任务,而将源代码建模为图并采用图神经网络是很有前途的。GCN和DR-GCN的精度低于TMP,这可能是由于GCN和DR-GCN未能捕获数据流和控制流引起的时间信息。此外,我们将CGE优于TMP的性能提高归因于TMP不考虑已知的安全模式,并且忽略了关键变量。

在这里插入图片描述

CGE-WON表示删除图形规范化模块,CGE-WOE剔除了专家模式提取模块,只利用图形特征进行漏洞学习与检测,CGE-WOG删除了合同图特征,table 3表明这四个模块都是必要的,合同图特征对CGE的性能比安全模式特征有更高的增益。

CGE选用了三个全连接层和一个sigmoid层,使用其他的结构没有这么好的效果,例如将卷积和最大池化层替换为全连接层表示为CGE(FC),将它们替换为LSTM层,记为CGE(LSTM),然后保留卷积和最大池化层,将3个全连接层更改为1个或2个,表示为CGE(1-FC)和CGE(2-FC),最后采用平均池化层代替最大池化层,表示为CGE(AP),结果如Table 4所示,表明(1)RNN架构(如LSTM)不适合特征融合任务,(2)CGE的默认设置比五种备选方案产生更好的结果,(3)使用平均池或改变完全连接层的数量,会导致性能略有下降。

在这里插入图片描述

6. 讨论

现有的工作采用了控制流图、代码属性图和抽象语法树来表示程序代码。它们与我们的合同图之间的差异可以概括为:

(i)控制流图利用节点来建模基本块,即没有任何跳跃的直线代码,并使用边来表示跳跃,倾向于将每个节点视为同等重要。

(ii)代码属性图将语句建模为节点,并将语句之间的控制流表示为边。

(iii)抽象语法树采用源代码的抽象语法结构的树表示,它依赖于树结构,难以完全表征节点之间的丰富语义信息。

(iv)在我们的合同图中,节点用于建模变量和相关调用并被分类为不同的类别,即核心节点、正常节点和回退节点。我们还根据代码中的时间顺序显式地建模边缘的顺序,并考虑智能合约的特定回退机制。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值