这两篇写得还不错,可以让局外人稍微了解一下
https://www.bikeji.com/t/4027#reply3
DAO 提现bug
简述
恶意用户通过一个合约地址而不是普通的用户地址(不含智能合约)参与DAO,之后通过
Split(DAO概念里的退出、提现),利用这个合约地址反复调用DAO的提现方法,在余额
减少N的情况下提现k*N的提现。
攻击表现
[DAO项目]的合约地址0xbb9bc244d798123fde783fcc1c72d3bb8c189413上可以看到大量到
地址[0x304a554a310c7e546dfe434669c62820b7d83490]的内部交易[
https://etherscan.io/txsInternal?a=0xbb9bc244d798123fde783fcc1c72d3bb8c189413&p=3
]
(内部交易:Internal transaction是指合约执行过程中产生的交易,比如在合约内给用
户转账)
目前DAO还有余额7,930,715.340858306 Ether ($137,915,139.78),黑客地址
3,641,694.241898506 Ether ($63,583,981.46)
原理
DAO合约记录著用户的余额,提现时候的伪代码如下:
// msg.sender为交易发送方的地址,这里就是提现的用户
if (balance[msg.sender]>=withdrawRequest) { //检查是否有足够的钱
msg.sender.send(withdrawRequest); //给用户转账
balance[msg.sender] -= withdrawRequest; //在记录中扣除相应的数额
}
合约在执行这里的msg.sender.send(withdrawRequest)时会产生一个内部交易,这个交易
很简单,就是从这个合约转账withdrawRequest给提现用户地址。但是以太坊所有交易在
转账的同时会执行交易接收方地址的合约。通常提现的地址是个普通用户,所在的地址没
有智能合约,而这里,黑客从一个自己写的合约发送提现申请,当这个地址收到钱时就会
执行黑客合约里定义的默认操作。
//黑客合约的默认操作
function defaultAction() {
//设定DAO合约的地址
address DAO = 0xbb9bc244d798123fde783fcc1c72d3bb8c189413;
if (attackMode) { //是否开启了攻击模式
DAO.withdraw(200); //递归调用DAO的提现操作
}
}
这样当黑客发起提现时,以太坊的执行过程如下: 执行到msg.sender.send这行,触发黑
客合约执行,继续调用DAO的提现,再次执行到msg.sender.send,注意这里的代码是在
msg.sender.send执行后完才扣除用户在DAO里记录的余额,因此再次执行时用户的余额还
是不变的...... 只要DAO合约还有钱,黑客给定的手续费足够,黑客不终止,这个过程可
以一直进行下去。
这个问题可以在写合约的时候解决。
-先扣除用户余额,再转账
-在转账给用户时只给足够小的手续费(Gas),即使递归调用也因为Gas耗尽而结束
-一旦开始提现,锁定合约的状态,不允许其他提现操作进行
DAO的开发人员已经考虑了这个问题,但是没有修复所有有这个漏洞的地方,被黑客所利
用。
目前的解决方案
DAO不能暂停,因此黑客的攻击还可以继续下去。黑客地址已经偷取了近三分之一DAO合约
里的以太币。目前开发人员号召DAO的用户否决黑客所用的Split申请,以及广播大量垃圾
交易阻塞以太坊的网络,不让黑客的交易被打包。终极的解决方案是以太坊回滚,会严重
影响以太坊的声望。