《我学区块链》—— 十二、以太坊安全之 Parity 第一次安全事件漏洞分析

十二、以太坊安全之 Parity 第一次安全事件漏洞分析

       截止目前,Parity 多重签名钱包共发生过两次安全事件,第一次发生在 2017年07月19日,涉及 Parity 1.5 及以上版本,造成 15万以太币约 3000万美元被盗,第二次发生在 2017年11月07日,致使约 50万枚以太币被锁在合约中无法取出,当时价值大约 1.5亿美元,本篇先对发生于 7月19日的第一次安全漏洞做一下分析,下一篇再分析 2017年11月7日的安全漏洞。

       概括来说,黑客向每个有漏洞的合约发送了两笔交易:第一笔交易用来获取多重签名钱包的拥有权限,第二笔交易是转移合约上的全部资金。

       可从官方默认地址 paritytech/parity 检出代码,再切换到 tag v1.5.x 版本,或直接从这里 问题代码 git id 4d08e7b0aec46443bf26547b17d10cb302672835 进入,来查看完整代码。

攻击分析

       第一步:成为合约的 owner

// enhanced-wallet.sol
// gets called when no other function matches
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 = 0, msg.data.length > 0的交易,以执行_walletLibrary.delegatecall分支。由于通过 json-rpc 调用以太坊智能合约时,to参数为合约地址,而要调用的合约方法会经编码后,放在data参数中,因此代码_walletLibrary.delegatecall(msg.data)理论上能无条件的调用合约内的任何一个函数,本次安全事件就是黑客调用了一个叫做initWallet的函数:

// 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);
}

       注意参数列表中的_owners,因为是多重签名合约,所以是address[]即地址数组,该函数原本的作用是用多重所有者的地址列表来初始化钱包,函数会继续向底层调用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;
}

       经过这一步,合约的所有者就被改变了,相当于获取了 Linux 系统的 root 权限。

       第二步: 转账,以owner身份调用execute函数,提取合约余额到黑客的地址:

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);
        }
    }
}

       注意函数第一行后面的修改器限制为onlyowner,黑客进行上面的动作就是为了突破该限制。

// simple single-sig function modifier.
modifier onlyowner {
    if (isOwner(msg.sender))
        _;
}

       因此,问题的关键就在于,上面的initWallet没有检查以防止在合约初始化后再次调用到initMultiowned,进而使得合约的所有者被改成黑客。

解决方案:

       通过上面的分析可以看到,核心问题在于越权的函数调用,那修复方法便是对initWallet及与之相关的接口方法initDaylimitinitMultiowned重新定义访问权限:

// throw unless the contract is not yet initialized.
modifier only_uninitialized {
    if (m_numOwners > 0) throw; 
        _;
}

       通过检查m_numOwners变量值,若已经初始化,则直接返回(旧版 solidity 中是抛出异常),不允许再执行initWallet等方法:

// constructor - stores initial daily limit and records the present day's index.
function initDaylimit(uint _limit) only_uninitialized {
    m_dailyLimit = _limit;
    m_lastDay = today();
}
// constructor - just pass on the owner array to the multiowned and
// the limit to daylimit
function initWallet(address[] _owners, uint _required, uint _daylimit) only_uninitialized {
    initDaylimit(_daylimit);
    initMultiowned(_owners, _required);
}
// 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) only_uninitialized {
    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;
}

       可注意到,每个函数第一行的最后面都添加了限定修改器标识only_uninitialized,这就是 Parity 多重签名钱包,第一次安全事件的漏洞原理和解决办法,该漏洞发生于 2017年07月19日,致使大约 3000万美元资产被盗。下一篇我们分析 Parity 的第二次安全事件。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Although portfolio management didn't change much during the 40 years after the seminal works of Markowitz and Sharpe, the development of risk budgeting techniques marked an important milestone in the deepening of the relationship between risk and asset management. Risk parity then became a popular financial model of investment after the global financial crisis in 2008. Today, pension funds and institutional investors are using this approach in the development of smart indexing and the redefinition of long-term investment policies. Written by a well-known expert of asset management and risk parity, Introduction to Risk Parity and Budgeting provides an up-to-date treatment of this alternative method to Markowitz optimization. It builds financial exposure to equities and commodities, considers credit risk in the management of bond portfolios, and designs long-term investment policy. The first part of the book gives a theoretical account of portfolio optimization and risk parity. The author discusses modern portfolio theory and offers a comprehensive guide to risk budgeting. Each chapter in the second part presents an application of risk parity to a specific asset class. The text covers risk-based equity indexation (also called smart beta) and shows how to use risk budgeting techniques to manage bond portfolios. It also explores alternative investments, such as commodities and hedge funds, and applies risk parity techniques to multi-asset classes. The book's first appendix provides technical materials on optimization problems, copula functions, and dynamic asset allocation. The second appendix contains 30 tutorial exercises. Solutions to the exercises, slides for instructors, and Gauss computer programs to reproduce the book's examples, tables, and figures are available on the author's website.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值