风险提示:理性看待区块链,提高风险意识!
硬核:避免 Solidity 语言和智能合约的不当使用,绕开以太坊的致命弱点
首页 > 币界资讯 > 竞争币新闻 2019-02-01 10:05:00

很多以太坊的智能合约控制着有实际价值的数字资产。因此,保证合约没有安全漏洞是十分重要的事情。本文是一篇 2017 年对以太坊合约攻击调研的文献,来帮助大家避免以太坊智能合约设计中的一些可能导致安全性问题的弱点。在这里,你也可以看到,导致以太坊分叉的著名事件 The DAO 攻击,其原理是什么。

这篇文献分两部分,第一部分介绍了一些如果对 Solidity 语言和智能合约不当使用会导致问题的弱点;第二部分则用一些实例展示了这些弱点可能会导致怎样的问题。

原文标题:《以太坊合约的安全性弱点,你都绕开了吗?》

不当使用会导致问题的点

合约内函数调用

在使用 Solidity 编写智能合约时,可以调用其他合约中的函数。假设 Alice 合约里有一个 ping(uint) 函数,c 是一个 Alice 合约在以太坊上的地址。如果其他合约(或 Alice 合约自身)想要以参数 42 调用 ping 函数,有三种方式:

第一种

硬核:避免 Solidity 语言和智能合约的不当使用,绕开以太坊的致命弱点

call 调用:通过合约地址,合约函数,函数签名和调用参数进行调用。如果被调用函数中有修改合约变量的代码,将修改被调用合约中相应的变量。

第二种

硬核:避免 Solidity 语言和智能合约的不当使用,绕开以太坊的致命弱点

delegatecall 与 call 类似,区别是 delegatecall 执行时,仅仅使用被调用函数的代码,而代码中如果涉及到合约变量的修改,则是修改调用者合约中的变量。如果被调用的函数中有 d.send(amount) 的指令,表示向地址 d 转一定数额的以太币,在 call 模式下这笔钱从被调用合约的余额中转出。在 delegatecall 模式下将从调用者合约的余额中转出。

因此, delegatecall 是更危险的命令,如果这一命令加载的函数代码是合约编写者不可控的,可能会导致合约的钱被转走或合约被销毁等严重后果。

第三种

硬核:避免 Solidity 语言和智能合约的不当使用,绕开以太坊的致命弱点

这第三种调用方式在论文中被称为直接调用 (direct call). 它先在合约里声明了 Alice 合约需要调用的函数,然后调用它。这种方式与以上两种方式在异常处理上会有区别。

需要注意的是,以上三种方式如果将函数名或者参数类型设置错误,则会调用回退函数 (fallback function). 如果是因为笔误打错了内容,可能会触发本不该执行的回退函数中的代码。

Gasless Send

在 Solidity 中,如果变量 rec 的类型为 address, 那么 rec.send(amount) 表示由合约向地址 rec 转账数额为 amount 的 wei. (10^18 wei = 1 ether ) 在这个执行的过程中,还会触发地址 rec 的回退函数。如果回退函数执行过程中消耗的 gas 大于 2300,则会触发一个异常,导致转账失败。

异常处理

在背景介绍中我们提到过,使用 Solidity 执行智能合约时会抛出异常,但是不同的合约内函数调用方式对异常(exception)的处理方式不一样。

如果合约执行过程中没有函数调用,或者只有 direct call 直接调用,那么当触发一个异常的时候,视为合约执行失败,直接停止合约的执行,回滚执行过程中的转账和对合约变量的修改等操作,并扣除全部的交易费用。

如果通过 call, delegatecall 或 send 调用其他合约函数,在执行期间触发的异常不会影响原有函数。也就是说,如果在执行 send 的触发的回退函数过程中,如果 gas 不足引起了异常,转账会失败,但是原有合约会被成功地执行。

如果对这一点缺乏足够的理解,错误地认为合约执行成功意味着 call 调用也一定成功,错误地认为没有触发异常就意味着 ether 转账成功,就可能导致合约有安全性问题。正确的做法应当是通过函数调用返回的结果判断其执行是否成功。而一些研究表明有 28% 的合约没有检查返回结果。(当然,这不意味着一定有安全问题)。

重入问题

Solidity 中回调函数的机制,可能会让合约调用其他函数后,被调用的函数又调用了调用者合约的函数,造成循环,下面是一个例子。

假设区块链上已经如下的合约 Bob,如果 sent 变量为 false, 就向给定地址发送一笔钱。

硬核:避免 Solidity 语言和智能合约的不当使用,绕开以太坊的致命弱点

而 Mallory 是攻击者恶意构造的合约,代码如下所示。

硬核:避免 Solidity 语言和智能合约的不当使用,绕开以太坊的致命弱点

Bob 合约设计的本意是,如果 sent 变量为 false, 就向给定地址发送一笔钱。然而,当这笔钱发往攻击者合约时,会触发攻击者合约的回退函数,回退函数再次调用 ping 函数,如此无限循环,直到交易费耗尽或调用深度达到上限 1024 次触发异常。但之前提到了,对于 call 调用的函数在执行过程中触发的异常,不会影响原来的函数的成功执行。也就是说,除了最后一步转账会失败,之前的转账都会成功。

几种攻击

接下来,我们介绍几种利用上面提到弱点的攻击例子。

DAO 攻击

DAO 攻击是以太坊历史上最著名的攻击,盗走了价值 6000 万美元的以太币。以太坊社区通过强行回滚硬分叉了以太坊,导致了以太坊和以太经典两条分叉链并存的局面。

下面是一个简化版的 DAO 智能合约,但足以描述 DAO 合约的漏洞。

硬核:避免 Solidity 语言和智能合约的不当使用,绕开以太坊的致命弱点

这个合约的功能很简单,任何人可以向指定地址捐献以太币,受捐赠人可以提走自己受捐赠的币。

而攻击者通过以下的合约,就可以大量转走合约中的币。

硬核:避免 Solidity 语言和智能合约的不当使用,绕开以太坊的致命弱点

其原理与上文所说的重入问题完全一样, SimpleDAO 合约的 withdraw 函数执行时向攻击者合约转账,转账会触发攻击者合约的回退函数,攻击者合约的回退函数会重新调用 SimpleDAO 合约的 withdraw 函数,形成一个循环。当循环因为各种原因结束的时候,除了最后一步,之前的执行都不会失败。攻击者转出了大量的钱。

另外,这个合约没有考虑整数溢出问题,因此有如下攻击成本更低的方案

硬核:避免 Solidity 语言和智能合约的不当使用,绕开以太坊的致命弱点

在这个合约中,攻击者设计了一个函数 attack, 当这个函数被执行的时候,攻击合约先给自己捐赠 1 wei, 然后把这 1 wei 取出来。在取钱的时候,会触发攻击者合约的回退函数。与之前的攻击不同,这次我们只利用重入问题 1 次,也就是 withdraw 函数被执行了两遍。在 withdraw 第二次向攻击者转账以后,攻击者不再调用 withdraw.

于是 withdraw 函数中的转账操作 msg.sender.call.value(amount)() 发生了 2 次,自然地,它的下一行也会被调用 2 次。这两次被调用将 credit[攻击者地址] 变成了 -1 wei, 会被虚拟机解读为 2^256-1 wei. 这时,攻击者可以从中取出几乎无限多的钱出来。

特别的是,即使 withdraw 函数在转账后检查 send 执行是否成功,也只能防范第一种攻击。

以太王座

考虑下面一个游戏合约,在游戏中,大家将竞争一个王座。后来者可以通过向王座上的人支付一笔钱来取而代之,每一轮取得王座需要的钱都要比上一轮高。最后取得王座的人有额外的收益。(没有在合约中体现。)

硬核:避免 Solidity 语言和智能合约的不当使用,绕开以太坊的致命弱点

这个合约看上去没什么问题。事实上,他人在向王座上的人(地址)支付费用的时候,会触发那个地址(如果是一个合约)的回退函数。如果王座上合约地址的回退函数需要的交易费过高,会触发 gasless send 的问题,就会导致转账失败。但后续变更王座拥有者的代码还会照常执行,新来者可以毫无成本地获得王座。

修改这一问题的思路看上去很简单,只要将转账的代码 king.send(compensation) 变成 if(!king.call.value(compensation)())throw; 来判断一下转账是否成功就可以了。然而这会导致另一个问题。王座上的地址(合约)将自己的回退函数设定成一定会触发异常,例如 function(){throw;},就没有人有能力将他从王座上赶下去了,因为所有转账的结果都会失败。

以上就是这一期的内容,在接下来的文章中,我们将会介绍文献中提到的其他的 Solidity 的弱点与可能导致的问题。

参考文献: [1] Atzei, Nicola, Massimo Bartoletti, and Tiziana Cimoli. 「A survey of attacks on ethereum smart contracts (sok).」Principles of Security and Trust. Springer, Berlin, Heidelberg, 2017. 164-186.

上一篇: Vlad Zamfir 驳「code is law」之父:加密法不需要成为萨博法
下一篇: 以太坊,一场大胆的社会实验,你我都参与其中
推荐专栏
web3首席知识博主
一位相信价值投资的币圈KOL。稳定盈利的缠论野生交易员 #BTC行情分析师 #价值投资 #链上数据分析
爱Web 3,爱生活,爱科技,爱炒币的老韭菜
热门币种
更多
币种
价格
24H涨跌幅
BTC比特币
¥267,916.95
37,538.63 USDT
+5.68%
ETH以太坊
¥14,653.39
2,053.13 USDT
+4.22%
USDT泰达币
¥7.24
1.01 USDT
+0.2%
BNB币安币
¥1,805.75
253.01 USDT
+3.56%
XRP瑞波币
¥4.61
0.64620 USDT
+3.1%
SOLSolana
¥473.69
66.37 USDT
+16.8%
USDC
¥7.14
0.99980 USDT
-0.02%
OKBOK币
¥425.96
59.68 USDT
+2.6%
ADA艾达币
¥2.72
0.38160 USDT
+7.74%
DOGE狗狗币
¥0.54850
0.07686 USDT
+5.81%
热搜币种
更多
币种
价格
24H涨跌幅
dYdX
¥30.23
4.1719 USDT
+10.27%
Filecoin
¥38.66
5.3346 USDT
+10.62%
FTX Token
¥26.76
3.6921 USDT
+2.5%
PancakeSwap
¥19.44
2.6829 USDT
+5.84%
Solana
¥480.98
66.371 USDT
+16.8%
奇亚
¥197.66
27.2758 USDT
+3.32%
Yield Guild Games
¥3.08
0.4246 USDT
+4.43%
Conflux
¥1.24
0.1711 USDT
+7.47%
火必积分
¥21.87
3.0173 USDT
+5.06%
Terra Classic
¥0.00
8.293E-5 USDT
+3.33%
阿童木
¥71.74
9.8992 USDT
+9.92%
比特币
¥272,034.94
37538.63 USDT
+5.68%
最新快讯
更多
SoraVentures联创:如果在香港或新加坡运营加密货币基金,投资或言论会受到限制
2023-11-16 11:57:40
SoraVentures联创:如果在香港或新加坡运营加密货币基金,那么的投资或言论会受到限制
2023-11-16 11:57:40
SoraVentures联创:如果在香港或新加坡获得法律许可,那么的投资或言论就会受到很多限制
2023-11-16 11:57:40
比特币网络常规交易手续费升至186sats/字节
2023-11-16 11:56:15
Web3社交协议beoble完成200万美元Pre-Seed轮融资,DCG等参投
2023-11-16 11:54:45
币安合约将于12月15日更新WebSocket域名
2023-11-16 11:51:02
去中心化隐私协议Hinkal完成410万美元融资,DraperAssociates领投
2023-11-16 11:43:32
下载币界网APP