به راهنمای مبتدیان برای حسابرسی قرارداد هوشمند خوش آمدید! یکی از بهترین راهها برای شروع حسابرسی قراردادهای هوشمند این است که وارد شوید و به چند نوع آسیبپذیری رایج در قراردادهای هوشمند نگاه کنید.
اگر قبلاً درک اولیه ای از زبان برنامه نویسی Solidity اتریوم داشته باشید، مفید خواهد بود. همانطور که به برخی از کدهای نوشته شده توسط برنامه نویسان Noob solidity نگاه خواهیم کرد.
حمله به ورود مجدد
سناریوی دنیای واقعی:
تصور کنید 50 شکلات دارید. شما یک خواهر کوچک شیطون دارید که به او اجازه داده اید در هر لحظه فقط 2 شکلات از شما بگیرد. شما همچنین نمی خواهید بیش از 10 شکلات در روز به او بدهید، از ترس اینکه پوسیدگی دندان پیدا کند. برای اطمینان از این موضوع، هر شب شمارش می کنید که چند شکلات با شما باقی مانده است. فکر میکنی کار میکنه؟ یا اینکه توسط خواهر کوچکت هک می شوی؟
متأسفانه کار نخواهد کرد! خواهرت متوجه می شود که تا عصر از تعداد شکلات هایت بی اطلاعی. بنابراین روز بعد، خواهر کوچک شما 6 بار قبل از غروب به شما سر می زند و هر بار 2 شکلات می گیرد! این همان چیزی است که ما به آن حمله ورود مجدد می گوییم.
در اینجا شما به جای اینکه هر بار که خواهرتان 2 شکلات از شما می گیرد، تعداد شکلات هایی را که در شب دارید به روز می کنید. این همان چیزی است که در قرارداد هوشمند نیز اتفاق می افتد. قرارداد هوشمند تعادل خاصی را در نظر می گیرد در حالی که مهاجم در واقع مشغول برداشتن مقداری رمزارز از قرارداد چندین بار است.
مثال کد دنیای واقعی:
این کد متعلق به یک قرارداد هوشمند به نام است Unbanked. هر کسی می تواند اتر را از یک قرارداد بدون بانک خارج کند تا زمانی که موجودی msg.sender (یعنی تماس گیرنده withdraw
تابع ) بزرگتر یا برابر با مقدار درخواستی برای برداشت است.
function withdraw(uint _amount) { require(balances[msg.sender] >= _amount); msg.sender.call.value(_amount)(); balances[msg.sender] -= _amount;
}
توجه داشته باشید که یک کلمه کلیدی فراخوانی وجود دارد که برای ارسال مقدار مورد نیاز اتر به آن استفاده می شود msg.sender
. مهاجم می تواند با ایجاد قراردادی به نام Thief که در آن تابع برداشت را در a فراخوانی می کند، از این مورد سوء استفاده کند fallback()
تابع. آ fallback()
تابع در Solidity یک تابع ویژه است که با ارسال اتر به قرارداد هوشمند اجرا می شود.
این بدان معنی است که یک مهاجم قادر است به صورت بازگشتی تابع برداشت را فراخوانی کنید. بنابراین، قبل از به روز رسانی قرارداد هوشمند، مانده های msg.sender
در آخرین خط کد، مهاجم قبلاً چندین بار اتر را خارج کرده است. اگر موجودی ها قبل از استفاده از کلمه کلیدی فراخوانی به روز شوند، از این امر جلوگیری می شود بررسی ها-اثرات-تعامل ها الگوی.
تأثیر:
La اولین حمله بازگشت مجدد در سال 2016 در یک DAO (سازمان خودمختار غیرمتمرکز) اتفاق افتاد که منجر به هک حدود 50 میلیون دلاری شد. برای معکوس کردن این هک، جامعه اتریوم بلاک چین اتریوم را تقسیم کرد که باعث ایجاد ETC (اتریوم کلاسیک) و اتریوم (اتریوم) شد.
سرریز و زیر جریان حسابی
سناریوی دنیای واقعی:
بیایید یک بازی فکری انجام دهیم. این شامل یک چرخ چرخان است، و برنده بر اساس بزرگترین عددی که می تواند در چرخاندن چرخ به دست آورد، تعیین می شود. چرخ از 256 تا -256 علامت گذاری شده است.
قوانین بازی این است که نشانگر همه بازیکنان در ابتدای هر چرخش روی 0 قرار دارد. و بازیکن مجاز است فقط در جهت اعداد منفی بچرخد. چگونه در این بازی برنده خواهید شد؟
یک استراتژی خوب برای برنده شدن در این بازی هر بار این است که چرخ را با چنان قدرتی بچرخانید که چرخ تا 256- بچرخد و سپس در یک حرکت به 256 تبدیل شود. این امکان پذیر است زیرا 256 درست بعد از -256 روی چرخ می آید. این همان چیزی است که ما آن را زیر جریان حسابی می نامیم. و سرریز حسابی درست برعکس این است.
مثال کد دنیای واقعی:
An سرریز یا سرریز زمانی اتفاق می افتد که یک عملیات حسابی به حداقل یا حداکثر خود برسد.
function withdraw(uint _amount) public { require(balances[msg.sender] - _amount > 0); address payable to = payable(msg.sender); to.transfer(_amount); balances[msg.sender] -= _amount;
}
La _amount
پارامتر تابع برداشت یک عدد صحیح بدون علامت است. مقدار نگاشت تعادل (که مانند یک فرهنگ لغت در پایتون یا یک جفت کلید-مقدار در C++ یا جاوا است) نیز یک عدد صحیح بدون علامت است.
mapping(address => uint256) public balances
بیانیه مورد نیاز بررسی می کند که آیا مانده های msg.sender
مثبت است یا خیر اما این گزاره همیشه درست خواهد بود حتی اگر مقدار آن بیشتر از مانده باشد msg.sender
. این به این دلیل است که هر دو balances
و _amount
متغیرها از نوع عدد صحیح بدون علامت هستند و نتیجه محاسباتی آنها (بعد از underflow) نیز یک عدد صحیح بدون علامت خواهد بود!
و همانطور که ممکن است به خاطر داشته باشید، یک عدد صحیح بدون علامت همیشه مثبت است. این بدان معنی است که یک مهاجم قادر است مقدار نامحدودی اتر را از قرارداد هوشمند برداشت کند! می توانید یک مثال دقیق و کد پیاده سازی برای این آسیب پذیری پیدا کنید اینجا کلیک نمایید.
نکته مهم دیگری که در اینجا باید به آن توجه کرد این است که عملیات حسابی بین دو عدد صحیح بدون علامت نیز یک عدد صحیح بدون علامت است. اگر در قراردادهای هوشمند نادیده گرفته شود، می تواند خطرناک باشد، زیرا می تواند منجر به نقض امنیت ناخواسته شود!
function votes(uint postId, uint upvote, uint downvotes) { if (upvote - downvote < 0) { deletePost(postId) }
}
همانطور که ممکن است در مثال بالا متوجه شده باشید، دستور if کاملا بی معنی است upvote - downvote
همیشه مثبت خواهد بود و پست حذف خواهد شد حتی اگر downvotes
بزرگتر از upvotes
. برای جلوگیری از چنین حملاتی، توصیه می شود از نسخه کامپایلر Solidity بزرگتر از آن استفاده کنید 0.8.0.
تأثیر:
یک سکه به نام سکه PoWH در سال 2017 راه اندازی شد. اگرچه این یک بازی Ponzi بود، اما خود به دلیل یک اشکال سرریز حسابی هک شد که منجر به از دست دادن حدود 866 ETH یا 950,000 دلار در آن زمان شد. می توانید در این مورد به تفصیل بخوانید اینجا کلیک نمایید.
باید خواند شود: درس هایی از حمله به Tinyman، بزرگترین DEX در Algorand
حمله انکار سرویس
سناریوی دنیای واقعی:
تصور کنید در یک دانشگاه فناوری بیت کوین هستید. همه چیز خوب به نظر می رسد به جز اینکه یک میز ناهارخوری مشترک برای همه وجود دارد. و متأسفانه افراد کمی از کلاس دیگر هستند که همیشه قبل از هر کس از کلاس شما میز ناهارخوری را اشغال می کنند.
در سناریوی عملی، آنها خدمات ضروری را به همه انکار می کنند که منجر به از دست دادن زمان گرانبها می شود. این همان چیزی است که ما آن را "حمله انکار سرویس" می نامیم.
مثال کد دنیای واقعی:
در بازی به نام پادشاه اتر، هر کسی می تواند پادشاه شود. اما قانون برای پادشاه شدن این است که فرد باید بیشتر از پادشاه فعلی اتر سپرده کند. این را می توان با تماس گرفتن انجام داد claimThrone()
عملکرد قرارداد پادشاه اتر که در آن شخص اتر را مستقیماً برای پادشاه قبلی می فرستد و پادشاه جدید می شود.
function claimThrone() external payable { require(msg.value > balance, "Need to pay more to become the king"); (bool sent, ) = king.call{value: balance}(""); require(sent, "Failed to send Ether"); balance = msg.value; king = msg.sender; }
همانطور که ممکن است حدس بزنید، این کد در برابر حمله DoS آسیب پذیر است، اما چگونه؟ برای این کار، باید بدانید که دو نوع آدرس در اتریوم وجود دارد که اول آن آدرس است آدرس یک خارجی حساب متعلق به یا به سادگی آدرس یک کیف پول، و دوم است آدرس قرارداد. اکنون اتر را می توان از هر یک از این نوع آدرس ها ارسال کرد.
اگر این، اتر توسط آدرس قرارداد ارسال شود، قرارداد پادشاه می شود. اما فرض کنیم که این قرارداد جدید دارای یک fallback()
تابعی که اگر قرارداد بخواهد اتر را بپذیرد ضروری است. سپس اگر فرد جدیدی آمد و سعی کرد با آن تماس بگیرد claimThrone()
عملکرد، همیشه شکست خواهد خورد!
توجه داشته باشید که این نیز تا حدی اتفاق می افتد زیرا claimThrone()
تابع به صراحت بررسی می کند که آیا انتقال اتر در دومین عبارت مورد نیاز موفقیت آمیز بوده است یا خیر. می توانید کد کامل را پیدا کنید و یک حمله DoS بر روی آن انجام دهید اینجا کلیک نمایید.
همچنین اگر کد دارای یک حلقه روی آرایه ای با اندازه های بزرگ باشد، ممکن است یک کد در برابر حمله DoS آسیب پذیر باشد. این اتفاق می افتد زیرا حد گاز در چنین مواردی می توان از آن فراتر رفت. می توانید در مورد آن بخوانید اینجا کلیک نمایید.
تأثیر:
یک بازی به نام دولتی ذهنی، که ظاهراً یک طرح پونزی بود، با 1100 اتر گیر کرد زیرا برای پردازش پرداخت به مقدار زیادی گاز نیاز بود.
تصادفی ناامن
سناریوی دنیای واقعی:
روزی مردی به نام هسکی بود که همیشه میمونش Pesky او را همراهی می کرد. هسکی بازی های قرعه کشی انجام داد و سود خوبی به دست آورد. یک روز آلیس متوجه شد که هسکی به شدت به میمونش Pesky خیره شده است. سپس او را دید که روی یک کاغذ چیزی می نویسد و آن را در پاکتی مهر و موم کرد. کنجکاو تصمیم گرفت بیشتر تحقیق کند.
بعداً همان شب، آلیس دید که برنده قرعه کشی با باز کردن عمومی پاکت مهر و موم شده تعیین می شود. پس از چند روز تماشای او، آلیس متوجه شد که هسکی با نگاه کردن به حرکات Pesky شماره قرعه کشی برنده را تعیین می کند (مثلاً اگر میمون سرش را خاراند، هسکی 10 را یادداشت کرد)! حالا آلیس فرمول برنده شدن در هر لاتاری را داشت و فقط باید بلیط بخت آزمایی را با شماره مناسب خریداری می کرد!
هسکی فرض کرده بود که روش "تصادفی" او برای تعیین برنده بخت آزمایی هرگز قابل تشخیص نیست، اما او در واقع نادرست بود.
مثال کد دنیای واقعی:
در این مثال، یک عدد تصادفی بر اساس هش ترکیب شماره یک بلوک و مهر زمانی بلوک آن ایجاد میشود. سپس این هش به متغیر پاسخ اختصاص داده می شود. اکنون هر کسی که این عدد تصادفی (به ظاهر) را حدس بزند، 1 اتر جایزه می گیرد. به نظر شما این غیرقابل هک است؟
function guess(uint _guess) public { uint answer = uint( keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)) ); if (_guess == answer) { (bool sent, ) = msg.sender.call{value: 1 ether}(""); require(sent, "Failed to send Ether"); } }
جواب منفی! یک مهاجم همچنان می تواند این عدد تصادفی را حدس بزند و به سادگی کد را کپی کنید تا مقدار اختصاص داده شده به متغیر پاسخ را تولید کند و همان متغیر پاسخ را به متغیر پاسخ ارسال کند. guess()
تابع!
guessTheRandomNumber.guess(answer);
می توانید کد کامل را پیدا کنید اینجا کلیک نمایید. برای جلوگیری از این حمله، توصیه می شود از یک تابع تصادفی قابل تأیید استفاده کنید VRF Chainlink.
تأثیر:
حدود 400 ETH به دلیل حمله به آن از بین رفت قرعه کشی میلیاردها هوشمند قرارداد. با کمال تعجب، حتی خود قرعه کشی قرارداد نیز یک طرح پونزی بود (اوه!).
دستکاری زمان
سناریوی دنیای واقعی:
ساتوشی عاشق خوردن کلوچه است. او عاشق همه نوع کلوچه هایی است که مادرش درست می کند. اما مادرش بسیار سخت گیر است و احساس می کند که زیاد خوردن کلوچه برایش خوب نیست. بنابراین مادرش قانونی وضع میکند که او کلوچهها را فقط ساعت 8 شب میگیرد.
همان روز در ساعت 7:45 بعد از ظهر، ساتوشی نزد مادرش می دود و از او کوکی می خواهد. مادرش می پرسد: "ساعت چند است؟"
"ساعت 8 O است!" - او پاسخ می دهد.
"باشه. سپس کلوچه ها را از کمد من بردارید.»
و به این ترتیب، ساتوشی توانست زمان را با موفقیت 15 دقیقه دستکاری کند تا بتواند کوکی های خود را دریافت کند! چه دختر تشنه شیرینی!
مثال کد دنیای واقعی:
مهر زمانی یک بلوک را می توان با حدود دستکاری کرد ثانیه 15 توسط یک معدنچی به این ترتیب، یک ماینر میتواند مهر زمانی مطلوبی تعیین کند و تراکنش خود را در همان بلوکی که استخراج میکند، قرار دهد. کارکرد play()
متعلق به یک قرارداد بازی به نام G-Dot است.
function play() public { require(now > 1640392200 && neverPlayed == true); neverPlayed = false; msg.sender.transfer(1500 ether);
}
این قرارداد 1500 اتر به بازیکنی که اولین کسی است که تابع پخش را فراخوانی می کند، پاداش می دهد. اما همانطور که می بینید، تابع play تنها در صورتی می تواند فراخوانی شود که now یا block.timestamp تراکنش که شامل تماس با play()
تابع، بزرگتر از زمان عصر 1640392200.
یک ماینر میتواند به راحتی این مهر زمانی را دستکاری کند و تراکنش خود را برای فراخوانی درج کند play()
عملکرد در همان بلوک به طوری که او خود بازیکن اول است. به این ترتیب تضمین می شود که ماینر برنده بازی خواهد بود!
تأثیر:
از block.timestamp برای تولید اعداد تصادفی در دولتي و بنابراین در برابر حملات دستکاری زمان آسیب پذیر بود.
با QuillAudits تماس بگیرید
QuillAudits یک پلت فرم حسابرسی قرارداد هوشمند است که توسط QuillHash
فن آوری ها
این یک پلتفرم حسابرسی است که قراردادهای هوشمند را برای بررسی آسیبپذیریهای امنیتی از طریق بررسی دستی مؤثر با ابزارهای تجزیه و تحلیل استاتیک و دینامیکی، آنالایزرهای گاز و همچنین شبیهسازها بهدقت تجزیه و تحلیل و تأیید میکند. علاوه بر این، فرآیند حسابرسی همچنین شامل آزمایش واحد گسترده و همچنین تجزیه و تحلیل ساختاری است.
ما هم ممیزی قرارداد هوشمند و هم تست های نفوذ را برای یافتن پتانسیل انجام می دهیم
آسیب پذیری های امنیتی که ممکن است به یکپارچگی پلت فرم آسیب برساند.
اگر در ممیزی قراردادهای هوشمند نیاز به کمک دارید، با کارشناسان ما تماس بگیرید اینجا!
برای به روز بودن از کار ما، به انجمن ما بپیوندید:
توییتر | لینک | فیس بوک | تلگرام
پست راهنمای مبتدیان برای حسابرسی قرارداد هوشمند: بخش 1 به نظر می رسد برای اولین بار در وبلاگ Quillhash.
منبع: https://blog.quillhash.com/2022/01/19/beginners-guide-to-smart-contract-auditing-part-1/
- "
- &
- 000
- 2016
- 7
- درباره ما
- حساب
- نشانی
- معرفی
- قبلا
- هر چند
- تحلیل
- حسابرسی
- خود مختار
- شروع
- بهترین
- بیت کوین
- بلاکچین
- اشکال
- خرید
- صدا
- موارد
- چک
- کلاسیک
- رمز
- سکه
- ترکیب
- مشترک
- انجمن
- شامل
- قرارداد
- قرارداد
- بیسکویت ها
- میتوانست
- ایجاد
- عضو سازمانهای سری ومخفی
- جاری
- دائو
- روز
- غیر متمرکز
- خود داری از خدمات
- جزئیات
- دگزامتازون
- پایین
- به آسانی
- خوردن
- ETH
- اتر
- ethereum
- blockchain اتریوم
- اترئوم کلاسیک
- مثال
- بهره برداری
- فیس بوک
- پایان
- نام خانوادگی
- رایگان
- تابع
- بازی
- بازیها
- GAS
- تولید می کنند
- GitHub
- رفتن
- خوب
- راهنمایی
- هک
- هک
- مخلوط
- سر
- اینجا کلیک نمایید
- چگونه
- HTTPS
- بررسی
- IT
- جاوه
- پیوستن
- پرش
- پادشاه
- زبان
- بزرگ
- لاین
- لینک
- طولانی
- به دنبال
- قرعه کشی
- مرد
- میلیون
- مادر
- تعداد
- کدام سازمان ها
- مقاله
- الگو
- پرداخت
- مردم
- قطعه
- سکو
- بازی
- بازیکن
- پونزو
- طرح پونزی
- قدرت
- روند
- برنامه نویسان
- برنامه نويسي
- عمومی
- ق
- معکوس
- این فایل نقد می نویسید:
- پاداش
- قوانین
- ساتوشی
- تیم امنیت لاتاری
- تنظیم
- هوشمند
- قرارداد هوشمند
- قراردادهای هوشمند
- So
- استحکام
- چیزی
- چرخش
- انشعاب
- آغاز شده
- بیانیه
- استراتژی
- موفق
- موفقیت
- فن آوری
- تست
- از طریق
- زمان
- ابزار
- معامله
- بدون بانک
- دانشگاه
- به روز رسانی
- ارزش
- آسیب پذیری ها
- آسیب پذیری
- آسیب پذیر
- کیف پول
- چی
- چرخ
- WHO
- پیروزی
- مهاجرت کاری
- نوشته