智能合约安全性问题CheckList

作者:CQITer小编 时间:2018-09-14 09:23

字号

根据腾讯腾讯安全2018上半年区块链安全报告,智能合约引发的安全问题以成为区块链自身机制安全性的主要问题,本文就目前文献中提到的主流安全性问题做出总结,并列出目前的相关研究。

整形溢出(Arithmetic Issues)

如下代码,如果没有assert判断,那么sellerBalance+value可能会超出uint上限制导致溢出。

pragma solidity ^0.4.15; contract Overflow { uint private sellerBalance=0; function add(uint value) returns (bool, uint){ sellerBalance += value; // complicated math with possible overflow // possible auditor assert assert(sellerBalance >= value); } } 危险的delegatecall(dangerous delegatecall)[contractfuzzer]

首先需要了解call和delegatecall的区别:call和delegatecall都为合约相互调用时的函数,假设A调用B函数,call方法结果展示到B中,delegatecall方法结果展示到A中。

在如下示例中,Mark如果用delegatecall调用了恶意合约Steal,那么Mark合约会被删除。

复现:

1.用A账户部署Steal,用B账户部署Mark合约,并在部署时为合约附加10个ether。

2.账户B调用Mark.call(address(Steal)),即用B调用Steal的Innocence方法,实际上innocence会在Mark的上下文环境运行,发现账户B收到合约的10 ether(注意不是A账户)

3.用C账户执行Mark.deposit()方法,并附加10ether,再调用destruct方法,发现B无法收到10ether,说明合约确实已经在第二步被销毁。

pragma solidity ^0.4.2; contract Steal{ address owner; constructor () payable { owner = msg.sender; } function innocence() { log0("123"); selfdestruct(owner); } } contract Mark { address owner; constructor () payable { owner = msg.sender; } function Deposit() payable {} function call(address a) { a.delegatecall(bytes4(keccak256("innocence()"))); } } 无Gas发送(Gasless Send)[contractfuzzer]

合约C调用合约D1时,由于fallback函数修改了storage变量——这是一个消耗大量gas的操作——导致了超过fallback的gas上限(2300gas)导致fallback失败,调用D2时,由于没有超过上限,调用成功。

复现:

1.用10ether部署C合约,0ether部署D1合约,0ether部署D2合约

2.调用C.pay(1000000000000000000, address(D1)),D1的count值仍为0

3.调用D1.kill(),以太币不增加。2,3两步说明了D1的fallback调用失败

4.调用C.pay(1000000000000000000,address(D2))

5.调用D2.kill(),发现账户增加1ether,说明D2的fallback调用成功

pragma solidity ^0.4.2; contract C { address owner; constructor () payable{ owner=msg.sender; } function pay(uint n, address d){ d.send(n); } function kill() { if (owner == msg.sender) { selfdestruct(owner); } } } contract D1 { address owner; uint public count = 0; constructor () payable{ owner=msg.sender; } function() payable { count = count+1; } function kill() { if (owner == msg.sender) { selfdestruct(owner); } } } contract D2 { address owner; constructor () payable{ owner=msg.sender; } function() payable {} function kill() { if (owner == msg.sender) { selfdestruct(owner); } } } 依赖于交易顺序/条件竞争(TOD/Front Running)[smarter]

由于:

1.只有当交易被打包进区块时,他才是不可更改的

2.区块会优先打包gasprice更高的交易

所以攻击者可以恶意操控交易顺序从而使合约对自己有利。如图,出题人和做题人同时发起合约,那么做题人得到的奖励因合约执行顺序不同而不同。

1534317198703

1534317198703

再例如ERC20标准中的approve,整个流程是这样的:

1.用户A授权用户B 100代币的额度

2.用户A觉得100代币的额度太高了,再次调用approve试图把额度改为50

3.用户B在待交易处(打包前)看到了这笔交易

4.用户B构造一笔提取100代币的交易,通过条件竞争将这笔交易打包到了修改额度之前,成功提取了100代币

5.用户B发起了第二次交易,提取50代币,用户B成功拥有了150代币

function approve(address _spender, uint256 _value) public returns (bool success){ allowance[msg.sender][_spender] = _value; return true 依赖于时间戳(Timestamp Dependence/Time manipulation)[smarter]

1534316701411

1534316701411

攻击者可以修改区块的时间戳-900s以此获益。

未处理的异常(Mishandled Exceptions/Unchecked Return Values For Low Level Calls)[smarter]
责任编辑:CQITer新闻报料:400-888-8888   本站原创,未经授权不得转载
继续阅读
热新闻
推荐
关于我们联系我们免责声明隐私政策 友情链接