引介 | TheDAO 攻击中 RACE To Empty

孙峰   |     |   151 次阅读

TheDAO 攻击中使用了 RACE To Empty的攻击方法。下面介绍一下RACE TO Empty的方法

下面代码看起来是安全,其实并不安全

function withdrawBalance() {  
  amountToWithdraw = userBalances[msg.sender];
  if (!(msg.sender.call.value(amountToWithdraw)())) { throw; }
  userBalances[msg.sender] = 0;
}

1、获取记录的余额 balances[ msg.sender ]
2、发送余额,发送不成功时抛出异常,本次调用不成功。
3、修改余额为0
这是一个看起来美好,实际在智能合约上执行时有问题的代码。关键在于钱包是有默认函数。比如像这样的代码

function () {  
  // To be called by a vulnerable contract with a withdraw function.
  // This will double withdraw.

  vulnerableContract v;
  uint times;
  if (times == 0 && attackModeIsOn) {
    times = 1;
    v.withdrawBalance();

   } else { times = 0; }
}

当你调用msg.sener.call.value时,就会调用到默认函数,而默认函数又调用了withdraw造成了递归调用。从堆栈上可以看到

vulnerableContract.withdraw run 1
      attacker default function run 1
        vulnerableContract.withdraw run 2
         attacker default function run 2

第一次调用 userBalances[msg.sender] 有当前余额值
第二次调用 由于 userBalances[msg.sender] = 0 还没有调用到,因此 userBalances[msg.sender]
还是原来的值,因此会造成重复支付。

避免的方式很简单,改为这样就可以避免

function withdrawBalance() {  
  amountToWithdraw = userBalances[msg.sender];
    userBalances[msg.sender] = 0;

    if( amountToWithdraw > 0 ) {
     if (!(msg.sender.call.value(amountToWithdraw)())) { throw; }
    }
}

非原创,具体可参考 http://vessenes.com/more-ethereum-attacks-race-to-empty-is-the-real-deal/

 
2 人喜欢