در تاریخ ۲۹ اکتبر، پروتکل Cross-chain موسوم به zkLink مورد حمله قرار گرفت که منجر به خروج حدود ۷۸۳,۰۰۰ دلار شد. این گزارش به بررسی دقیق مکانیزم حمله و آسیبپذیریهای موجود در قراردادهای هوشمند این پروتکل میپردازد.
ریشه اصلی آسیبپذیری
آسیبپذیری اصلی در تابع claimMessage در قرارداد Arbitrator قرار داشت. این تابع وظیفه دریافت و ارسال پیامها را بر عهده دارد، اما دو نقص امنیتی اساسی در فرضیات اعتماد آن وجود داشت:
- اجرای فراخوانیهای خارجی دلخواه: قرارداد Arbitrator اجازه میداد که Relayer پارامترهای مربوط به فراخوانی خارجی را تعیین کند. این امر باعث میشد مهاجم بتواند با هویت قرارداد Arbitrator، توابع دلخواه را در قراردادهای دیگر (مانند Gatewayها) اجرا کند.
- سوءاستفاده از Transient Storage (EIP-1153): در فرآیند تایید، قرارداد از مقداری در Transient Storage برای مقایسه هش پیام استفاده میکرد. از آنجایی که این مقادیر در طول یک تراکنش پاک نمیشدند، مهاجم توانست با انجام یک تراکنش قانونی در ابتدای همان تراکنش، مقدار مورد نظر را در حافظه موقت قرار داده و سپس در فراخوانی دوم (که مخرب بود) از آن مقدار برای دور زدن بررسیهای امنیتی استفاده کند.
مکانیزم جعل ریشه دستهای (Batch Root Forgery)
مهاجم با استفاده از نقص فوق، توانست تابع syncBatchRoot را در قرارداد ZkLink فراخوانی کند. از آنجایی که این تابع تنها بررسی میکرد که فراخواننده حتماً یک Gateway باشد و محتوای فراخوانی را تایید نمیکرد، مهاجم موفق شد یک ریشه (Root) جعلی را به عنوان ریشه معتبر برای یک Batch مشخص ثبت کند. پس از ثبت این ریشه جعلی، مهاجم توانست با ارائه Merkle proofهای ساخته شده بر اساس آن ریشه، داراییهای پروتکل را به صورت قانونی از Bridge خارج کند.
توصیههای امنیتی و اصلاحات
برای جلوگیری از چنین حملاتی، اقدامات زیر توصیه میشود:
- پاکسازی مقادیر در Transient Storage قبل از انجام هرگونه فراخوانی خارجی جهت جلوگیری از استفاده مجدد از مقادیر قدیمی.
- استفاده از لیست سفید (Whitelist) برای سرویسهای پیامرسان جهت جلوگیری از استفاده از قراردادهای Gateway به عنوان سرویس پیامرسان.
- جلوگیری از بازنویسی (Overwrite) ریشههای دستهای (Batch Roots) موجود در پروتکل.
- افزودن مکانیزمهای تایید مستقل در لایه Gateway برای اطمینان از صحت محتوای بهروزرسانیها و عدم اتکا صرف به هویت فراخواننده.