原理
根据以太坊的设计,在智能合约被部署或智能合约中的函数被调用时,代码执行需要定量的gas来保证计算被完整地完成。同时,以太坊网络限定了每个区块的最大gas总量值,区块中所有交易的gas总和不能超过此区块最大gas总量值。一旦合约中的某个操作将大量消耗gas以至于消耗的gas值达到了区块最大gas总量值,此操作将不会被成功执行,所有依赖此操作的验证步骤也将失效,合约会因此无法正常完成其余功能,从而造成一种拒绝服务状态。通常当一个合约开发者未考虑到区块gasLimit而在合约中引入了修改随时间增加大小会改变的数组等动态数据结构变量操作时,会发生此种拒绝服务攻击。攻击者可以在一个区块被开采出来后,马上以较高的gas价格发出多个交易,然后利用合约上述操作消耗整个区块gas限额,使该区块在特定的时间之前不包含其他任何交易,以阻止其他用户正常使用合约的功能。
示例
pragma solidity ^0.4.25;
contract DosFunc{
address[] listAddress;
function ifillArray() public returns(bool){
if(listAddress.length<1500){
for(uint i=0;i<350;i++){
listAddress.push(msg.sender);
}
return true;
}else{
return false;
}
}
}
该合约实例为一个记录财产权益人合约,所有合约财产权益人都会被记录到合约的listAddresses数组中,但是当前合约最多容纳1500个财产权益人。当合约财产权益人达到1500人以后,记录财产权益人的listAddresses数组将不再允许添加新的元素。
当我们调用 ifillArray函数,调用失败,显示gas超过限制,如下图所示:
通过debug调试,发现在循环中消耗gas最多的是SLOAD指令,消耗800 gas,而不是参考论文中的SSTORE指令消耗20000 gas,这里就比较奇怪了。
参考
[1]杨坤. 基于符号执行的智能合约自动化安全审计[D].电子科技大学,2020.