主页 > 苹果下载imtoken钱包下载哪一个 > “危机四伏”的以太坊转账操作|以太币转账安全风险——漏洞分析系列八

“危机四伏”的以太坊转账操作|以太币转账安全风险——漏洞分析系列八

安全,区块链领域举足轻重的话题,为何一行代码瞬间蒸发数十亿市值? 合约底层功能使用不当会导致哪些漏洞? 以太坊转账存在哪些漏洞和陷阱? 《区块链大本营》携手《成都联安科技》团队推出《合约安全漏洞分析连载》,带你回顾区块链安全的历史,用故事的方式解析漏洞背后的玄机。 让开发者在乐趣中学习,编写更有力的合约,防患于未然。 当然,这些文章不是为开发人员设计的。 即使你不是开发人员,看完本系列,相信你在遇到安全问题时也会有新的认识。

简介: 金风未动,蝉有所知,谋无常死而不自知——《名贤集》“七言”

上次我提到:

局部变量存储措手不及

意外的变量覆盖

Solidity语言默认的存储规则和引用未初始化变量带来的特殊性共同导致未初始化变量覆盖了原来的状态变量,占据了状态变量在Storage中的位置,重演了最近的“高铁座霸”事件。 由此带来的安全隐患不可低估。 因此,我们在开发过程中一定要注意编译警告,对未初始化的变量进行初始化,或者将其安排在暂存空间Memory中,杜绝此类隐患。

这次我们来聊聊:

转移过程复杂

安全应对每次攻击

在我们之前的漏洞分析系列中,我们讨论了各种代币合约漏洞。 大多数这些代币合约都以代币为基础运行,不一定是以太坊的中心货币——以太币。 但是,随着大量游戏合约的出现,越来越多的游戏合约直接使用以太坊作为游戏资金。

这些与“钱”直接挂钩的游戏合约,在以太坊转账的相关逻辑上,真的能牢不可破吗? 答案是不。 比如最近发生的Pandemica Ponzi游戏资金冻结事件[1],就属于“超过gas limit导致的DoS攻击”,我们在漏洞分析系列的第二期中重点介绍过。 本期我们将分析其他直接涉及以太坊转账的漏洞和陷阱。

sitebitcoin86.com 以太坊以太经典_以太坊漏洞_以太经典和以太坊算力差别

基础知识

在Solidity语言中,官方提供了三种发送Ether(以太币)到目标地址的方式。

.transfer(uint256 amount) 向目标地址发送数量为wei的以太币,失败时抛出异常,并发送矿工费2300 gas,不可调整。

.send(uint256 amount) returns (bool) 向目标地址发送以太币数量wei,失败返回false,发送2300 gas矿工费,不可调整。

.call.value(uint256 amount)() returns (bool) 向目标地址发送以太币数量wei,失败则返回false,发送所有可用gas,可调(.gas(uint256 gasAmount))[2]。

知道了发送以太币的三种方式,我们先放一下。 再介绍一下不变量检查的概念。

不变性检查是一种常见的防御性编程技术,用于强制执行正确的状态转换和验证操作。 它包括定义一组不变量(不应更改的矩阵或参数)并检查它们在一次或多次操作后是否未更改。 检查不应该改变的变量没有改变是一个很好的设计。

在ERC20代币中,我们也使用了这种技术,例如代币总数totalSupply是常数。 由于这个变量不应该被改变,所以可以在transfer()函数中加入一个检查机制,比较操作前后的totalSupply是否发生了变化,从而判断transfer()函数是否被正确执行[3]。

出现问题

对于发送以太币的三种方式,回退函数fallback都会有问题。 如果一个合约,而不是用户,接收到以太币(并且没有函数被调用),回退函数将被执行。 这里fallback函数的作用是接受Ether。 如果没有这个函数,合约将拒绝它(并同时抛出异常)。 如果合约使用transfer/send方式发送Ether到目标合约地址,在目标合约fallback函数执行过程中,合约只能依赖此时可用的“gas allowance”(2300gas)执行. 这个余量不足以完成任何一种存储访问,这将导致 fallback 函数始终返回 false,就像一个不可逆的弹簧。

以太坊漏洞_以太经典和以太坊算力差别_sitebitcoin86.com 以太坊以太经典

而当涉及到不变性检查时,开发人员倾向于依赖合约中存在的以太币,但实际上它可以被外部用户操纵,而不管合约的内部规则如何。 而且,开发者在学习Solidity时,很容易产生一个误区以太坊漏洞,即一个合约只能通过payable函数来接收Ether,而没有考虑接收Ether但不执行任何函数的情况。 此类合约容易受到强制将 Ether 发送到合约的攻击。 真是应验了古话,有钱能使鬼转磨。

具体案例分析

一、发送和接收以太币的安全风险

1. 使用transfer向地址发送Ether可能存在安全风险。

我们使用 Ethernaut-King 上的一个级别作为案例合同:

谁发送的以太币大于当前奖励,谁就成为新的国王,被推翻的国王获得新的奖励。 如果攻击者使用以下代码部署合约:

由于fallback函数无法接收ether,攻击者通过攻击合约成为king后,新的竞争者执行king.transfer(msg.value); 在向 case contract 发送以太币成为 King 的过程中; it will always revert,攻击者实际上通过漏洞系列第二期中描述的 Revert 实现了 DoS。

2.使用send向地址发送Ether可能存在安全隐患

代码截自第二期同案KingOfTheEtherThrone

因为send执行失败后会返回false而不是抛出异常,所以合约中没有检查send的返回值,部分通过合约账户参与游戏的玩家,因为send附带的2300 gas无法完成回退操作,导致无法接收以太币返回。

以太坊漏洞_以太经典和以太坊算力差别_sitebitcoin86.com 以太坊以太经典

3、使用call.value()()向地址发送Ether可能存在的安全风险

默认情况下,使用 call.value()() 发送以太币将带来所有剩余的气体。 如果合约执行存在隐患,可能会引发重入攻击。 此外,在发送以太币失败后,call.value 将返回 false。 如果没有勾选返回值,那么合约会默认所有发送的以太币都成功,然后执行状态变量的改变。 显然,这其中存在逻辑漏洞。

Bug修复

二、意外强行进入的以太币

1. 自毁

任何合约都可以实现 selfdestruct(address) 函数以太坊漏洞,该函数从合约地址中删除所有字节码,并将存储在那里的所有 Ether 发送到参数指定的地址。 如果指定的地址也是一个合约,则不会调用任何函数(包括回退函数)。 因此,使用 selfdestruct() 函数可以强制将 Ether 发送到任何目标合约,包括没有任何支付功能的合约,无论目标合约中是否存在任何代码。

这意味着任何攻击者都可以创建一个带有 selfdestruct() 函数的合约,向它发送以太币,调用 selfdestruct(target) 并强制将以太币发送到目标合约。 Martin Swende 有一篇很棒的博客文章,描述了自毁操作码的一些怪异之处,并描述了客户端节点如何检查不正确的不变量,这可能会导致相当灾难性的客户端问题 [4]。

2. 现在的以太

合约仍然可以在不使用 selfdestruct() 函数或调用任何支付函数的情况下接收以太币的第二种方法是将以太币发送到合约地址。 合约地址是确定性的。 实际上,地址是根据创建合约的地址和创建合约的交易Nonce的哈希值计算的,即如下形式:address = sha3(rlp.encode([account_address,transaction_nonce])在这一点上查看无密钥以太的一些有趣用例或如何计算以太坊合约的地址?()。这意味着,任何人都可以在创建合约之前计算合约地址并将以太币发送到该地址。当合约确实是创建后,它将有一个非零的以太币余额。

3.挖矿

以太经典和以太坊算力差别_sitebitcoin86.com 以太坊以太经典_以太坊漏洞

目前,合同和“外部账户”都不会阻止某人向他们发送以太币。 合约可以对正常传输做出反应并拒绝它,但是有一些方法可以在不创建消息的情况下发送以太币。 其中一种方法是简单地“挖掘”合约地址。

我们先从以下合约开始具体分析

该合约代表一个简单的游戏(自然会导致竞争条件),玩家可以向合约发送 0.5 以太币,希望成为第一个达到三个里程碑之一的人。 里程碑以以太币计价。 当游戏结束时,第一个达到里程碑的人将获得一部分合约的以太币。 当达到最终里程碑(10 Ether)时,游戏结束,用户可以领取奖励。

这个合同的问题是 uint currentBalance = this.balance + msg.value; (以及相关的行 [16])以及行 [32] 中 this.balance 的错误使用。 攻击者可以通过上述三种方式将以太币放入合约中:

比如第一种方式,自毁:

部署合约时,在交易中附加0.1个以太币,然后调用攻击函数自毁合约。 这个时候会发0.1个ether给case合约,因为case合约每次只能收到0.5个ether,普通玩家永远达不到milestone。 要求,游戏将没有获胜玩家,除非剩余的 0.4 个以太币被强行签订合约。

第二种方式,预存以太币:

使用solidity计算合约部署地址的方法是address(keccak256(0xd6, 0x94, _from, nonce)) 其中_from表示部署合约的账户地址,nonce表示账户地址的随机数部署合约时,就是最新的交易号+1。 如果部署合约的账户是第一笔交易,如果账户是合约,nonce=1,如果是普通用户,nonce=0:

Bug修复

这个漏洞是对 this.balance 的滥用。 在可能的情况下,合约逻辑应避免依赖合约余额的确切值,因为它可以在合约逻辑之外进行操作。 如果合约逻辑必须基于this.balance,那么就需要考虑合约的意外余额。

以太坊漏洞_sitebitcoin86.com 以太坊以太经典_以太经典和以太坊算力差别

如果你真的需要一个准确的余额值,你应该定义一个状态变量,当合约通过 payable 函数收到以太币时自增,以安全地跟踪合约收到的以太币,并且这个变量不会被强制发送以太币给合约(例如 selfdestruct() )。 因此,对上述案例合同修改如下:

溪云初,夕阳沉阁,山雨欲来,风满楼

随着区块链技术的普及,智能合约的种类也开始增多。 越来越多的新合约需求对智能合约的开发和审计是机遇也是挑战。 上述以太坊转账相关的安全隐患,在近期兴起的直接使用以太坊转账的游戏合约中频频出现,造成经济损失,同时降低投资者和项目方对区块链行业的信心。 面对这种情况,我们必须以精益求精的态度对待合约开发和审计这两个方面,以稳健的心态去迎接即将到来的区块链行业。

本期漏洞分析系列到此结束。 如果你想知道接下来会发生什么,请看下一章。

引用:

[1]:Pandemica庞氏游戏存在gas超漏洞,超过80万资金被冻结

[2]:以太坊官方文档-地址相关

#地址相关

[3]:Solidity 安全:

#醚

[4]:以太坊官方文档——发送和接收以太币:

#发送和接收以太币