تصور کنید زنجیرهای از حفرههای امنیتی که پیشتر نیازمند تیمی از چهار متخصص و ۹ ماه کار مداوم بود، اکنون در ۶ روز به نتیجه برسد. این کابوس جدید برای تیمهای دفاعی، نتیجهی دستاوردی است که XBOW با استفاده از لشکری از عاملهای هوش مصنوعی (AI Agents) به آن دست یافت. با استفاده از ناوگانی از عاملهای هوش مصنوعی که توسط مدل gpt-5.6-sol شرکت OpenAI قدرت گرفته بودند، این تیم با موفقیت توانست اجرای جاوااسکریپت در یک تب استاندارد کروم را به اجرای کامل کد در خارج از محیط ایزوله (Sandbox) رندر تبدیل کند.
به نقل از گزارش فنی منتشر شده در ۱۳ اکتبر ۲۰۲۶، این دستاورد نشاندهنده یک تغییر بنیادین در سرعت امنیت تهاجمی است. طبق گزارش منتشر شده در xbow.com، این کمپین از یکشنبه ۲ اوت تا جمعه ۷ اوت اجرا شد. هزینه این عملیات تقریباً ۴۰ هزار دلار برای پرداخت هزینههای API بود تا بتواند فرآیندهای جستوجو، تست فرضیات و توسعه اثبات مفهوم (PoC) را سازماندهی کند. گوگل در نهایت طبق قوانین VRP که در آوریل ۲۰۲۶ معرفی شد، اولین جایزه ۲۵۰ هزار دلاری برای یک زنجیره کامل (Full-chain) کروم را به این تیم اعطا کرد.
برای درک عمق این اتفاق و وخامت وضعیت، باید لایههای دفاعی را که این اکسپلویت آنها را متلاشی کرد، بررسی کنیم. مرورگرهای مدرن مانند کروم از استراتژی «دفاع در عمق» (Defense-in-depth) استفاده میکنند. ابتدا، موتور V8 از یک محیط ایزوله داخلی (In-process sandbox) برای مهار خرابیهای جاوااسکریپت استفاده میکند. در این ساختار، اکثر اشیای Heap جاوااسکریپت در یک منطقه رزرو شده ۴ گیگابایتی زندگی میکنند و بسیاری از اشارهگرها به صورت آفستهایی در این منطقه نمایش داده میشوند. مراجع به اشیای بومی (Native) و کدهای قابل اجرا، حفاظتهای اضافی دریافت میکنند. ثانیاً، کل فرآیند رندر در یک محیط ایزوله در سطح سیستمعامل محصور شده است تا دسترسی به ماشین میزبان کاملاً مسدود گردد.
مرحله اول: شکستن محیط ایزوله V8
نخستین رخنه در Maglev رخ داد؛ کامپایلر بهینهساز سطح میانی V8. عاملهای هوش مصنوعی با شناسایی یک باگ در نحوه مدیریت «بهینهزدایی» (Deoptimization) — یعنی فرآیند بازسازی اشیای جاوااسکریپت زمانی که فرضهای زمان اجرا شکست میخورند — توانستند این لایه را دور بزنند.
در مسیر آسیبپذیر در فایل maglev-code-generator.cc ، کامپایلر یک HeapNumber در فضای old-space برای یک مقدار مجازی ایجاد میکرد و خود شیء را در آرایه لیترال بهینهزدایی ذخیره مینمود. کد از BuildHeapNumber برای ایجاد یک NewHeapNumberFromBits با نوع تخصیص AllocationType::kOld استفاده میکرد و آن را از طریق StoreLiteral ذخیره مینمود. بهطور همزمان، متریالایزر عمومی در translated-state.cc شیء نگه داشته شده در اسلات ترجمه شده را مستقیماً در مالک بازسازی شده با استفاده از WRITE_FIELD و WRITE_BARRIER برای فیلدهای علامتگذاری شده با kStoreHeapObject مینوشت.
این وضعیت به V8 اجازه داد تا اشیای جاوااسکریپت متمایزی ایجاد کند که فیلدهای double آنها یک HeapNumber تغییرپذیر مشترک داشتند. این امر ناقض اصل تغییرناپذیری V8 بود که طبق آن HeapNumberRef فقط برای HeapNumbers تغییرناپذیر ایجاد میشود. مسیر آسیبپذیر باعث شد یک عدد تغییرپذیر شبیه به یک ثابت تغییرناپذیر به نظر برسد که کامپایلر مجاز به استفاده مجدد از آن بود.
با بهرهبرداری از این تضاد در تابع Array.prototype.at()، مهاجمان دسترسی خواندن و نوشتن در کل فضای ۴ گیگابایتی (Pointer-compression cage) V8 به دست آوردند. یک مشاهده کامپایلر دسترسی درون-محدوده (in-bounds) را تأیید میکرد، در حالی که مشاهده دیگر از همان ثابت برای آفست بارگذاری استفاده میکرد. تغییر فیلد double یک شیء، آفست را بدون ابطال استدلالهای مربوط به محدوده تغییر میداد.
سه تابع بارگذاری بهینهشده به اکسپلویت اجازه داد تا:
- اشارهگرهای V8 مجاور را افشا کند.
- ساختار یک آرایه packed-double را بازیابی کند.
- مرجع یک آرایه تعریفشده توسط مهاجم را به دست آورد.
این آرایه جعلشده برای بازنویسی متادیتای یک Uint8Array واقعی استفاده شد تا دسترسی ایندکسگذاری شده در کل فضای ایزوله گسترش یابد. برای یافتن آدرسهای پایدار، اکسپلویت مراجع را در فضای اشیای بزرگ (large-object space) V8 ذخیره کرد و بدین ترتیب توانست آدرسهای فعلی درون-ایزوله را در حین جمعآوری زباله (Garbage Collection) بدون نیاز به پین کردن اشیای متحرک بازیابی کند.
اصلاحیه مرحله اول
تیم V8 این نقص را با حذف مورد خاص literal-object برطرف کرد. اکنون HeapNumbers مجازی از مسیر متریالیزاسیون معمولی اشیای کپچر شده پیروی میکنند. Maglev بهطور صریح از تکرار آنها جلوگیری میکند و برای vobj::ObjectType::kHeapNumber از تابع CreateUnduplicatableId() استفاده میکند تا اطمینان حاصل شود که هر بهینهزدایی یک باکس تازه ایجاد میکند. این اصلاح در تغییر V8 شماره ۸۲۵۵۵۵۴ و شماره پرونده ۵۴۷۸۱۹۹۹ کرومیوم مستند شده است.
مرحله دوم: فرار از طریق WebP
تیم XBOW بهجای حمله مستقیم به محیط ایزوله V8، روی پیشفرضهای مصرفکنندگان پاییندستی حافظه V8 تمرکز کرد. آنها WebCodecs و ImageDecoder را هدف قرار دادند، بهویژه نحوه مدیریت اشیای ArrayBuffer منتقل شده.
- آسیبپذیری: در معناشناسی عادی، انتقال یک بافر باعث قطع اتصال (detach) نماهای آن میشود. با این حال، محیط ایزوله V8 بافرهای قطعشده را در برابر یک مهاجم داخلی از نظر معنایی تغییرناپذیر نمیکرد. اکسپلویت از یک نوشتن درون-ایزوله استفاده کرد تا یک نمای
Uint8Arrayفعال را از زنجیره اعلان قطع اتصالJSArrayBufferحذف کند. این کار به مهاجم اجازه داد بایتهایی را تغییر دهد که Blink تصور میکرد منحصراً در اختیار اوست. - حمله TOCTOU: این نقص به صورت یک باگ «زمان بررسی تا زمان استفاده» (Time-of-Check to Time-of-Use) در رمزگشایی WebP متحرک ظاهر شد. اکسپلویت دو فایل WebP معتبر (الف و ب) را در یک فضای ذخیرهسازی قرار داد. فایل WebP الف یک بوم ۶۴x۶۴ را توصیف میکرد که اولین فریم آن ۴۸ پیکسل عرض داشت و از x=16 شروع میشد.
ImageDecoderهندسه فایل الف را کش کرد. سپس مهاجم بایتها را با WebP ب جایگزین کرد که دارای یک فریم کامل ۶۴x۶۴ بود. - سرریز: تابع
WEBPImageDecoder::DecodeSingleFrame()از مستطیل کششده فایل الف برای تعیین گام (stride) و اندازه بافر خروجی استفاده کرد. این تابع اشارهگر خروجی را با استفاده ازbuffer.GetAddr(frame_rect.x(), frame_rect.y())به پیکسل بالا-چپ پیش برد. برای یک بازتولیدکننده ASAN، گام بیتمپ ۲۵۶ بایت و تخصیص ۱۶,۳۸۴ بایت بود. تابعGetAddr(16, 0)اشارهگر را ۶۴ بایت جلو برد و ۱۶,۳۲۰ بایت باقی گذاشت — که دقیقاً برای مستطیل کششده الف (256 * (64 - 1) + 48 * 4) کافی بود. با این حال، محموله ۶۴ پیکسلی فایل ب از بررسی اندازهlibwebpعبور کرد (256 * (64 - 1) + 64 * 4 = 16,384) و باعث ایجاد یک نوشتن ۶۴ بایتی فراتر از تخصیص بومیSkBitmapشد.
برای تبدیل این سرریز Heap به یک نوشتن بومی (Native Write)، اکسپلویت از هندسهای استفاده کرد که برای PartitionAlloc انتخاب شده بود. یک فریم دو پیکسلی که از x=124 در یک بوم ۱۲۶ پیکسلی شروع میشد، یک تخصیص پیکسل ۵۰۴ بایتی و یک WebPIDecoder ۴۹۶ بایتی را در اسلاتهای مجاور باکت ۵۱۲ بایتی قرار داد. پیش بردن اشارهگر به اندازه ۴۹۶ بایت تنها هشت بایت باقی گذاشت و اجازه داد نوشتن پیکسل، فیلدهای بومی رمزگشا را با اشارهگرهایی به رکوردهای WebPDecBuffer که پیشتر در یک بافر شبکه بومی آماده شده بودند، جایگزین کند. این امر باعث شد WebPCopyDecBufferPixels() بتواند یک مقدار ۶۴ بیتی انتخابی را در یک آدرس بومی انتخابی کپی کند.
اصلاحیه مرحله دوم
اولین اصلاحیه مقادیر rgba ، stride و size را بهطور همزمان تنظیم کرد و محدوده اعلام شده را به مستطیل حفظ شده محدود نمود. اصلاحیه بعدی، فرض محاسبه آنچه تخصیص باید شامل شود را حذف کرد؛ اکنون رمزگشا گام و محدوده را مستقیماً از بیتمپ ImageFrame میگیرد. این موارد در تغییرات ۸۲۶۶۲۵۷ و ۸۳۵۲۸۹۹ کرومیوم و شماره پرونده ۵۴۸۵۰۹۱۵۳ مستند شدهاند.
تحویل به WASM/WCP
برای دستیابی به اجرای کد بدون تغییر در ورودیهای محافظتشدهی اشارهگر کد، تیم هدف را روی جدول Wasm Code Pointer در سطح فرآیند V8 گذاشت. آنها پایه قابل نوشتن (writable base) مورد استفاده برای مکانیابی جدول را تغییر دادند و یک ورودی جایگزین ارائه کردند.
آنها از تکنیک «جزیرهپریدن» (Island-hopping) با استفاده از ۱۳ مقدار ۶۴ بیتی در کدهای Wasm تولید شده توسط Liftoff و یک ABI لودر ویندوز استفاده کردند. ورودی جایگزین به دستور jmp rcx در chrome.dll اشاره میکرد، جایی که RCX به اولین جزیره دستورات اشاره داشت. هر یک از ۱۳ جزیره تا شش بایت از لودر را فراهم میکرد و از روی دستورات تولید شده توسط کامپایلر میپرید. این لودر ۷۰ بایتی حافظه قابل نوشتن تخصیص داد، محموله Crashpad را کپی کرد، نگاشت را به قابل اجرا تغییر داد و آن را اجرا کرد.
مرحله سوم: شکستن محیط ایزوله رندر
مرز نهایی، محیط ایزوله رندر بود که Crashpad (مدیریت خطای کروم) در خارج از آن عمل میکند. آسیبپذیری در ProcessMemoryWin::ReadUpTo() بود که خواندنهای ناموفق از یک فرآیند بهظاهر منجمد را مدیریت میکرد.
زمانی که یک خواندن با خطای ERROR_PARTIAL_COPY شکست میخورد، Crashpad فرض میکرد که درخواست از مرز یک صفحه عبور کرده است و سعی میکرد یک «خواندن کوتاه» از اولین صفحه انجام دهد. منطق برنامه short_read را به صورت base::GetPageSize() - (address & (base::GetPageSize() - 1)) محاسبه میکرد. این منطق فرض میکرد که فضای آدرس فرآیند معلق، پایدار است.
با این حال، اکسپلویت از پرچم ویندوزی THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE (پرچمهای 0x42، شامل SKIP_THREAD_ATTACH) استفاده کرد تا رشتهای ایجاد کند که در حین NtSuspendProcess() فعال بماند. این رشته معاف از انجماد، بهطور مکرر صفحات منبع را بین حالتهای PAGE_NOACCESS و PAGE_READWRITE تغییر میداد.
شرایط رقابتی (Race Condition) به این صورت عمل میکرد:
- Crashpad یک خواندن کوچک (0x0b8 بایت) در یک صفحه صادر کرد.
- صفحه در حالت
PAGE_NOACCESSبود و خطایERROR_PARTIAL_COPYبازگرداند. - Crashpad استنباط کرد که عبور از مرز صفحه رخ داده است.
- رشته رندر صفحه را قابل خواندن کرد.
- Crashpad خواندن را تا انتهای صفحه (0x168 بایت) مجدداً امتحان کرد و باعث سرریز در اسلات Heap مقصد 0x0c0 به اندازه 0x0a8 بایت شد.
بازنویسی Crashpad
این سرریز باعث تخریب یک شیء SnapshotMinidumpMemoryWriter شد. محموله بهگونهای ترتیب داده شد که یک اسلات نویسنده آزاد 0xc0 توسط بافر خواندن بازیافت شود و دقیقاً قبل از یک نویسنده فعال قرار گیرد. بازنویسی 0x0a8 بایتی، نویسنده فعال را تغییر داد:
- آفست 0x00: vtable مربوط به
SnapshotMinidumpMemoryWriterرا بازنویسی کرد. - آفست 0x70: وضعیت نویسنده را به قابل نوشتن تغییر داد.
- آفست 0x78: vtable مربوط به
MemorySnapshot::Delegateرا با یک vtable جابهجا شده ازCopyingOutputStreamAdaptorجایگزین کرد.
این کار باعث شد فراخوانی delegate به مسیر نوشتن بافر شدهی adaptor هدایت شود. سپس اکسپلویت از WriteObject() استفاده کرد تا یک محدوده 0xd0 بایتی کنترلشده را از رندر بخواند و به یک آدرس قابل نوشتن در chrome.exe کپی کند. این بایتها یک جریان خروجی تک-متدی و یک شیء fallback-launcher ساختند. هنگامی که adaptor دادهها را تخلیه (flush) کرد، تابع مجازی Write() جریان، وارد FallbackUnhandledExceptionFilter() شد که یک هدف معتبر برای CFG بود. این در نهایت FallbackCrashHandlerLauncher::LaunchAndWaitForHandler() را فراخوانی کرد و دستور cmd /k calc.exe را به CreateProcessW پاس داد.
از آنجایی که ASLR ویندوز ماژولها را یک بار در هر بوت تصادفی میکند (و نه برای هر فرآیند)، chrome.exe در هر دو محیط رندر و هندلر در یک آدرس نگاشت شده بود. این امر به محموله اجازه داد تا آدرسهای هندلر را از پایه تصویر خود استخراج کرده و ساختار را با استفاده از برچسب زمانی و اندازه تصویر تأیید کند.
اصلاحیه مرحله سوم
پیادهسازی اصلاحشده، فرض زمانی مبنی بر اینکه هدف بدون تغییر باقی میماند را حذف کرد. اکنون تلاش مجدد توسط هر دو مرز صفحه منبع و درخواست اصلی فراخوان با استفاده از std::min(size, page_remainder) محدود شده است. علاوه بر این، فراخوان عمومی در process_memory.cc اکنون هر نتیجه ReadUpTo() که بزرگتر از تعداد درخواستی باقیمانده باشد را رد میکند. این موارد در تغییر کرومیوم ۸۲۶۷۳۲۲ و شماره پرونده ۵۴۵۸۲۰۹۳۱ مستند شدهاند.
نقش ارکستراسیون هوش مصنوعی
تیم XBOW صرفاً از یک عامل نخواست که «یک اکسپلویت بنویسد». در عوض، آنها کار را به کمپینهای موازی تقسیم کردند: جستوجوی گسترده آسیبپذیری، تخریب حافظه V8، فرار از محیط ایزوله V8، فرار از محیط ایزوله رندر و در نهایت یکپارچهسازی. این رویکرد ارکستراسیون شباهت زیادی به روشهای پیشرفته دسترسی به وب توسط Scry دارد که در آن عاملهای هوش مصنوعی برای انجام تحقیقات پیچیده و خودکار به جای اسکرپینگهای سنتی به کار گرفته میشوند.
پژوهشگران انسانی در نقش معماران ظاهر شدند؛ آنها اهداف را انتخاب کردند، زمینه (Context) را فراهم کردند و نتایج AI را به چالش کشیدند. این رویکرد ماژولار اجازه داد تا تیم در صورت برخورد با «تداخل باگها» (Bug Collisions)، اجزا را تعویض کند. برای مثال، اولین نسخه از زنجیره از یک آسیبپذیری Gin برای فرار از V8 استفاده میکرد، اما این مورد تکراری با پرونده ۵۴۲۶۷۰۵۶۶ بود. تیم در عرض چند ساعت آن را با مرحله WebP جایگزین کرد و زنجیره را نجات داد.
تحلیل: استاندارد جدید برای هوش مصنوعی تهاجمی
این اتفاق سیگنالی است مبنی بر اینکه سد ورود برای توسعه اکسپلویتهای سطح بالا فرو ریخته است. انتقال از یک چرخه انسانی ۹ ماهه به یک چرخه ۶ روزه تقویتشده با AI نشان میدهد که «پنجره آسیبپذیری» برای Zero-dayها در حال کوچک شدن است، اما حجم باگهای کشف شده احتمالاً منفجر خواهد شد.
برای جامعه امنیتی، هشداردهندهترین جزئیات، نسبت هزینه به پاداش است. سرمایهگذاری ۴۰ هزار دلاری در اعتبارات API منجر به پاداش ۲۵۰ هزار دلاری و یک اکسپلویت Full-chain شد. این مدل اقتصادی، تحقیقات خودکار و عاملمحور آسیبپذیری را برای بازیگران پیچیده بسیار سودآور میکند.
علاوه بر این، این واقعیت که هر سه آسیبپذیری شامل تغییر چیزی بود که تصور میشد تغییرناپذیر است — یک HeapNumber ، یک بافر WebP منتقل شده و حفاظت صفحه یک فرآیند منجمد — نشان میدهد که LLMها بهویژه در یافتن «نقضهای ناپذیری» (Invariant Violations) مهارت دارند. آنها میتوانند دانش وسیعی از APIهای سیستمعامل و کدهای منبع را ترکیب کنند تا تنها مورد خاصی (Edge Case) را بیابند که در آن فرض اصلی سیستم شکست میخورد.
منتظر کنفرانس آتی «Unprompted» در پایان اکتبر باشید، جایی که XBOW ترتیب زمانی کامل و لاگهای ارکستراسیون این کمپین را افشا خواهد کرد.
گام بعدی شما
- بررسی مستندات VRP گوگل برای درک معیارهای جدید پاداشدهی به اکسپلویتهای Full-chain.
- مطالعه گزارشهای فنی Chromium در مورد اصلاحات مربوط به Maglev و Crashpad برای درک الگوهای نفوذ جدید.
- ارزیابی ابزارهای Red Teaming مبتنی بر عامل برای شناسایی سریعتر نقاط ضعف در زیرساختهای نرمافزاری.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell و تأثیر آنها بر سرعت استنتاج مدلهای امنیتی مراجعه کنید.




گفتگو