最全整理|智能合约审计工具检测内容有哪些?

目前,出现了多种智能合约审计工具,笔者整理了最常见的检测内容,可分为五大项、二十七小项。下面按分类对已有结果进行说明。

1、代码规范检测
此大项主要针对合约编写时的一些代码规范进行检测,共有十一小项。

1.1 ERC规范
包含了ETH常见的ERC20、ERC721、ERC1400、ERC1404、ERC223、ERC777等常见的合约标准检测,确保了开发人员能正确实现这些标准。
1.2 Transfer To Zero Address
在transfer、transferFrom、transferOwnership等敏感函数中,用户操作不可逆,所以建议开发者在这些函数实现中增加目标地址非零检查,避免用户误操作导致用户权限丢失和财产损失
1.3 TX Origin Authentication
tx.origin是Solidity的一个状态变量,它遍历整个调用栈并返回最初发送调用(或事务)的帐户的地址。在智能合约中使用此变量进行身份验证会使合约容易受到类似网络钓鱼的攻击。
1.4 Constructor Mistyping
构造函数仅在合约部署的时候被调用,合约owner的设置一般放在构造函数中,合约的构造函数还会执行初始化的操作。在使用function的方式定义构造函数时,如果函数名与合约名失配,就变成了一个普通函数。那么,合约将存在重大安全风险。
1.5 Complex Code In Fallback Function
合约的fallback函数通常用以接收一笔eth转账,但如果在fallback里实现过于复杂的逻辑,可能会将gas耗尽,导致转账不成功。
1.6 Unary Operation
当定义的操作的意图是将数字与变量+=相加但却意外地以错误的方式定义=+时,会出现错误。它不是计算总和,而是再次初始化变量。
1.7 Redefine Variable From Base Contracts
Solidity中同一合约或不同合约允许有相同的状态变量,他们不会构成直接威胁,在单个相当于重新定义了这个变量,在多个合约中继承使用时会出现先后关系和使用错误的情况,所以尽量避免出现相同的状态变量。
1.8 Unused Variables
Solidity中允许有未使用的变量,它们不会构成直接的安全问题,但会降低代码的可读性并且额外占用存储空间导致部署时的资源消耗增加。
1.9 No Return
如果声明一个函数有返回值,而最后没给它返回值,就会产生一个默认的返回值,而默认返回值和实际执行后的返回值可能存在差异。
1.10 Overload Syscall
对于Solidity已内置函数如assert,如果在合约中进行了重定义,可能会出现异常。
1.11 Fake Recharge Vulnerability
ERC20合约在transfer函数中可能失败,这时函数并没有异常退出,而是return false;如果交易所根据调用状态来判断转账是否成功,将会导致错误的判断。

2、函数调用检测
此大项用于检查合约中在进行函数调用时可能出现的问题,共有五小项。

2.1 Invoke Low Level Calls
call是以太坊智能合约编写语言Solidity提供的底层函数,用来与外部合约或者库进行交互。此类函数使用时需要对调用参数的安全性进行判定;delegatecall会保持调用环境不变的属性表明,构建无漏洞的定制库并不像人们想象的那么容易。库中的代码本身可以是安全的,无漏洞的,但是当在另一个应用的环境中运行时,可能会出现新的漏洞;selfdestruct自杀函数的调用会销毁合约。
2.2 Invoke Extcodesize
extcodesize在合约部署的时候为零,攻击者可以在自己的构造函数中调用受害合约,这个时候使用extcodesize验证是无效的。
2.3 Invoke Ecrecover
keccak256()和ecrecover()都是内嵌的函数,keccak256()可以用于计算公钥的签名,ecrecover()可以用来恢复签名公钥。传值正确的情况下,可以利用这两个函数来验证地址。但当ecrecover()的参数错误时候,返回0x0地址,如果_from也传入0x0地址,就能通过校验。也就是说,任何人都可以将0x0地址的余额转出
2.4 Unchecked Call Or Send Return Values
在调用call/send函数后无论执行成功还是失败都不会直接抛异常,如果不对调用返回值进行检查,函数会继续执行。
2.5 Re Entrancy
合约内错误的逻辑实现可能导致重入调用,用户能重复转走Token。

3、业务逻辑安全检测
此大项主要用于检查可能导致业务逻辑出现安全风险的问题,共有六个检查项。

3.1 Block Members Manipulation
区块参数依赖风险主要有时间戳依赖和区块哈希依赖,这种风险主要来自于使用他们生成随机数,因为它们可以被操纵或者被攻击者获取,所以不应该用于随机种子。
3.2 Arbitrary Jump with Function Type Variable
由于Solidity不支持指针算术,因此无法将此变量更改为任意值。但是,如果开发人员在最坏的情况下使用汇编指令(例如或赋值运算符),则攻击者可以将函数类型变量指向任何代码指令,从而违反所需的验证和所需的状态更改。
3.3 Check This Balance
合约代码中严格限制了合约的资金,而大多数情况下合约资金都是可变的,因此用户能轻松利用这个特性使得合约的功能逻辑无法正常执行。
3.4 Function Problem
函数永远只会以revert()等异常状态结束,无法正常执行完后return,说明函数设计出现了问题。
3.5 Call Problem
Call调用永远失败,说明函数设计出现了问题。
3.6 Denial Of Service
合约可能在恶意调用之后,造成合约拒绝服务,其他用户无法正常调用合约。

4、溢出检测。
溢出是典型的合约漏洞,可能导致检查被绕过,合约运行逻辑出错。在此大项中,VaaS主要进行了三小项的检测。

4.1 Exponent Arithmetic Overflow
4.2 Integer Overflow
4.3 Integer Underflow

5、异常可达状态检测
用于检测合约在执行过程中可能出现的异常状态,共有两个检查项。

5.1 Assert Fail
assert的限制条件是必须满足的,在条件可能不满足的情况下会报错,说明合约运行状态异常。
5.2 Require Fail
与assert类似,默认reqiure条件是可能满足的,当条件在任何情况下都无法满足会报错,说明合约运行状态异常

现在我们进行一个测试,以最近曝出的以太坊游戏合约Cheeze Wizards的漏洞为例,测试工具是一键式智能合约自动形式化验证工具Beosin-VaaS,这也是目前唯一能够检测以上五大项、二十七小项常规安全漏洞的“一键式”自动检测工具。

检测方法:
1.下载:首先在VSCode的插件市场安装一个sodility开发插件,再安装Beosin-VaaS:ETH插件。“一键式”自动对windows系统的用户,需要保证电脑装有Node.js和Visual C++库,否则插件运行时可能出错。
Node.js下载地址:https://nodejs.org/zh-cn/download/。 Visual C++库下载地址:https://visualstudio.microsoft.com/zh-hans/downloads/,选择其他工具和框架中的Microsoft Visual C++ Redistributable for Visual Studio 2019下载安装。整个测试过程均在本地完成,减少源码泄露风险。另外,工具基于VS Code 实现了ETH智能合约的代码高亮与代码补全,方便一边开发一边测试,迅速定位,快速修改。

2.业务逻辑测试方法:
因为VaaS支持异常可达状态检测,所以在使用VaaS时可以通过添加require和assert来测试函数的业务逻辑是否有问题。
最全整理|智能合约审计工具检测内容有哪些?
上图为合约中问题函数resolveTimeOutDuel的简单实现,若直接由VaaS进行检测不会发现任何问题。
最全整理|智能合约审计工具检测内容有哪些?
根据函数的说明可以知道,resolveTimeOutDuel是在决斗超时的情况下被调用,当满足一定的条件后,会将wiz2的power转移到wiz1上。根据这个逻辑我们很容易可以得到一个结论,那就是无论条件是否满足,函数执行前wiz1.power+wiz2.power和执行后的wiz1.power+wiz2.power应该相等。所以可以在函数起始和结束添加代码来描述这个逻辑:
最全整理|智能合约审计工具检测内容有哪些?
这样再通过VaaS进行检测时35行就会报告Assert Fail:
最全整理|智能合约审计工具检测内容有哪些?