【智能合约系列004-以太坊安全之 Parity 第二次安全事件漏洞分析】

2017年11月,Parity多签钱包遭遇第二次重大安全事件,导致约50万枚以太币被永久锁定,价值约1.5亿美元。本次事故源于合约底层被破坏,而非资产被盗。黑客利用库合约初始化漏洞,成为owner并销毁合约,致使资金无法取出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Parity 多签钱包的第二次漏洞发生于 2017年11月07日,不同于 17年7月19日那次,本次不是资产被黑客盗走,而是合约底层被破坏,导致资产就在那,但却永远也取不出,就像驾船到太平洋最深处,投下一枚硬币,硬币就在那,但你可能再也无法找到它。

       本次漏洞导致约 50万枚以太币被锁在多签智能合约里,当时价值大约 1.5亿美元。下面我们来分析下这次漏洞的原理。

漏洞回放
       2017年11月7日,漏洞发现者在Github 的 Parity 项目网站上建了一条题为 “anyone can kill your contract” 的问题,说自己意外地杀死了 Parity 多签库合约,并贴出他执行的交易信息。他在问题的跟随评论里进一步解释发生了什么:他本来不是合约的所有者(owner),但由于自己的合约未初始化,再次初始化时,他把自己变成了 “库合约” 的 owner,然后作为 owner 他调用了合约的 kill 方法,把库合约里的代码都抹去了。


漏洞分析
       Parity 钱包提供了一个多签合约的模板,用户使用模板可以用很少的代码,很容易生成自己的多签智能合约。实际业务逻辑都通过delegatecall内嵌式地交给库合约。这样做的一个主要好处是:多签合约的主逻辑(代码量较大)作为库合约只需在以太坊上部署一次,而不会作为用户多签合约的一部分重复部署,因此可以为用户节省部署多签合约所耗费的大量Gas。

问题代码:

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

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

       参考上面几段代码,为了修复 2017年7月19日的那个 bug,确保初始化逻辑只能被执行一次,几段函数都增加了only_uninitialized。

       这里补充一个知识点,即当库合约的函数被调用时,它是运行在调用方的上下文里。而为了能被用户合约调用,本次事件中的库合约初始化函数都是 public 的。

       这次的问题就出现在黑客直接调用了库合约的初始化函数,由于库合约本质上也不过是另一个智能合约,这次攻击调用使用的就是库合约本身的上下文,对于调用者而言,这个库合约是未经初始化的,而黑客通过初始化参数把自己设置的成了 owner,接下来又作为 owner 调用了 kill 函数,抹除了库合约的所有代码,这样所有依赖这个库合约的用户多签合约就都无法执行,而合约中的代币全部被锁在合约内无法转移。

修复方法
       显然only_uninitialized的限制仍是不严谨的,而若想不发生本次的安全事件,可进一步对initWallet、initDaylimit及initMultiowned添加internal限定类型,以禁止外部调用:

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

// constructor - stores initial daily limit and records the present day's index.
function initDaylimit(uint _limit) internal only_uninitialized {
    m_dailyLimit = _limit;
    m_lastDay = today();
}

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


转自:https://blog.youkuaiyun.com/xuguangyuansh/article/details/81070173 
 

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在当今的软件开发领域,自动化构建与发布是提升开发效率和项目质量的关键环节。Jenkins Pipeline作为一种强大的自动化工具,能够有效助力Java项目的快速构建、测试及部署。本文将详细介绍如何利用Jenkins Pipeline实现Java项目的自动化构建与发布。 Jenkins Pipeline简介 Jenkins Pipeline是运行在Jenkins上的一套工作流框架,它将原本分散在单个或多个节点上独立运行的任务串联起来,实现复杂流程的编排与可视化。它是Jenkins 2.X的核心特性之一,推动了Jenkins从持续集成(CI)向持续交付(CD)及DevOps的转变。 创建Pipeline项目 要使用Jenkins Pipeline自动化构建发布Java项目,首先需要创建Pipeline项目。具体步骤如下: 登录Jenkins,点击“新建项”,选择“Pipeline”。 输入项目名称和描述,点击“确定”。 在Pipeline脚本中定义项目字典、发版脚本和预发布脚本。 编写Pipeline脚本 Pipeline脚本是Jenkins Pipeline的核心,用于定义自动化构建和发布的流程。以下是一个简单的Pipeline脚本示例: 在上述脚本中,定义了四个阶段:Checkout、Build、Push package和Deploy/Rollback。每个阶段都可以根据实际需求进行配置和调整。 通过Jenkins Pipeline自动化构建发布Java项目,可以显著提升开发效率和项目质量。借助Pipeline,我们能够轻松实现自动化构建、测试和部署,从而提高项目的整体质量和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值