يحدث هجوم الطابع غير المحدود (Infinite Mint) عندما يسمح عقد رمز مميز للمهاجم بإنشاء رموز بدون تصريح صحيح، أو بدون تطبيق حد أقصى للإمداد، أو كلاهما معًا. يقوم المهاجم بطباعة رصيد عشوائي، ثم يبيع أو يبادل أو يودع أو يضارب على الرموز التي تم إنشاؤها حديثًا قبل أن ترد الأسواق والتكاملات.
العقوبة ليست مجرد تضخم، فالطباعة اللانهائية يمكن أن تُدمّر قيمة التبادل للرمز، وتستهلك مجمعات السيولة، وتخلق ضمانات غير مدعومة في أنظمة الإقراض أو الخزانة. في 5 يونيو 2022، سُجلت خسارة إلقراند بقيمة 113 مليون دولار في حادثة تصنف كـ”Infinite Mint”، مما يُظهر كيف يمكن أن يتحول ضعف الحدود في الإصدار إلى خسارة على مستوى النظام البيئي.
ما هو هجوم الطابع غير المحدود وكيف يعمل؟
يستغل هجوم الطابع غير المحدود مسار إصدار رمز لا يثبت بشكل موثوق من يمكنه الطباعة وكمية الرموز المسموح بها. Function mint العامة أو غير المحمية بشكل صحيح تتيح للمهاجم إنشاء أرصدة تعتبرها باقي البروتوكولات رموز ERC-20 شرعية.
تكون وظيفة mint آمنة فقط عندما يتطابق تفويض المنفذ، وظروف الإصدار، وحسابات العرض مع التصميم الاقتصادي للبروتوكول. إذا كان بإمكان أي منفذ الوصول إلى منطق _mint-المماثل، أو إذا كان منفذ ذي امتيازات يمكنه تجاوز حد العرض، فإن ندرة الرمز ليست سوى افتراض.
يتمثل الهجوم النموذجي للطابع غير المحدود في التسلسل التالي:
- يحدد المهاجم مسار إصدار عام أو يمكن تجاوز فحص الوصول إليه.
- يتصل المهاجم بالوظيفة مع مستلم ومبلغ يسيطر عليه المهاجم.
- يزيد العقد من رصيد المهاجم وغالبًا إجمالي العرض.
- ينقل المهاجم الرموز المُصدّرة إلى مجمع DEX أو سوق إقراض أو جسر أو خزنة.
- يبادل المهاجم الرموز الاصطناعية بأصول حقيقية أو يستخدمها كضمان.
- يتحمل مزودو السيولة والمقرضون وحاملو الرموز الخسارة الاقتصادية الناتجة.
الثغرات في الطباعة اللانهائية تكون أكثر خطورة عندما يقبل عقود أخرى أرصدة الرموز دون التحقق بشكل مستقل من كيفية إنشائها. بروتوكول الإقراض لا يميز بين الرموز التي تم إصدارها من خلال الإصدار المقصود وبين الرموز التي تم إصدارها عبر تجاوز فحص الوصول المفقود.
ما هي الأنماط في Solidity التي تسبب الثغرات في الطابع غير المحدود؟
عادةً، تنشأ ثغرات الطابع غير المحدود من عدم وجود تفويض، أو غياب حدود العرض، أو أن التفويض صحيح من الناحية الظاهرية لكنه يمكن الوصول إليه عبر مسار إداري غير مقصود. راجع كل وظيفة يمكنها زيادة الرصيد، بشكل مباشر أو غير مباشر.
النمط الأكثر وضوحًا هو وظيفة mint عامة بدون قيود على من يمكنه الاتصال:
function mint(address to, uint256 amount) external {
balanceOf[to] += amount;
totalSupply += amount;
}
هناك خطر مشابه عندما يتحقق المطور من هوية غير صحيحة، مثل استخدام tx.origin، الذي لا يجب أن يحدد سلطة الإصدار، لأن المهاجم يمكن أن يحث محفظة ذات امتيازات على استدعاء عقد وسيط ضار. بالإضافة إلى ذلك، فإن التحقق مما إذا كان المستلم مميزًا لا يثبت أن المنفذ مخول لإصدار الرموز.
تشمل الأسباب الشائعة الأخرى:
- وظيفة
setMintercallable من قبل أي حساب، مما يسمح للمهاجمين بمنح أنفسهم حقوق إصدار. - بروكسي قابل للترقية يمكن أن يستبدل قيود الطباعة.
- جسر أو معالج عبر السلاسل يثق بالرسائل العشوائية كتعليمات إصدار.
- فرض حد في وظيفة إصدار واحدة دون وجوده في طريق إصدار آخر، مثل طلبات المكافآت أو وظائف التهجير.
- أخطاء حسابية أو تحويل وحدات تجعل الحد الاسمي غير فعال.
- محاسبة حرق وطباع حيث لا يتم التحقق فعليًا من الحرق قبل إصدار الرموز البديلة.
متغير totalSupply وحده لا يعتبر وسيلة للتحكم في الإمداد، فالعقد يجب أن يرفض كل عملية إصدار تؤدي إلى تجاوز totalSupply + amount للحد الأقصى المقصود. يجب تطبيق هذا المبدأ على كل مسار إصدار، بما في ذلك استرداد الإدارة، والإصدار عبر الجسر، والمكافآت، والتهجر.
كيف تبدو رموز Solidity الضعيفة والمثبتة ضد الطابع غير المحدود؟
عقد قابل للثغرة يتيح وظيفة إصدار غير مقيدة، بحيث يمكن لأي حساب خارجي أن يصدر أي مبلغ لنفسه، بدون حد أقصى يحد من الضرر.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract VulnerableInfiniteMintToken {
string public constant name = "Vulnerable Token";
string public constant symbol = "VULN";
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
event Transfer(address indexed from, address indexed to, uint256 amount);
function mint(address to, uint256 amount) external {
// ثغرة: يمكن لأي جهة خارجية أن تخلق رموزًا عشوائية.
totalSupply += amount;
balanceOf[to] += amount;
emit Transfer(address(0), to, amount);
}
function transfer(address to, uint256 amount) external returns (bool) {
require(balanceOf[msg.sender] >= amount, "رصيد غير كاف");
unchecked {
balanceOf[msg.sender] -= amount;
}
balanceOf[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
}
أما النسخة المصححة فتفصل بين المسؤول عن الإصدار ومن يملك صلاحية إصدار الرموز، وتفرض حدًا أقصى قبل كل زيادة على الرصيد. الحد الأقصى ليس بديلاً عن التفويض، والتفويض ليس بديلاً عن الحد، فكل منهما يعالج حالات فشل مختلفة.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract CappedRoleMintToken {
string public constant name = "Capped Token";
string public constant symbol = "CAP";
address public immutable admin;
uint256 public immutable maxSupply;
uint256 public totalSupply;
mapping(address => bool) public isMinter;
mapping(address => uint256) public balanceOf;
event Transfer(address indexed from, address indexed to, uint256 amount);
event MinterUpdated(address indexed minter, bool allowed);
modifier onlyAdmin() {
require(msg.sender == admin, "ليس مسؤولاً");
_;
}
modifier onlyMinter() {
require(isMinter[msg.sender], "ليس مخولًا بالإصدار");
_;
}
constructor(uint256 maxSupply_) {
require(maxSupply_ > 0, "الحد الأقصى صفري");
admin = msg.sender;
maxSupply = maxSupply_;
isMinter[msg.sender] = true;
}
function setMinter(address minter, bool allowed) external onlyAdmin {
isMinter[minter] = allowed;
emit MinterUpdated(minter, allowed);
}
function mint(address to, uint256 amount) external onlyMinter {
// تصحيح: لا يمكن للإصدار المصرح به أن يتجاوز الحد الأقصى الثابت.
require(totalSupply + amount <= maxSupply, "تجاوز الحد الأقصى");
totalSupply += amount;
balanceOf[to] += amount;
emit Transfer(address(0), to, amount);
}
function transfer(address to, uint256 amount) external returns (bool) {
require(balanceOf[msg.sender] >= amount, "رصيد غير كاف");
unchecked {
balanceOf[msg.sender] -= amount;
}
balanceOf[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
}
إذا كانت بروتوكولك يتضمن إصدار مكافآت، أو عبر الجسر، أو حصص خزنة، أو عمليات تهجير، فإن مراجعة العقود الذكية والأمان يمكن أن تتتبع كل مسار إصدار العودة إلى تفويضه والتحقق من الثابت في العرض قبل النشر.
كيف تمنع هجمات الطابع غير المحدود في Solidity؟
يتطلب منع هجمات الطابع غير المحدود تصميم إصدار متعدد الطبقات: سلطة واضحة، قيود على الحد الأقصى، صلاحيات إدارية محدودة، واختبارات تثبت أنه لا يمكن لطريق آخر إنشاء أرصدة. يعتمد التصميم المناسب على ما إذا كان العرض ثابتًا، أو محدودًا، أو يعتمد على الإصدار، أو مدعومًا بضمان، أو عبر الجسر.
| التقنية | ماذا توقف | تكلفة الغاز أو التعقيد | القيد |
|---|---|---|---|
وظيفة mint عبر دور مخصص |
إصدار رموز من قبل أي طرف خارجي | قراءة حالة التخزين للتحقق من الدور | الممول المخترق يمكن أن يصدر ضمن سلطته |
| الحد الأقصى الثابت للإمداد | إصدار يتجاوز الحد المسموح | عملية حساب واحدة للتحقق لكل إصدار | لا يمنع طرف مخول من إصدار كامل الحد قبل الوقت |
| فصل الأدوار الإدارية والمخولة | منح مخول لنفسه صلاحية أكثر بسلاسة | رمز إدارة إضافي للأدوار | يظل المسؤول حد الثقة الحاسم |
| إدارة متعددة التوقيعات أو مؤجلة | تغييرات منفردة على المخولين أو الإعدادات | تأخير عملياتي وتكاليف حوكمة | لا يصحح المنطق الخاطئ للـ mint الذي تم نشره بالفعل |
مسار _mint مركزي داخلي موحد |
إغفال فحوصات الحد في وظائف الإصدار الثانوية | مجهود إعادة الهيكلة | يجب أن يجد المراجع كل مسار خارجي إلى _mint |
| مراقبة الأحداث لنشاط الطباعة | اكتشاف متأخر للإصدار غير الطبيعي | البنية التحتية للمراقبة خارج السلسلة | لا يمكن للمراقبة عكس عملية الطباعة الخبيثة بالفعل |
بالنسبة للرموز ذات العرض الثابت، غالبًا يكون النمط الأقوى هو إصدار كامل الحصة عند نشر العقد وإزالة كل إمكانيات الإصدار لاحقًا. أما الرموز القائمة على الإصدار، فحدد جدولًا زمنيًا قابلاً للقياس، وطبّق الحد الإجمالي في وظيفة الإصدار الداخلية المشتركة، وتأكد من أن كل وظيفة خارجية لديها تفويض واضح.
تعتبر منهجية Soken إصدار الرموز كحدود لإنشاء الأصول، وليس كوظيفة ملائمة. يجب أن يكون لكل مسار يؤدي إلى زيادة في رصيد الرموز نموذج تفويض موثق، وحد اقتصادي صريح، ونقطة ثابتة يمكن اختبارها.
ما هي الحوادث الحقيقية التي صُنفت على أنها هجمات الطابع غير المحدود؟
تُظهر الحوادث المسجلة أن عيوب إصدار الرموز تؤثر على أصول قائمة و بروتوكولات حديثة في عدة سلاسل. تُصنف الحوادث تحت اسم طباعة غير محدودة في قاعدة بيانات اختراقات DefiLlama.
| البروتوكول | التاريخ | المبلغ المفقود | السلسلة |
|---|---|---|---|
| إلقراند | 2022-06-05 | 113 مليون دولار | إلقراند |
| Paid Network | 2021-03-05 | 27 مليون دولار | إيثريوم |
| Gala | 2024-05-20 | 22.3 مليون دولار | إيثريوم |
| Haven Protocol | 2021-07-03 | 8.2 مليون دولار | Haven |
| OcelotDex | 2024-09-20 | 4.9 مليون دولار | إيثريوم |
| TSR | 2026-06-01 | 2.5 مليون دولار | BSC |
| Raydium AMM | 2026-06-10 | 1.3 مليون دولار | سولانا |
| Flooring Protocol | 2026-06-08 | 500 ألف دولار | إيثريوم |
تشترك هذه الحوادث في خاصية اقتصادية جوهرية: بمجرد دخول رموز غير مدعومة إلى سوق مدمج، يتجاوز الهجوم عقد الرمز ذاته. فالمجمعات السيولة تشتري العرض الاصطناعي، وأسواق الإقراض قد تقبله كضمان، والجسور أو الخزائن قد تنقله إلى أنظمة أخرى.
السؤال المهم هنا أوسع من “هل يمكن للمهاجم استدعاء mint؟”، فهو: “هل يمكن لأي مسار غير موثوق أو مفرط الثقة أن يجعل البروتوكول يتعرف على رموز غير مدعومة كأصول صحيحة؟”
كيف تكشف عن ثغرة الطابع غير المحدود قبل النشر؟
اكتشاف ثغرات الطابع غير المحدود يتطلب رسم جميع التحولات الحالة التي تزيد الرصيد، ثم إثبات أن كل عملية تمت بتصريح وبحفاظ على الثابت المنشود في العرض. يجب أن يبدأ المراجعة يدوياً بعقود الرموز، لكنه يجب أن يتابع داخل الجسور، والخزائن، وموزعي المكافآت، ووحدات الترقية، والمساعدات الإدارية.
الاتبع هذه قائمة التحقق:
- حدد كل عملية كتابة إلى الأرصدة، والحصص، وعدادات العرض، ووظائف
mintالداخلية. - تحقق من أن كل مسار إصدار خارجي لديه تفويض مناسب للمنفذ.
- تأكد من أن كل مسار يفرض نفس الحد أو قيود الإصدار.
- راجع الوظائف التي تضيف، أو تزيل، أو تستبدل المخولين والمديرين.
- تتبع مسارات الترقية والجسر والتهجير واستدعاءات التصريحات التي يمكن أن تصل إلى منطق
mint. - تحقق مما إذا كانت الحسابات ذات الامتيازات العالية يمكنها تغيير الحد، أو المذّكر، أو وحدة الحسابات بشكل غير متوقع.
- تحقق من أن أحداث
mintوtotalSupplyتتطابق مع إنشاء الأرصدة الفعلي. - جرب التكاملات التي تقبل الرمز كضمان، أو سيولة، أو أصول خزنة.
أداة Slither مفيدة لفهرسة الوظائف القابلة للنداء من الخارج، والتغييرات ذات الامتيازات العالية، ونماذج الوصول المشبوهة، لكنها لا تستطيع إثبات أن نموذج العرض في البروتوكول صحيح اقتصاديًا. التحليل اليدوي ضروري لمسارات الإصدار غير المباشرة وسلطة العقود المتبادلة.
يمكن أن يستخدم اختبار التحمُّل في Foundry لاختبار أن المتصلين العشوائيين لا يمكنهم الإصدار، وأن العرض لا يتجاوز الحد. تعتبر اختبارات الثوابت فعالة بشكل خاص لأنها تعبر عن الخاصية الأمنية مباشرة، مثل:
function invariant_supplyNeverExceedsCap() public view {
require(token.totalSupply() <= token.maxSupply(), "تم تجاوز الحد الأقصى للعرض");
}
يجب أن تؤكد الخاصية الثانية أن فقط الجهات المصرح لها يمكنها زيادة العرض، ويتم اختبار ذلك في جميع الوظائف العامة، وليس فقط في وظيفة mint. للمزيد من البحوث حول السيطرة على الوصول، ومحاسبة الرموز، والمخاطر المتعلقة بالعقود، زوروا مركز Soken.
رؤية أمنية: تعتبر منهجية Soken أن كل وحدة رمزية جديدة هي مسؤولية أنشأها البروتوكول. إذا لم يتمكن العقد من إثبات من هو الذي سمح بإنشائها ولماذا تبقى ضمن نموذج الإصدار، فإن مسار الطباعة هو حد أمان يتطلب إعادة تصميم.
تحويل التحقق من الدور وحد حدود الإمداد إلى عملية مقيدة ومسؤولة، لكن فقط إذا استخدمها كل مسار إصدار. المهمة الهندسية الأساسية هي مركزية إنشاء الرصيد وجعل التجاوزات مستحيلة من خلال التصميم.
قبل الإطلاق أو الترقية، اكتب ثابت إمداد الهدف بلغة بسيطة، وطبّقه في مسار الإصدار المشترك، وطبّقه بواسطة اختبارات الثوابت في Foundry.