okx

区块链安全:DAO攻击事件分析

时间:2023-06-18|浏览:208

前言:

最近我写了一些安全分析文章,可能之后还会发一些其他的文章。

基础知识:

1. 跨合约调用

智能合约之间的调用本质上是外部调用,可以使用 message.call 或者创建智能合约对象的形式进行调用。

例如,合约1调用合约2的某个方法:

bytes4 methodId = bytes4(keccak256("increaseAge(string,uint256)")); returnAddr.call(methodId, "jack", 1);

还可以通过已知合约地址的方式获取合约对象:

Contract1 c = Contract1(AddressOfContract1); c.foo(); // 跨合约调用

2. 智能合约发送 ETH

在智能合约中,可以使用代码向某个地址(可以是人或智能合约)发送以太币。常用的两个方式是:

(1) 调用 send 函数:

msg.sender.send(100);

(2) 使用 message.call:

msg.sender.call.value(100);

这两个方式发送 gas 不同。在调用 send 方法时,只会发送 2300gas;而使用 message.call 会发送全部的 gas。执行完之后,剩余的 gas 会退还给发起调用的合约。

3. fallback 函数

智能合约中可以有唯一的一个未命名函数,称为 fallback 函数。该函数不能有实参,不能返回任何值。如果其他函数都不能匹配给定的函数标识符,则执行 fallback 函数。

当合约接收到以太币但是不调用任何函数时,就会执行 fallback 函数。如果一个合约接收了以太币但是内部没有 fallback 函数,那么就会抛出异常,然后将以太币退还给发送方。

contract Sample { function payable() { // your code here } }

通常,当我们单纯使用 message.call 或者 send 函数发送以太币给合约时,没有指明调用合约的某个方法,这种情况下就会调用合约的 fallback 函数。

攻击事件还原:

我们用一个简单的模拟代码来了解整个攻击过程。

首先是存在漏洞的智能合约代码 Bank:

用户可以通过 addToBalance 方法存入一定量的以太币到这个智能合约,通过 withdrawBalance 方法可以提现以太坊,通过 getUserBalance 可以获取到账户余额。

注意到这里是通过 message.call 的方式来发送以太币,所以在调用 sender 的 fallback 函数的时候我们就会有充足的 gas 来进行循环调用。如果是 send 的方式,gas 只有 2300,稍微一操作就会耗尽 gas 抛出异常,这不够用来进行嵌套调用。这里是不同操作需要的 gas 数量:

出问题的是 withdrawBalance 方法,特别是修改保存在区块链的 balances 的代码是放在发送以太币之后。攻击代码如下:

这里的 deposit 函数是往 Bank 合约中发送 10 wei。withdraw 是通过调用 Bank 合约的 withdrawBalance 函数把以太币提取出来。注意看这里的 fallback 函数,这里循环调用了两次 Bank 合约的 withdrawBalance 方法。

攻击过程如下:

(1) 假设 Bank 合约中有 100 wei,攻击者 Attack 合约中有 10 wei。

(2) Attack 合约先调用 deposit 方法向 Bank 合约发送 10 wei。

(3) 之后 Attack 合约调用 withdraw 方法,从而调用了 Bank 的 withdrawBalance 方法。

(4) Bank 的 withdrawBalance 方法发送给了 Attack 合约 10 wei。

(5) Attack 合约收到 10 wei 之后,又会触发调用 fallback 函数。

(6) 这时,fallback 函数又调用了两次 Bank 合约的 withdrawBalance,从而转走了 20 wei。

(7) 之后,Bank 合约才修改 Attack 合约的 balance,将其置为 0。

通过上面的步骤,攻击者实际上从 Bank 合约转走了 30 wei。Bank 则损失了 20 wei。如果攻击者多嵌套调用几次 withdrawBalance,完全可以将 Bank 合约中的以太币全部转走。

复现过程:

给 Bank 合约 100 wei,给 Attack 合约 10 wei。

(1) 部署 Bank,分配 100 wei。

(2) 部署 Attack 。

(3) 调用 Attack 合约的 deposit 方法。

(4) 调用 Attack 合约的 withdraw 方法。

(5) 查看 Attack 合约的余额,变成了 30 wei,即窃取了 20 wei。

DAO 攻击事件代码分析:

在 DAO 源码中,有 withdrawRewardFor 函数:

function withdrawRewardFor(address _account) noEther internal returns (bool success) { if ((balanceOf(_account) * rewardAccount.accumulatedInput) / totalSupply < paidOut[_account]) throw; uint reward = (balanceOf(_account) * rewardAccount.accumulatedInput) / totalSupply - paidOut[_account]; if (!rewardAccount.payOut(_account, reward)) // vulnerable throw; paidOut[_account] += reward; return true; }

这里调用了 payOut 函数进行付款:

function payOut(address recipient, uint amount) returns (bool) { if (msg.sender != owner || msg.value > 0 || (payOwnerOnly && _recipient != owner)) throw; if (_recipient.call.value(_amount)){//vulnerable PayOut(_recipient, _amount); return true; } else{ return false; } }

payOut 中直接使用的是 message.call 的方式发送以太币,从而导致了嵌套漏洞。

总结:

在编写智能合约进行以太币发送的时候,应该使用 send 或者 transfer 的方式,而不是使用 message.call 的方式。虽然 send 还是有一些小问题,但以后有时间再分析。DAO 事件直接导致了以太坊硬分叉,分为 ETH 和 ETC。可见,在区块链领域,安全问题不容忽视,因为它的修复难度和所造成的影响都很高,毕竟是和钱打交道,给个评论。

热点:区块链

欧易

欧易(OKX)

用戶喜愛的交易所

币安

币安(Binance)

已有账号登陆后会弹出下载

« 上一条| 下一条 »
区块链交流群
数藏交流群

合作伙伴

非小号交易所排名-专业的交易行情资讯门户网站,提供区块链比特币行情查询、比特币价格、比特币钱包、比特币智能合约、比特币量化交易策略分析,狗狗币以太坊以太币玩客币雷达币波场环保币柚子币莱特币瑞波币公信宝等虚拟加密电子数字货币价格查询汇率换算,币看比特儿火币网币安网欧易虎符抹茶XMEX合约交易所APP,比特币挖矿金色财经巴比特范非小号资讯平台。
非小号行情 yonghaoka.cn 飞鸟用好卡 ©2020-2024版权所有 桂ICP备18005582号-1