完全揭示了tBTC的安全漏洞。为什么分散的锚硬币很难?

释放双眼,带上耳机,听听看~!
从备受关注的主网线路到突然的紧急停工,tBTC只用了大约2天,那么发生了什么?由Keep团队撰写的事件审查报告详细指出了漏洞的位置以及相关的发现过程

编译:自由和容易

前言:从备受关注的主网线路到突然的紧急停工,tBTC只用了大约2天,那么发生了什么?由Keep团队撰写的事件审查报告详细指出了漏洞的位置以及相关的发现过程。同时,它也暴露了tBTC系统复杂设计带来的挑战。

完全揭示了tBTC的安全漏洞。为什么分散的锚硬币很难?插图

(图片来自:tucheng.com)

以下是该报告的译文:

2020年5月18日上午(UTC时间),在以太坊比特币主网上进行了约48小时的测试后,Keep团队决定触发TBTCSystem合同允许的10天紧急存款暂停。团队发现,当确定使用比特币地址类进行赎回时,在赎回存入合同方面存在重大问题。它将使未清存款的签字人存款面临清算风险,因此决定触发该暂停。

实际上,支持公开存款的存款(以及未偿还的TBTC供应的一部分)属于一个很早就加入该系统的运营商。在测试过程中,它与Keep团队保持持续沟通。暂停后不久,团队建议以1.005 BTC与1 TBTC的比率交换TBTC,以恢复TBTC的供应,从而将地址的供应恢复到99.83%。该团队将触发对未结存款的有控制赎回,以释放支持这些存款的剩余保证金。尽管没有风险,Keep团队还将协调清除系统中任何剩余的未使用的ETH

tBTC系统的粗略设计

首先,让我简要介绍一下tBTC系统的设计。 (译者吐槽:虽然是介绍,但实际上并不简单)。在tBTC系统中,有权使用BTC的人(即存款人)可以在以太坊区块链上打开tBTC存款合约。此存款是一个智能合约,可与网络中的3个签名者进行交互。这些签名者共同生成和控制一个比特币钱包(没有一个签名者可以访问该钱包)。

开立存款合约时,存款人选择几种可用手数之一(译者注:最初的设计是1BTC,目前有很多选择),一旦存款产生了钱包地址,就会将相应的BTC编号发送到比特币地址。然后,存款人向存款智能合约提交证书,以证明BTC转移已经发生并且可以铸造等量的TBTC(以太坊ERC-20代币)。这使BTC持有者可以进入tBTC系统,然后使用其TBTC令牌余额与支持它的智能合约进行交互。

为了正确地证明比特币网络上以太坊区块链上的交易,tBTC系统使用中继将有关比特币区块的足够数据传输到以太坊智能合约以确认比特币交易已经完成。已经积累了一定数量的确认。这是为了确保交易(a)存在于比特币链上,并且(b)以合理的确定性得到充分确认,也就是说,比特币区块链的叉子将无法将其删除。

为了确保控制钱包的签名者不能以未经授权的方式非法转移他们共同持有的BTC,他们必须提供相当于所存BTC价值150%的ETH押金。这些存款由存款智能合约持有,直到赎回存款为止。

当TBTC持有人打算交换BTC时,他们可以通过称为赎回的过程进行交换。赎回操作允许以太坊用户(赎回者)支付大笔资金加上少量签名费,然后分配一个比特币地址并授权三个签名者共同生成签名以完成比特币交易。从存款地址转移到指定地址。这使TBTC持有者可以退出tBTC系统并返回到比特币的区块链,同时将签名者的存款返还到各自的可用资金池中以支持新的存款。然后,签名者将赎回者支付的签名费进行分配。

活动时间表

Keep团队认为此事件起源于部署tBTC。该流程于2020年3月15日15:52(UTC时间)完成,当时创建了tBTC系统彩票池。但是,彩票池本身的部署不会使任何资金面临风险。它用于随机选择具有足够保证金支持的签名者。签署人必须通过将ETH放入保证金合约中来选择使用此彩票池。然后,合同需要签署人的额外授权才能使用他们注入保证金合同中的资金。最后,签名者必须在彩票池中注册。

在接下来的三天左右,几个签名者提供了押金并授权了彩票池,但是只有三个签名者在池中注册以在存款开放期间使用,这三个签名者实际上是由同一个人控制的。当Keep团队测试存入和赎回时,他们在准备帮助Keep进行测试时伸出了援助之手。

可以通过https://dapp.tbtc.network/上的dApp的Alpha版本查看存款,该版本当前限制为0.001 BTC存款大小。由三个签名者提供的ETH存款也设定了可以投放的TBTC数量的上限,因为每个TBTC存款都需要以ETH值的1.5倍来保证。在调查潜在问题时,dApp于2020年5月15日晚上被短暂撤回,但在得知问题后,团队于5月16日重新启用了它。此外,社区中的一些成员在当地建立了dApp版本并使用它们开设大于0.001 BTC批量的存款。

5月18日凌晨2点29分(UTC时间),控制这三个签名者的操作员试图赎回他们已经打开的存款,但发现赎回过程无法完成。

他们联系了团队的成员,后者转达了另一位团队成员指出的问题,并指出以太坊区块链上的高油价导致中继将比特币状态更新了几个区块。 Keep团队告知操作员这可能是他们所看到的问题,继电器开始使用更高的汽油价格,并在UTC时间3:07赶上进度。

在UTC时间3:13,操作员表示他们仍然无法完成兑换操作,然后Keep团队使用dApp的本地版本来调查问题。这时,观察到存款合同中的漏洞-“ Tx向错误的pubkeyhash发送值。”该漏洞表明dApp构建以证明以太坊区块链成功完成赎回的证据不正确。特别是,证据无法证明交易已将存入的BTC发送到正确的赎回者地址。

经过30分钟的调查,Keep团队怀疑问题不一定出在dApp的客户端证书中,而是在以太坊区块链上用于赎回的特定比特币地址及其可证明性。在证实这种怀疑之前,Matt Luongo通知了所涉三个方面中的两个,并做好了准备,以防万一。此后,Keep团队调查了智能合约中的问题,并确定了潜在的保证金威胁。由于签署人的押金只能在6个小时后没收赎回证书的情况下才能被扣押,因此Keep团队决定继续调查并确认合同问题,然后再采取进一步行动。

在世界标准时间4:43,Matt通知James Prestwich确认这一发现,后者写了tBTC合同的大部分内容,该合同在比特币开发方面具有丰富的经验,他仍然与bitcoin-sp有关。图书馆。 James在世界标准时间5:02确认了这一发现,Keep团队立即开始将托管的dApp URL重定向到tBTC主页,以防止新的存款被打开。

在世界标准时间5:18,Keep团队确认了问题的存在,并意识到合同不能简单地从外部进行修复,因此决定触发tBTC系统合同中的10天一次性紧急中止。此功能可以将新的存款暂停10天,但不会影响任何未结存款。为了安全起见,触发任何tBTC系统更新过程都需要钱包团队的3个技术成员中的2个手动创建以太坊交易,然后使用气隙系统对交易信息进行签名,最后将交易和签名提交给以太坊区块链该过程在世界标准时间5:45完成。

在第二天早晨(东部时间),Keep团队意识到尽管托管dApp的登录页面已重定向到tBTC主页,但是托管dApp的其他页面(例如特定的存款页面)没有此操作。最好不要将托管dApp的其余页面重定向到世界标准时间14:11的tBTC主页,而不是冒险冒险冒险地存入任何现有存款。

技术问题描述

问题的根源在于证明赎回交易实际上是在比特币区块链上进行的过程。在正常情况下,为比特币交易提供有效签名的签名者可以立即释放押金,并让赎回者负责在比特币区块链上广播交易。但是,如果tBTC系统在此阶段解除了签署方的财务义务,则签署方将有机会进行恶行。

因此,tBTC系统仅在签名者出示有效签名并证明交易在比特币区块链上被接受后才释放签名者的押金。

比特币区块链上的赎回交易已被完全确认的证据适合进行一些健全性检查。其中一项是验证比特币交易是否将由签名者共同控制的资金发送到所请求的兑换地址。这些检查由redemptionTransactionChecks函数执行:

函数redemptionTransactionChecks(DepositUtils.Deposit存储_d,字节存储_txInputVector,字节存储_txOutputVector)公共视图返回(uint256){require(_txInputVector.alidateVin(),“提供的输入向量无效”); require(_txOutputVector.alidateVout(),“提供了无效的输出向量”);字节内存_input = _txInputVector.slice(1,_txInputVector.length-1);字节内存_output = _txOutputVector.slice(1,_txOutputVector.length-1); require(keccak256(_input.extractOutpoint())== keccak256(_d.utxoOutpoint),“ Tx花费了错误的UTXO”); require(keccak256(_output.slice(8,3).concat(_output.extractHash()))== keccak256(abi.encodePacked(_d。redeemerOutputScript)),“ Tx将值发送到错误的pubkeyhash”); return(uint256(_output.extractValue()));}

观察到的错误是在最后一次检查中“ Tx将值发送到错误的pubkeyhash”

require(keccak256(_output.slice(8,3).concat(_output.extractHash()))== keccak256(abi.encodePacked(_d.redeemerOutputScript)),“ Tx将值发送到错误的pubkeyhash”);

比特币有几种类的输出脚本。最常见的类是:付给pubkeyhash(p2pkh),付给scripthash(p2sh),付给见证pubkeyhash(p2wpkh)和付给见证者scripthash(p2wsh),这就是我们所说的标准输出类。该地址表示20或32字节的哈希,检查以及有关输出脚本类的信息。类信息用于将哈希值插入标准模板,该模板将创建相应的输出脚本。

完全揭示了tBTC的安全漏洞。为什么分散的锚硬币很难?插图1

输出脚本的长度不同。所有输出脚本均以1字节为前缀。例如,标准p2pkh输出脚本的长度为25个字节或26个字节(计算其长度前缀)。输出值表示为8字节的little-endian整数,在输出脚本之前立即对其进行序列化。因此,标准输出的长度在(8 +1 + 22 =)31到(8 +1 + 34 =)43字节之间。

BTCUtils.extractHash()从标准输出中提取哈希。它通过检查输出脚本的前缀和后缀来确定哈希的位置。如果输出脚本为非标准脚本,则返回一个空字节数组。

我们已经可以看到一些模式。所有旧类的脚本都有后缀,而所有见证类都没有。除p2pkh之外的所有类的脚本都将在输出的第十个字节处开始散列,该字节位于索引处:

(_output.slice(8,3).concat(_output.extractHash()))

该表达式占用字节8、9和10并连接散列。对于见证类,字节8是长度前缀,字节9和10是模板前缀,因此很容易看出将它们连接到哈希将产生一个(长度前缀)输出脚本。但是,对于p2sh地址,此表达式不会附加模板后缀。对于p2pkh地址,它将仅提取前缀的2个字节,并且(同样)没有后缀。这意味着该表达式将修改旧的输出脚本,并且永远不会输出有效的旧的输出脚本。

字节内存_modifiedLegacyOutputScript =(_output.slice(8,3).concat(_output.extractHash()));要求(keccak256(_modifiedLegacyOutputScript)== keccak256(abi.encodePacked(_d.redeemerOutputScript)));

此代码等效于已部署的代码。意外修改旧脚本后,它将它们与未修改的旧脚本进行比较。

当_d.redeemerOutputScript是旧脚本时,此方程式将始终失败,并且事务将始终被恢复。

此错误既不会损害兑现者,也不会损害存款人的利益,也就是说,用户的资金是安全的。实际上,由于此代码验证了兑换证书,因此它仅在兑换者收到BTC之后运行。

但是,由于系统无法验证兑换是否成功,因此可以扣押签名人的押金,就像兑换失败一样。特别是,如果在签字人提供交易签名后的6小时内,尚未证明存款合同中发生了赎回交易,则赎回者可以通知合同赎回证书已过期。

赎回证书超时通知将被视为签名者被暂停的标志,这意味着签名者不符合系统要求,但系统不会将其视为恶意软件。在赎回期间,这意味着系统无法验证赎回者是否收到了他们的资金。在这种情况下,系统会没收签名人的保证金,并将全部保证金发送给兑换人以作为补偿。采用这种方法是为了防止出现以下情况:签名者在请求的赎回交易中生成签名,但是密谋在另一个交易中生成签名,然后在确认正确的交易之前竞相确认其交易。

在正常情况下,进行不良交易的签名者可能会受到惩罚,但是这次发生的事情显然是不正常的。

如何审查tBTC代码?

tBTC的初始设计将赎回限制为p2wpkh地址,并在赎回过程中强制执行此限制。但是在2月初,Keep团队的工程总监Antonio提出了一项更改,以放宽允许赎回交易的输出脚本(不仅限于p2wpkh)。在赎回期间,这是为了允许存款接受任意的比特币输出脚本,使赎回者可以灵活地接受他们喜欢使用的任何钱包。更改后保留了有问题的代码。

上面的提交消息指出:“结果通过了所有当前测试,但是未在仓库中测试过非p2wpkh输出脚本。”

在接下来的几个月中这种情况没有改变。

其他观察

上述问题并不是兑换代码中的唯一问题。实际上,即使代码被证明是正确的,由于关于OP_VERIF和OP_VERNOTIF的共识规则,恶意的赎回者仍可以指定生成无效比特币交易的输出脚本。这将迫使该交易永远不会包含在比特币区块中。在这种情况下,可以确认交易的输出脚本是无关紧要的,并且兑换方将能够保证收到押金(同时将BTC留给签署人)。也就是说,除了兑换后的错误验证之外,在请求兑换时仍然缺少验证。因此,将来的版本必须仅支持标准地址类。

值得注意的是,如果兑换者指定了一个输出脚本,该脚本将导致无效的比特币交易,则他们将能够获得签名人的存款,而签名人只能控制BTC存款,但由于签名人的存入超过150%的抵押贷款,这意味着恶意赎回者仍将受益于这种情况。

此错误的验证代码也存在于无效代码路径中,该路径已在错误纠正PR中删除。

总和误差

最后,总结一下此事件中的一些问题:

首先,在提出新提交后,Keep团队无法进行更多测试。因此,团队错过了在开发过程中抓住这个问题的机会。

在基于dApp的手动质量检查过程中,Keep团队没有验证UI中的成功交换是否导致链上的封闭存款。结果,团队错过了在手动质量检查过程中发现问题的机会。

Keep团队未在兑换入口点完全考虑输入验证。这是系统中相对较少的用户控制的数据片段之一,因此它应该是输入验证的主要考虑因素。

Keep团队没有花费足够的时间来生成用于单元测试的比特币测试向量。

Keep团队做了什么?

James Prestwich已在GitHub上发布了PR,并提供了建议的修复程序。在合并修订之前,Keep团队将在接下来的几天中进行测试。

Keep团队已调整了Bits Trail审核的计划范围;

Keep团队已与先前的审核员ConsenSys Diligence和当前的Bits Trail通信,以识别问题并让他们提供进一步的反馈。

通过提供1.005 BTC-1 TBTC比例来赎回未赎回的TBTC,Keep团队恢复了TBTC供应的99.83%。他们将使用有抵押的TBTC赎回未清的存款并释放抵押的ETH。

接下来是什么?

除技术和流程改进外,KEEP团队还将宣布在接下来的几天中如何重新部署tBTC系统。

———————————————————————————————————————————————— ——————————————————————————————

以上是Keep团队对该漏洞的完整说明。为此,比特币代码维护者Pieter Wuille也与相关人员进行了讨论并确认了问题。

完全揭示了tBTC的安全漏洞。为什么分散的锚硬币很难?插图2

此外,还建议Keep团队应与经验丰富的比特币开发人员联系,以使他们可以对比特币逻辑进行双重或三重审查,但仅审查以太坊合同部分是不够的。

Keep的创建者也肯定了这一建议。

以下是一些个人意见:

为了追求最大程度的分散,毫无疑问,tBTC系统的设计非常复杂,这导致了许多潜在的安全问题。这件事不足为奇。

在短期内,实际上很少有用户愿意尝试tBTC。在以太坊主网上正式启动后,只有3个签署者完成了注册步骤,这与协议的高门槛和早期阶段有关;

该系统确实非常分散,有关该问题的报告也写得很详细。

在短期内,一些更集中的比特币锚定硬币(例如WBTC)将发展得更快,而tBTC需要时间和更多的精力来证明自己。

人已赞赏
头条深度

Grayscale大量屯积比特币可能不会对价格产生积极影响

2020-5-23 0:21:20

头条深度

Binance必须再次“运行”吗?不要放开这个“白马”

2020-5-23 0:28:09

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索