事件回顾
近日来,币安被盗事件再次引发了大家对数字货币安全的关注。其实网络盗窃事件很普遍,并且引发了技术的不断升级。以太坊作为区块链2.0的代表,自从诞生以来就一直遭受各种攻击。为此,本文就以太坊空账户DoS攻击事件,来谈一谈以太坊如何在技术上保障安全。
以太坊在2016年9月份和10月份遭受一连串的DoS攻击,攻击者在以太坊网络内以非常低的成本创建了1900万个空账户。空帐户会浪费硬盘空间,增加同步时间并减慢处理时间,这导致了2016年10月的“EIP-150 Hard Fork”硬分叉。以及,11月的“Spurious Dragon”分叉对此攻击所造成的影响进行了进一步修复。
什么是空账户?
空帐户是具有零余额,零常数和空代码的帐户(以太坊中每个账户都有一个常数,常数等于和这个帐号所相关的交易数)。空账户在功能上等同于不存在的账户,唯一的实际区别是,空账户需要存储在以太坊状态树中。空账户很容易产生,只需要你向一个不存在的账户发0个或者任意个以太坊,那个不存在的账户就会变成空账户。从理论上讲,如果矿工接受0交易手续费,甚至可以从空账户或者不存在的账户发送交易。
在以太坊树中拥有1900万个空帐户有什么问题?
空账户会存储在以太坊状态树中
· 他们浪费存储空间
· 它们会增加同步时间,尤其是fast sync 和 warp sync.
· 它们减慢了事务处理速度,因为在RAM中存储大部分或全部状态变得更加困难,因此需要更多的磁盘访问。
攻击方式:
攻击者建立了一个母合约,这个智能合约被调用了4750次。这个母合约方法如下:
(1)创建一个子合约
0000 PUSH32 0x6004600c60003960046000f3600035ff00000000000000000000000000000000
0021 PUSH1 0x00
0023 MSTORE
0024 PUSH1 0x20
0026 PUSH1 0x00
0028 PUSH1 0x00
002a CREATE
这会在一个新地址上创建一个子合约。
;; Child contract (in full)
PUSH1 0x00
CALLDATALOAD
SELFDESTRUCT
子合约的代码在PUSH32里,即:
0000 PUSH32 0x6004600c60003960046000f3600035ff00000000000000000000000000000000
前面的12 bytes是构造子合约的函数,接下来的4 bytes是子合约的代码。
(2)在存储中加载一个计数器
002b PUSH1 0x00
002d SLOAD
002e DUP1
这将跟踪正在创建的空帐户,并允许后续调用母合约,从前一个停止的位置继续生成空账户。
(3)循环调用子合约的”自毁函数“40次
(以下代码会被循环40次)
0030 PUSH1 0x01
0032 ADD
0033 DUP1
0034 PUSH1 0x00
0036 MSTORE
0037 PUSH1 0x00
0039 DUP1
003a PUSH1 0x20
003c DUP2
003d DUP1
003e DUP8
003f PUSH1 0x06
0041 CALL
0042 POP
首先向计数器添加一,复制它,将一个副本放回内存,然后用计数器作为调用数据调用子合约。子合约将它发送的数据(计数器)解释为一个地址,当它自毁时,它将其(零)值发送到该地址,从而创建了一个空的帐户。
selfdestruct(address)“自毁函数“会将合约的余额转到指定地址address,”自毁函数“函数的Gas数很少,这个设计是为了鼓励人对不再使用的智能合约进行销毁,销毁后合约的状态会从状态存储中消失。当你不再使用一个合约,你调用“自毁函数“,比起调用Transfer将合约中的钱转出要更划算。
这里的关键点是子合约在当前交易执行结束之前实际上不会自毁,因此可以在一个交易中多次调用它。子合约在当前交易终止时被删除。 每次调用母合同时,会重新创建子合约,然后可以从内部多次调用它的”自毁函数“,最后被销毁。
(4) 检查剩下的Gas
如果有足够的Gas话, 再返回并重新遍历所有40个调用,并继续增加地址计数器。
0328 GAS
0329 PUSH2 0x6000
032c LT
032d PUSH3 0x00002f
0331 JUMPI
因此,每次对母合同的调用都能够创建数千个空账户,具体取决于最初供应的Gas量。
(5)存储计数器,为下一次调用中做准备
0332 PUSH1 0x00
0334 SSTORE
使用”自毁函数“创建账户,成本极低。
谁会进行这种DoS攻击?
网络协议存在着攻击机会,这是很常见的,现实世界中并没有人会去做,因为他们没有动力去利用这些攻击漏洞。可悲的是,我们担心会有人针对以太坊发动这样的攻击。
最危险的群体是“griefers”,他们可能为了做空以太币,从而发动一次DoS攻击,为的就是让以太币的价格在短期内能够下跌。同样的,极端分子认为加密货币是一种零和游戏,他们也可能会参与破坏。当然,那些想要攻击以太坊矿工的人也可能会参与进来。因为在目前,这种攻击还没有什么成本,这些群体很可能会发动攻击。
以太坊的防御措施
阻止这样的攻击
调整Gas用量,使“自毁函数”成本变高。以太坊进行了一次“EIP-150 Hard Fork”硬分叉。在2016年10月18日 23:19:31 2463000号块。除了其他的Gas量的调整以外,“自毁函数”调整成了5000 Gas(原本是0 Gas)。
清除以前建立的空账户
随后,以太坊做了叫做“Spurious Dragon”的分叉。在2016年11月23日 01:15:44 2675000号块,这次分叉的相关改动是EIP 161 状态清扫, 为了清除以前建立的空账户。真正的清除账户的操作实际是在分叉之后进行的,空账号会在被某个交易“触碰”时自动删除。以太坊基金会会系统性的开始”CALL“这些因为攻击而生成的空账号以逐步完成清扫。
概括:当一笔交易“触及”一个“空账户”时,这个“空账户”会被删除。
解释:
· “空账户”:如果帐户没有代码且常数为0和余额为0,则该帐户被视为空。(以太坊中每个账户都有一个常数,常数等于和这个帐号所相关的交易数)
· “触及”:当一个账户涉及到“状态改变”的操作时,被认为是被“触及”。这包括但不限于,被转入0个以太坊。
· 账户的“状态改变”:
o 当它是"SUICIDE"操作的目标或者退款地址时。("SUICIDE"即后来的"selfdestruct",因为自杀是一个沉重的主题,所以改成了自毁)
o 当它是"CALL"操作或 message-call的目标或者源时。
o 当它是"CREATE"操作或者合约创建操作的目标或者源时。
o 作为矿工,是区块奖励或者交易费的接受方时。
· 一个交易会在产生交易收据之前,立即执行自杀列表。因此,如果出现触及空账户的情况,空账户会被自杀掉。
事实上,目前的实现只需要跟踪四种情境:
· 一个空帐户,存在通过CALL传递给它的零值的交易;
· 一个空账户,存在通过SUICIDE传输给零值的交易;
· 一个空帐户,存在通过message-call交易传递给它的值为零;
· 一个空账户通过zero-gas-price fees 转账将零值转移给它。 (一般Gas Price设置为零的交易,矿工不会去挖,但矿工可能挖自己发送的0交易费的交易)
网友评论