欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

Solidity 安全:已知攻击方法和常见防御模式综合列表,Part-6

来源:本站整理 作者:佚名 时间:2018-08-08 TAG: 我要投稿

一、拒绝服务
这个类别非常广泛,但其基本攻击形式都是让用户短暂地(在某些情形下,是永久)推出不可操作的合约。这种攻击可以把 Ether 永远锁在被攻击的合约中,正如 Parity 多签名钱包第二次被黑中的情形一样。
1.1 漏洞
有很多办法可以让合约变得不可操作。这里我只强调一些微妙的区块链 Solidity 编码模式,虽然看不太出来,但可能留下让攻击者执行 DOS 攻击的空间。
1.通过外部操纵映射或数组(Array)循环 ——在我的经历中,我看过此种模式的各种形式。通常情况下,它出现在 owner 希望在其投资者之间分配代币的情况下,以及,在合约中可以看到类似于 distribute() 函数的情况下:
contract DistributeTokens {
    address public owner; // gets set somewhere
    address[] investors; // array of investors
    uint[] investorTokens; // the amount of tokens each investor gets
    // ... extra functionality, including transfertoken()
    function invest() public payable {
        investors.push(msg.sender);
        investorTokens.push(msg.value * 5); // 5 times the wei sent
        }
    function distribute() public {
        require(msg.sender == owner); // only owner
        for(uint i = 0; i // here transferToken(to,amount) transfers "amount" of tokens to the address "to"
            transferToken(investors[i],investorTokens[i]);
        }
    }
}
请注意,此合约中的循环遍历的数组可以被人为扩充。攻击者可以创建许多用户帐户,让 investor 数据变得更大。原则上来说,可以让执行 for 循环所需的 Gas 超过区块 Gas 上限,这会使 distribute() 函数变得无法操作。
2.所有者操作——另一种常见模式是所有者在合约中具有特定权限,并且必须执行一些任务才能使合约进入下一个状态。例如,ICO 合约要求所有者 finalize() 签订合约,然后才可以转让代币,即:
address public owner; // gets set somewhere
function finalize() public {
    require(msg.sender == owner);
    isFinalized == true;
}
// ... extra ICO functionality
// overloaded transfer function
function transfer(address _to, uint _value) returns (bool) {
    require(isFinalized);
    super.transfer(_to,_value)
}
在这种情况下,如果权限用户丢失其私钥或变为非活动状态,则整个代币合约就变得无法操作。在这种情况下,如果 owner 无法调用 finalize() 则代币不可转让;即代币系统的全部运作都取决于一个地址。
3.基于外部调用的进展状态——有时候,合约被编写成为了进入新的状态需要将 Ether 发送到某个地址,或者等待来自外部来源的某些输入。这些模式也可能导致 DOS 攻击:当外部调用失败时,或由于外部原因而被阻止时。在发送 Ether 的例子中,用户可以创建一个不接受 Ether 的合约。如果合约需要将 Ether 发送到这个地址才能进入新的状态,那么合约将永远不会达到新的状态,因为 Ether 永远不会被发送到合约。
1.2 预防技术
在第一个例子中,合约不应该遍历可以被外部用户人为操纵的数据结构。建议使用 withdrawal 模式,每个投资者都会调用取出函数独立取出代币。
在第二个例子中,改变合约的状态需要权限用户参与。在这样的例子中(只要有可能),如果 owner 已经瘫痪,可以使用自动防故障模式。一种解决方案是将 owner 设为一个多签名合约。另一种解决方案是使用一个时间锁,其中 [13]行 上的需求可以包括在基于时间的机制中,例如 require(msg.sender == owner || now > unlockTime) ,那么在由 unlockTime 指定的一段时间后,任何用户都可以调用函数,完成合约。这种缓解技术也可以在第三个例子中使用。如果需要进行外部调用才能进入新状态,请考虑其可能的失败情况;并添加基于时间的状态进度,防止所需外部调用迟迟不到来。
注意:当然,这些建议都有中心化的替代方案,比如,可以添加 maintenanceUser ,它可以在有需要时出来解决基于 DOS 攻击向量的问题。通常,这类合约包含对这类权力实体的信任问题;不过这不是本节要探讨的内容。
1.3 真实的例子:GovernMental
GovernMental是一个很久以前的庞氏骗局,积累了相当多的 Ether。实际上,它曾经积累起 1100 个以太。不幸的是,它很容易受到本节提到的 DOS 漏洞的影响。这篇 Reddit 帖子描述了合约需要删除一个大的映射来取出以太。删除映射的 Gas 消耗量超过了当时的区块 Gas 上限,因此不可能撤回那 1100 个 Ether。合约地址为 0xF45717552f12Ef7cb65e95476F217Ea008167Ae3,您可以从交易0x0d80d67202bd9cb6773df8dd2020e7190a1b0793e8ec4fc105257e8128f0506b中看到,最后有人通过使用 250 万 Gas的交易取出了 1100 Ether 。
 
二、操纵区块时间戳
区块时间戳历来被用于各种应用,例如随机数的函数(请参阅随机数误区以获取更多详细信息)、锁定一段时间的资金、以及各种基于时间变更状态的条件语句。矿工有能力稍微调整时间戳,如果在智能合约中错误地使用区块时间戳,可以证明这是相当危险的。
一些有用的参考资料是:Solidity Docs,以及这个 Stack Exchange 上的问题。
2.1 漏洞
block.timestamp 或者别名 now 可以由矿工操纵,如果他们有这样做的激励的话。让我们构建一个简单的、容易受到矿工的剥削的游戏,
roulette.sol :

[1] [2]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载