智能合约自动检测工具『链必验』,如何带你解锁Web3.0世界
摘要: 超级干货,查阅更多安全检测项,可复制下面网站阅读。
【链必验】智能合约自动检测工具,可用来检测区块链智能合约漏洞。平台针对每个用户模拟了一条单独的测试链,用户可以自主在测试链上对智能合约进行部署、测试和验证,是集智能合约开发、测试、验证于一体的综合平台。
在验证的过程中,平台采用形式化验证等技术,对执行环境进行建模,通过数学推理等方法对安全属性进行验证,发现合约在运行时可能出现的安全问题,协助合约开发者发现合约中的潜在安全隐患,定位漏洞产生的位置,增强合约的安全性。主要包含四大方面的检测:代码规范检测、标准规范检测、函数调用检测、业务逻辑安全检测。
Web3.0世界,最不可或缺的,便是智能合约。今天,跟着我们一起来学习这款智能合约自动检测工具,一起解锁Web3.0世界。
ONE 代码规范检测
1.内存ABIEncoderV2数组
等级:ERROR
描述:0.4.7-0.5.9版本solc编译器存在一个BUG,此BUG会导致abi.encode接口处理多维数组时产生错误结果。
样例
在编译器版本为0.5.9时,嵌套数组badArr的返回值是错误的,为 [[1, 2], [2, 3], [3, 4]]。在编译器版本为0.6.7时,嵌套数组badArr的返回值是正确的,为 [[1, 2], [3, 4], [5, 6]]。
修复建议:避免使用0.4.7-0.5.9编译器,或禁止使用0.4.7-0.5.9编译器中的abi.encode接口。
2.多重构造函数
等级:ERROR
描述:在0.4.22版本的编译器中,合约允许合约同时存在两个格式的构造函数(以constructor关键字声明构造函数,或以合约名声明函数)。因此构造函数中的变量存在相互覆盖的危险。
样例
constructor()中初始化x为1,而Test()初始化x为2,位置靠后定义的构造函数会失效,因此x最终会被初始化为1。
修复建议:只使用一种构造函数。
3.公开的mapping嵌套结构变量
等级:ERROR
描述:公开的变量有一个默认的只读getter函数,但公开的mapping嵌套引用结构将导致非法的getter函数。
样例
公开的mappingm嵌套了引用类型struct,将导致针对public变量的默认的读取方法m[1].a失败。
修复建议:避免使用public mapping嵌套引用类型,或者使用pragma experimental ABIEncoderV2。
4.从右到左读写控制字符
等级:ERROR
描述:Unicode [U+202E] 强制编译器从右到左读取,与正常顺序相反,可能误导使用者。
样例
_f函数期待输入值i,j,m依次传递给a,b,c由于有U+202E的存在,输入函数需要以j,i,m的顺序给出。
修复建议:避免使用U+202E字符。
5.状态变量覆盖
等级:ERROR
描述:合约的继承包括状态变量的继承,在子合约中重载基类合约的状态变量可能会造成变量的使用逻辑错误。
样例
合约Test是Base的子合约,Test中a的定义重载了Base中的状态变量a。调用f1()将返回Base中的a,调用f2()将返回Test中的a。
修复建议:避免重载基类合约状态变量。
6.未初始化的storage变量
等级:ERROR
描述:未初始化的storage状态变量的地址将指向第一个状态变量的地址,使用它可能造成数据覆盖或数据丢失。
样例
未初始化变量st的存储指针将指向状态变量a,st.b的赋值将覆盖变量a使a的值变为2。
修复建议:使用前先初始化storage局部变量,或改用memory局部变量。
7.常量函数改变状态
等级:WARNING
描述:在Solidity 0.5.0之前,其可变性被定义为constant/prue/view的函数,但更改了函数体中的语句。这样的函数可以编译,但是只报warning,进一步调用该函数会失败。这个问题在0.5.0以后的版本中已经修复,常量函数中实现的状态修改无法编译。
样例
变量a在f()函数中改变了它的值,但是a()函数被标记为view。所以调用 f() 不会改变 a。
修复建议:确保Solidity 0.5.0之前的合约的可变性是正确的。
8.删除包含mapping的结构
等级:WARNING
描述:使用delete重置包含mapping的struct时,struct中的mapping不会被重置,这可能导致后续逻辑错误。
样例
合约在初始化后实例化了一个struct a,并将a.i初始化为10以及a.j[10]初始化为100。在f1中使用delete a重置了结构a。f2()中可以读取数据,结果是变量a.i已经被重置为0,但是a.j[10]中的数据仍然是100。
修复建议:避免使用delete重置包含mapping的struct。
9.返回值失配
等级:WARNING
描述:在returns语句中声明了返回名称及类型,但实际返回值与声明中变量不符。
样例
函数f()定义了一个返回类型和名称为uint a,而return语句直接返回100,与return声明不匹配。
修复建议:确保return语句中的值与returns语句中的返回声明相匹配。
10.重用基类构造函数
等级:WARNING
描述:合约之间允许继承,子合约继承父合约的状态变量、函数、构造函数。当子合约继承了多个构造函数时,可能多次重用构造函数。
样例
Test1和Test2都是继承了合约test的子合约,并重用了构造函数将各自的状态变量a初始化为1和2,合约Test3继承了合约Test1和Test2,因此具有两个不同的构造函数,导致Test3中状态变量a被多次赋值,并最终赋值为2。
修复建议:确保子合约拥有唯一继承的构造函数。
TWO 标准规范检测
1.未检查转账操作
等级:ERROR
描述:当合约定义ERC20标准的transfer/transferFrom接口时,需要检查transfer/transferFrom接口的返回值,否则会导致对转账状态的判断错误。
样例
token.transferFrom(msg.sender, address(this), amount);的返回值需要检验。
修复建议:对所有转账函数的结果进行校验。
2.错误的ERC20接口
等级:WARNING
描述:在定义标准的ERC20接口时与标准ERC20接口不完全一致。
样例
ERC20标准的Transfer事件是event Transfer(address indexed from, address indexed to, uint256 value);。
修复建议:完全参照ERC20标准设置ERC20事件和接口。
3.错误的ERC721接口
等级:WARNING
描述:定义的标准ERC721接口和标准ERC721接口并不完全相同。
样例
函数ownerOf(uint256 tokenId)是ERC721的接口,但缺少参数或返回值。
修复建议:对照ERC721
THREE 函数调用检测
1 受控的代理调用
等级:ERROR
描述:委托调用是调用合约的一种方式。委托调用的操作空间在调用发起这一方,因此没有任何权限控制或调用地址未知的调用是可被入侵的。
样例
addr可以被调用者任意操控。
修复建议:为执行delegatecall所在函数设置权限控制,指定调用者。
2.未检查的底层call
等级:ERROR
描述:智能合约的底层调用具有返回数据,调用合约执行失败不会导致调用发起合约执行失败,如果调用操作失败且没有检查返回值,可能导致预计逻辑与实际状态出现差异。
样例
address(f).call(abi.encodePacked(function_selector)); 实现了对合约Base中函数f()的调用。但是使用了call没有对调用的返回值做校验,将导致无法判断调用状态。
修复建议:对所有底层call方法返回值进行校验。
3.未检查的send方法
等级:ERROR
描述:智能合约的转账函数send具有返回值,如果转账失败代码会继续执行,调用不会回退状态。因此使用send进行转账时应检查返回值,并以此判定转账是否成功。
样例
函数f使用send进行转移ether,由于没有对send的返回值进行校验,将不能知晓转账是否真实成功。
修复建议:使用send进行转账时,对返回值进行校验。
4.底层call
等级:INFO
描述:使用低级调用是有风险的。低级调用不检查代码是否存在或调用成功。
修复建议:避免底层call
FOUR业务逻辑安全检测
1.转账地址未知
等级:ERROR
描述:转账函数没有添加任何权限限制,且转账接受者可设定,任何人都可以获取合约资金。
样例
函数f()没有任何权限控制,且转账接受者是msg.sender。调用f()即可获得合约所有资金。
修复建议:当合约存在对外转账功能时,对包含转账函数添加正确的权限控制。
2.动态数组长度的修改
等级:ERROR
描述:在solc 0.6.0版本以下,动态数组类型的长度信息可以被直接修改,长度信息的改变将直接影响存储的数组数据。
样例
合约部署后,动态数组a的第20位数据a[20]为1,若调用f(10)将a的长度修改为10,则a[20]指向的值将丢失。
修复建议:避免对动态数组的长度直接或间接修改。
3.谨慎使用枚举
等级:ERROR
描述:在0.4.5版本以前,枚举类型的调用不会进行溢出判断。
样例
E是长度为3的enum类型,即使尝试读取E的第10个,bug()函数也不会恢复。
修复建议:避免使用0.4.0-0.4.4版本的solc编译器,或对枚举值进行区间判断。
4.锁定ETH的合约
等级:ERROR
描述:在智能合约中存在收取以太币的函数,但不存在发出币的函数,将导致以太币被锁定在合约中。
样例
函数f()有一个payable符号,但合约没有能力花费/转移以太币。
修复建议:移除收钱函数的payable属性,或添加可消耗Ether/向外转Ether的函数。
5.错误的修饰器
等级:ERROR
描述:修饰器起到一个状态/权限控制的作用,在修饰器中如果无法到达_;代码段,将无法执行函数并引起逻辑错误。
样例
修饰符 bug1() 有一个 if 语句,当bool_test 为 false 时, _; 不会到达,那么函数 use() 将不会被使用。
修复建议:保证修饰器可以到达_;代码段,正确执行修饰器功能。
6.缺少返回值
等级:ERROR
描述:在函数返回声明有返回值,但没有相应的返回实现。
样例
函数f()声明返回一个uint类型的值,而合约在函数体中缺少return关键字,这将导致返回0(uint类型的最小值 )。
修复建议:添加对应的返回值或删除返回声明。
7.重入风险
等级:ERROR
描述:调用外部合约的主要危险之一是它们可以接管控制流。在重入攻击(又名递归调用攻击)中,恶意合约在函数的第一次调用完成之前回调调用合约。这可能会导致函数的不同调用以不希望的方式交互。在call调用后改变关键状态变量容易造成重入危险。
样例
在函数f()判断地址拥有的数额大小后,使用call发送以太币,最后storage变量book在转账操作后发生变化。因此,攻击者可以循环调用f()来提取以太币。
修复建议:使用检查-生效-交互模式避免重入攻击。
8.合约自毁函数
等级:ERROR
描述:合约中包含了自毁函数,且没有使用任何身份认证,将使合约处于不稳定状态。
样例
任何人都可以通过调用f()将合约销毁并提取合约中的资金。
修复建议:尽量避免使用自毁函数,或添加正确的权限控制。
9.构造函数中存在未初始化的函数指针
等级:ERROR
描述:在合约的构造函数中存在未初始化的函数指针,直接调用这些指针将出现错误。
样例
f是constructor中的函数指针,而在函数指针完全实现之前,它已被调用。这种行为会导致部署失败。
修复建议:在函数指针完全实现之前不要调用函数指针。
10.未初始化的状态变量
等级:ERROR
描述:使用未初始化的状态变量可能导致逻辑错误。
样例
状态变量a未初始化,将被默认为0地址。在执行转账操作时,将导致以太币丢失(转账去0x0地址)。
修复建议:尽可能在声明状态变量时便初始化该状态变量。
11.可入侵的升级合约
等级:ERROR
描述:合约包含自毁函数,且初始化函数任何人可调用。
样例
函数initialize()会初始化合约,但任何人都可以调用,如果攻击者在所有者之前调用initialize(),攻击者可以随时调用kill()来使代理合约功能失效。
修复建议:在合约中将初始化函数功能在构造函数中执行,保证owner不能任意修改。
12.断言错误
等级:ERROR
描述:assert的限制条件是必须满足的。
修复建议:请查看代码逻辑寻找问题并修复。
13.整型上溢出
等级:ERROR
描述:上溢出是指运算的结果超过了结果类型所能表示的上限。
修复建议:请加上溢出判断或使用SafeMath库进行运算。
14.整型下溢出
等级:ERROR
描述:下溢出表示计算操作中的结果超过了结果类型可以表示的下限。
修复建议:请加下溢出判断或使用SafeMath
以上只选取一些案例
查阅更多安全检测项
可复制下面网站阅读
https://beosinofficial.gitbook.io/vaas-zhong-wen/
作者:成都链安;来自链得得内容开放平台“得得号”,本文仅代表作者观点,不代表链得得官方立场凡“得得号”文章,原创性和内容的真实性由投稿人保证,如果稿件因抄袭、作假等行为导致的法律后果,由投稿人本人负责得得号平台发布文章,如有侵权、违规及其他不当言论内容,请广大读者监督,一经证实,平台会立即下线。如遇文章内容问题,请联系微信:chaindd123
评论(0)
Oh! no
您是否确认要删除该条评论吗?