Skip to Content
logologo
AI Incident Database
Open TwitterOpen RSS FeedOpen FacebookOpen LinkedInOpen GitHub
Open Menu
発見する
投稿する
  • ようこそAIIDへ
  • インシデントを発見
  • 空間ビュー
  • テーブル表示
  • リスト表示
  • 組織
  • 分類法
  • インシデントレポートを投稿
  • 投稿ランキング
  • ブログ
  • AIニュースダイジェスト
  • リスクチェックリスト
  • おまかせ表示
  • サインアップ
閉じる
発見する
投稿する
  • ようこそAIIDへ
  • インシデントを発見
  • 空間ビュー
  • テーブル表示
  • リスト表示
  • 組織
  • 分類法
  • インシデントレポートを投稿
  • 投稿ランキング
  • ブログ
  • AIニュースダイジェスト
  • リスクチェックリスト
  • おまかせ表示
  • サインアップ
閉じる

レポート 902

関連インシデント

インシデント 5024 Report
The DAO Hack

Loading...
Analysis of the DAO exploit
hackingdistributed.com · 2016

Analysis of the DAO exploit

Phil Daian

So I'm sure everyone has heard about the big news surrounding the DAO getting taken to the tune of $150M by a hacker using the recursive Ethereum send exploit.

This post will be the first in what is potentially a series, deconstructing and explaining what went wrong at the technical level while providing a timeline tracing the actions of the attacker back through the blockchain. This first post will focus on how exactly the attacker stole all the money in the DAO.

A Multi-Stage Attack This exploit in the DAO is clearly not trivial; the exact programming pattern that made the DAO vulnerable was not only known, but fixed by the DAO creators themselves in an earlier intended update to the framework's code. Ironically, as they were writing their blog posts and claiming victory, the hacker was preparing and deploying an exploit that targeted the same function they had just fixed to drain the DAO of all its funds. Let's get into the overview of the attack. The attacker was analyzing DAO.sol, and noticed that the 'splitDAO' function was vulnerable to the recursive send pattern we've described above: this function updates user balances and totals at the end, so if we can get any of the function calls before this happens to call splitDAO again, we get the infinite recursion that can be used to move as many funds as we want (code comments are marked with XXXXX, you may have to scroll to see em): function splitDAO ( uint _proposalID , address _newCurator ) noEther onlyTokenholders returns ( bool _success ) { ... uint fundsToBeMoved = ( balances [ msg . sender ] * p . splitData [ 0 ]. splitBalance ) / p . splitData [ 0 ]. totalSupply ; if ( p . splitData [ 0 ]. newDAO . createTokenProxy . value ( fundsToBeMoved )( msg . sender ) == false ) throw ; ... Transfer ( msg . sender , 0 , balances [ msg . sender ]); withdrawRewardFor ( msg . sender ); totalSupply -= balances [ msg . sender ]; balances [ msg . sender ] = 0 ; paidOut [ msg . sender ] = 0 ; return true ; } The basic idea is this: propose a split. Execute the split. When the DAO goes to withdraw your reward, call the function to execute a split before that withdrawal finishes. The function will start running without updating your balance, and the line we marked above as "the attacker wants to run more than once" will run more than once. What does that do? Well, the source code is in TokenCreation.sol, and it transfers tokens from the parent DAO to the child DAO. Basically the attacker is using this to transfer more tokens than they should be able to into their child DAO. How does the DAO decide how many tokens to move? Using the balances array of course: uint fundsToBeMoved = ( balances [ msg . sender ] * p . splitData [ 0 ]. splitBalance ) / p . splitData [ 0 ]. totalSupply ; Because p.splitData[0] is going to be the same every time the attacker calls this function (it's a property of the proposal p, not the general state of the DAO), and because the attacker can call this function from withdrawRewardFor before the balances array is updated, the attacker can get this code to run arbitrarily many times using the described attack, with fundsToBeMoved coming out to the same value each time. The first thing the attacker needed to do to pave the way for his successful exploit was to have the withdraw function for the DAO, which was vulnerable to the critical recursive send exploit, actually run. Let's look at what's required to make that happen in code (from DAO.sol): 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 )) throw ; paidOut [ _account ] += reward ; return true ; } If the hacker could get the first if statement to evaluate to false, the statement marked vulnerable would run. When that statements runs, code that looks like this would be called: function payOut ( address _recipient , uint _amount ) returns ( bool ) { if ( msg . sender != owner || msg . value > 0 || ( payOwnerOnly && _recipient != owner )) throw ; if ( _recipient . call . value ( _amount )()) { PayOut ( _recipient , _amount ); return true ; } else { return false ; } Notice how the marked line is exactly the vulnerable code mentioned in the description of the exploit we linked! That line would then send a message from the DAO's contract to "_recipient" (the attacker). "_recipient" would of course contain a default function, that would call splitDAO again with the same parameters as the initial call from the attacker. Remember that because this is all happening from inside withdrawFor from inside splitDAO, the code updating the balances in splitDAO hasn't run. So the split will send more tokens to the child DAO, and then ask for the reward

情報源を読む

リサーチ

  • “AIインシデント”の定義
  • “AIインシデントレスポンス”の定義
  • データベースのロードマップ
  • 関連研究
  • 全データベースのダウンロード

プロジェクトとコミュニティ

  • AIIDについて
  • コンタクトとフォロー
  • アプリと要約
  • エディタのためのガイド

インシデント

  • 全インシデントの一覧
  • フラグの立ったインシデント
  • 登録待ち一覧
  • クラスごとの表示
  • 分類法

2024 - AI Incident Database

  • 利用規約
  • プライバシーポリシー
  • Open twitterOpen githubOpen rssOpen facebookOpen linkedin
  • e1b50cd