智能合约审计-拒绝服务
2020年3月22日 星期日, 发表于 合肥
如果你对本文有任何的建议或者疑问, 可以在 这里给我提 Issues, 谢谢! :)
描述:拒绝服务漏洞(DOS)智能合约无法按照设定的方式被调用
核心问题:智能合约中的拒绝服务是一个致命的漏洞,因为漏洞导致的拒绝服务一般为永久性的,无法恢复
拒绝服务的原因
-
意外执行了SELFDESTRUCT指令
-
访问控制策略出错
-
Gas达到区块上限
-
非预期的异常抛出
漏洞分析
selfdestruct()合约自毁函数
-
指令执行后,合约将拒绝服务,地址对应的字节码将被标注为删除
-
合约地址中所有的ETH将被发送到指定的新地址
-
进行ETH转移时,即使目标地址为一个合约地址,也不会触发该地址的fallback函数,因此不需要该合约有任何的payable函数
-
如果selfdestruct函数被非预期的执行,则整个合约会拒绝服务
selfdestructGame合约分析
pragma solidity ^0.4.24;
contract selfdestructGame{
address owner;
constructor() payable {
owner = msg.sender;
}
function ownedEth() public constant returns(uint256){
return this.balance;
}
function destruct(address _who) public {
selfdestruct(_who);
}
}
漏洞点:在address owner处发送地址到_who处,由于调用了selfdestruct()函数对selfdestructGame合约进行自毁,将selfdestructGame合约balance发送到攻击者地址处。
访问控制策略错误
onwer权限变更不需要确认,因此若owner被设置为一个错误地址,合约将彻底失去管理员权限
onwerGame合约分析
pragma solidity ^0.4.24;
contract ownerGame{
address owner;
constructor() {
owner = msg.sender;
}
function changeOwner(address _new) public {
owner = _new;
}
}
漏洞点:由于owner没有权限控制,导致可以任意调用成为管理员,如果owner地址为无效地址,就会导致合约作废
Gas达到区块上限
对于每一个区块来说,有一个区块的GasLimit,任何交易的gas花费都不超过这个上限,否则无法被打包
GaslimitGame合约分析
pragma solidity ^0.4.24;
contract gaslimitGame{
mapping (uint => uint) count;
event GasLog(uint gas);
function gasUse(uint n) public {
uint gastmp = gasleft();
for(uint i=0;i<n;i++)
{
count[i]=i;
emit GasLog(gastmp-gasleft());
gastmp = gasleft();
}
}
}
非预期的异常导致拒绝服务
在竞拍合约中,竞拍进行时,每个人向合约发送自己的竞拍出价对应ETH
如果有人出价高于当前最高出价者,则合约退还当前最高出价者的出价,并且此人成为新的最高出价者
攻击者构造一个fallback函数一定会抛出异常的合约,并将合约地址成为最高出价者,则其他人将永远不能成为新的最高出价者
Auction合约分析
pragma solidity ^0.4.24;
contract Auction {
address public topBidder;
uint256 public topBid;
constructor() public payable {
require(msg.value > 0);
topBid = msg.value;
topBidder = msg.sender;
}
function bid() payable {
require(msg.value > topBid);
topBidder.transfer(topBid);
topBidder = msg.sender;
topBid = msg.value;
}
// finish the auction
function auction_end() {
// ...
}
}
contract revertContract{
function testBid(address _addr) public payable{
bytes4 methodHash = bytes4(keccak256("bid()"));
_addr.call.value(msg.value)(methodHash);
}
function ownedEth() public constant returns(uint256){
return this.balance;
}
function() public payable{
revert();
}
}
漏洞点:topBidder的攻击者合约在竞拍成功后的上一个最高价格时候,当后面的竞拍者出更高价格的时候,会触发topBidder.transfer(topBid),然而攻击者合约内使用了fallback函数,在经过transfer函数的时候会发生异常,导致交易回滚,从而使bid()函数拒绝服务。使整个合约没办法正常进行。
漏洞预防
- 严格限制selfdestruct指令的权限限制
- 设置完善合理的访问控制策略
- 如果目标地址可以是一个合约,需要考虑合约的特性