
編集者注: この記事は以下から引用しましたスローミストテクノロジー(ID:SlowMist)編集者注: この記事は以下から引用しました
スローミストテクノロジー(ID:SlowMist)
、Odailyからの許可を得て転載。
WETH: 55159.02134,
WBTC: 9.01152,
CHAI: 77930.93433,
HBTC: 320.27714,
HUSD: 432162.90569,
BUSD: 480787.88767,
PAX: 587014.60367,
TUSD: 459794.38763,
USDC: 698916.40348,
USDT: 7180525.08156,
USDx: 510868.16067,
imBTC: 291.3471
SlowMist Technology のマネーロンダリング対策 (AML) システムの予備統計分析によると、Lendf.Me が攻撃を受けた累積損失は約 2,469 万 6,616 米ドルで、具体的に盗まれた通貨と金額は以下のとおりです。
詳細な分析プロセスは次のとおりです。
攻撃の詳細
副題
攻撃の詳細
今回 Lendf.Me を攻撃した攻撃者のアドレスは 0xa9bf70a420d364e923c74448d9d817d3f2a77822 で、攻撃者はコントラクト 0x538359785a8d5ab1a741a0ba94f26a800759d91d を展開して Lendf.Me を攻撃しました。
Etherscan で攻撃者のトランザクションの 1 つを表示する: https://etherscan.io/tx/0xae7d664bdfcc54220df4f18d339005c6faf6e62c9ca79c56387bc0389274363b
攻撃者は最初に 0.00021593 imBTC を入金しましたが、Lendf.Me から 0.00043188 imBTC を引き出すことに成功し、その引き出し金額は入金金額のほぼ 2 倍であったことがわかりました。では、攻撃者はどのようにして短期間の取引で 2 倍の残高を手に入れたのでしょうか?そのためには、トランザクション内の各アクションを深く分析して、何が起こったのかを確認する必要があります。
bloxy.info でトランザクションを表示することで、完全なトランザクション プロセスを知ることができます。
その直後、supply() 関数の 2 回目の呼び出し中に、攻撃者は自身のコントラクト内の Lendf.Me のdrawr() 関数の呼び出しを開始し、最終的に現金を引き出します。
ここで、攻撃者のdrawr()呼び出しがtransferFrom関数内で発生していること、つまりLendf.MeがtransferFromを通じてユーザーのtokensToSend()フック関数を呼び出していることが簡単にわかります。明らかに、攻撃者は、supply() 関数を通じて Lendf.Me コントラクトを再入力し、再入攻撃を引き起こしました。では、攻撃の具体的な詳細は何でしょうか?次にLendf.Meのコントラクトコードを追ってみましょう。
副題
コード分析
一連の処理の後、Lendf.Me の Supply() 関数は doTransferIn 関数を呼び出して、ユーザーが提供した通貨をコントラクトに入金し、市場変数の情報を割り当てます。先ほどの攻撃プロセスを振り返ると、攻撃者は 2 番目の Supply() 関数、つまり 1590 行目以降の 2 番目の Supply() 関数で、リエントラントによって現金を引き出すためにdraw() 関数を呼び出します。行 1590 以降のコードは、withdraw() の実行後も実行され続けます。ここでの操作は、攻撃者の引き出し可能な残高の増加につながります。
Supply() 関数を詳しく分析してみましょう。
上図によれば、supply()関数の最後でマーケットとユーザーの残高が更新されることが分かりますが、その前に関数の開始時にユーザーの残高を事前に取得し、次のように、localResults.userSupplyCurrent に保存されます。
localResults 変数に値を代入することで、ユーザーの送金情報がこの変数に一時的に格納され、攻撃者はこの時点でdrawr() 関数を実行します。
ここで重要な点が 2 つあります。
1. 関数の開始時に、コントラクトはまずストレージ マーケット変数と SupplyBalance 変数を取得します。
通常の出金ロジックでは、drawr()を単独で実行すると正常にユーザーの残高が引き落とされて更新されますが、攻撃者がsupply()にwithdraw()を埋め込んでいるため、drawr()関数内でユーザーの残高が更新されてしまいます。残高 (supplyBalance)、supply() 関数で実行される次のコード、つまり 1590 行目以降、ユーザーの残高が再度更新され、更新に使用された値が前のコードの先頭に保存されます。 Supply() 関数 localResults 内のユーザーの元のデポジットに、攻撃者による Supply() 関数デポジットへの最初の呼び出しの値を加えたもの。
このような操作では、出金後にユーザーの残高が差し引かれているにもかかわらず、再度ユーザーが出金額を差し引いていない場合、次のsupply()関数のロジックにより値が上書きされ、攻撃者が出金操作を実行させますが、残高を差し引くのではなく、残高を増加させました。このようにして、攻撃者は、Lendf.Me が空になるまで、指数関数的な量の現金を引き出すことができます。
複数の契約が連携する場合には、複数者契約のコードセキュリティやビジネスセキュリティも確認し、さまざまなビジネスシナリオを組み合わせた場合のセキュリティ問題を十分に考慮する必要があります。
OpenZeppelin の ReentrancyGuard など、主要なビジネス操作メソッドにロック メカニズムを追加します。
コントラクトを開発するときは、まずこのコントラクトの変数を変更してから、外部呼び出しを行うという書き方を使用してください。
プロジェクトがオンラインになる前に、優秀なサードパーティのセキュリティ チームが包括的なセキュリティ監査を実施し、潜在的なセキュリティ問題を可能な限り発見します。
複数の契約が連携する場合には、複数者契約のコードセキュリティやビジネスセキュリティも確認し、さまざまなビジネスシナリオを組み合わせた場合のセキュリティ問題を十分に考慮する必要があります。
契約では、「ブラックスワン」イベントが発生した場合に時間損失を検出して停止できるように、可能な限り一時停止スイッチを設定する必要があります。
セキュリティは動的であり、各プロジェクト関係者は、自分のプロジェクトに関連する可能性のある脅威インテリジェンスをタイムリーに取得し、潜在的なセキュリティ リスクを迅速に調査する必要もあります。
契約では、「ブラックスワン」イベントが発生した場合に時間損失を検出して停止できるように、可能な限り一時停止スイッチを設定する必要があります。