ریسک کانفیگهای CI: چطور یه regex در فایل YAML میتونه سرور رو بندازه
نویسنده: آرش لطیفی
باگ CVE-2026-97229 از داخل کانفیگ CI اکسپلویت شد. نگاهی فنی به مکانیزم حمله، نقش Onigmo، و اینکه چطور CI خودتون رو در برابر ورودیهای مخرب مقاوم کنید.
TL;DR — حمله اخیر به GitLab Alpine از داخل فایل کانفیگ CI شروع شد: یه regex دستکاریشده باعث integer overflow در موتور regex روبی شد و به RCE رسید. اگه CI شما ورودی کاربر رو کامپایل میکنه، این پست مال شماست.
زنجیره حمله، قدم به قدم
بر اساس گزارش Alpine و اطلاعیه رسمی Ruby، ماجرای CVE-2026-97229 این شکلی بود:
- GitLab توی کانفیگ CI اجازه میده کاربرها regex تعریف کنن (مثلاً برای
rules:changesیا policyها). این regexها باRegexp.newکامپایل میشن — یعنی ورودی کاربر مستقیم به کامپایلر regex میره. - کامپایلر regex روبی (Onigmo، همون موتور قدیمی Oniguruma) روی پترنهای خاص با integer overflow کرش میکرد. در نتیجه دو باگ جدا (
CVE-2026-93577وCVE-2026-97229) به RCE منجر شد، بهعلاوه یه double free جداگانه (CVE-2026-89078). - مهاجم بدون احراز هویت، کانفیگ CI دستکاریشده فرستاد. هر بار کامپایل regex کرش میکرد و پروسه worker میمرد. Alpine ۸ کرش ثبت کرد که ۴ بارش اکسپلویت تا وسط راه رفته بود.
نکته فنی مهم: مطابق بحث HN، اکسپلویت فقط به overflow وابسته نبود — ترکیب «regexهایی که بایتکد خیلی بلند تولید میکنن + integer overflow + double free» کنار هم زنجیره رو کامل میکرد. یعنی رفع یه باگ تنهایی کافی نبود، و دقیقاً به همین دلیل Ruby هر چهار باگ رو یکجا در 3.3.13 / 3.4.12 / 4.0.9 بست.
چرا فیکس GitLab کافی نبود؟
GitLab فیکس رو روز ۱۲ اکتبر منتشر کرد (نسخههای 19.4.2، 19.3.5، 19.2.8) — ولی فیکس یه پچ روی Ruby باندلشده بود، نه تغییر در کد GitLab. توزیعهای دیگه و کسانی که ایمیج خودشون رو میسازن، از استاندارد Ruby استفاده میکردن و فیکس رو نداشتن. Alpine روز ۲۶ اکتبر فهمید آپگریدشون به 19.3.6 هنوز آسیبپذیره.
تست سریع روی سرورتون:
اگه GitLab رو با Ruby خودتون اجرا میکنید (سورس یا ایمیج شخصی)، آپگرید GitLab رو به تنهایی حساب باز نکنید:
درس کلی: ورودی کاربر + کامپایل = منطقه خطر
مشکل GitLab خاص نبود. هر جا ورودی غیرقابل اعتماد مستقیم به یه کامپایلر میره، همین الگو تکرار میشه. چند قاعده عملی:
| قاعده | توضیح |
|---|---|
| کامپایل دیرهنگام نکنید | regex کاربر رو موقع ذخیره کامپایل و reject کنید، نه موقع اجرای هر pipeline |
| روی زبان runtime حساب باز نکنید | فیکس ممکنه در زبان زیرین باشه (اینجا Ruby)، نه کد اپلیکیشن شما |
| تاگ شناور ممنوع | ruby:3.4 و latest ممکنه ماهها نسخه آسیبپذیر بمونن؛ نسخه دقیق پین کنید |
| کرشهای worker رو لاگ کنید | ۸ کرش پشتسرهم با پترن مشابه، اولین سیگنال حمله در Alpine بود |
| CI رو ایزوله کنید | runnerها و workerهای parseکننده کانفیگ، حداقل privilege ممکن رو داشته باشن |
چکلیست سختگیرسازی CI
- [ ] ورودیهای regex در کانفیگ CI رو در زمان ذخیره با تست درونی reject کنید.
- [ ] نسخههای runtime (Ruby/Node/Python) در ایمیجهای CI رو پین کنید و یه job هفتگی برای bump خودکار بذارید.
- [ ] لاگهای worker رو برای کرشهای تکراری با ورودی مشابه مانیتور کنید.
- [ ] feed امنیتی vendor رو subscribe کنید — Alpine یه هفته بعد از فیکس حمله خورد.
- [ ] برنامه «تشخیص داده نمیشود → بازسازی» رو از قبل بنویسید؛ Alpine چون این رو تمرین کرده بود، سریع عمل کرد.
FAQ
Q: ما CI نداریم، ولی nginx/Node/PHP داریم — چی کار کنیم؟
همون اصل رو پیاده کنید: بفهمید فیکسهای امنیتی در کد خود اپ میاد یا در runtime زیرین. حملههای اخیر (PHP2Shell، همین باگ Onigmo) نشون دادن لایه زیرین هدف اصلیه. ورژن runtime رو مثل یه وابستگی جدی مدیریت کنید.
Q: چطور بفهمم regexهای کاربرم خطرناکن؟
هر جا new RegExp(userInput) معادلش (توی هر زبانی) دارید، ریسک هست — حتی بدون RCE، ReDoS با پترنهای تو در تو رایجه. حداقل: طول محدود کنید، از محدودیت step/time استفاده کنید، یا مجموعه پترنهای مجاز رو whitelist کنید.
Q: فیکس GitLab ۱۹.۴.۲ رو نصب کردیم، امن شدیم؟
اگه از پکیج یا ایمیج رسمی GitLab استفاده میکنید و تازه pull کردید، بله — پچ Ruby داخلشه. اگر Ruby جدا دارید، حتماً نسخه embedded/bin/ruby یا Ruby سیستمتون رو چک کنید: حداقل 3.3.13 / 3.4.12 / 4.0.9.