۴۸ دقیقه تا root
آنتروپیک یه باگ با اسکور 9.8 داخل کرنل لینوکس پیدا کرده که میشه باهاش بدون هیچ لاگین و به صورت ریموت، وسط هندشیک TCP دسترسی root گرفت.

آنتروپیک یه باگ با اسکور 9.8 داخل کرنل لینوکس پیدا کرده که میشه باهاش بدون هیچ لاگین و به صورت ریموت، دسترسی root گرفت و این باگ از سال 2016 داخل کرنل لینوکس بوده و در این لحظه که من اینو مینویسم، هنوز پچ نشده!
حالا سوال اینه که چطور میشه فقط با باز کردن یه کانکشن TCP و بدون اینکه هیچ اپلیکیشنی اون کانکشن رو ببینه، یه سرور لینوکسی رو زمین زد ؟
منو شما وقتی به امنیت سرور فکر میکنیم یه تصویر ساده تو ذهنمون میاد که پسورد قوی بذار و SSH رو با کلید ببند و حواست باشه چیزی که رو سروره خودش آسیب پذیر نباشه و این حرفا ولی این باگ اصلا به این چیزا کاری نداره.
این باگ قبل از هر چیزی، داخل خود کرنل و درست وسط هندشیک اولیه TCP اتفاق میفته.
یعنی هنوز nginx یا sshd یا دیتابیست اصلا خبر نداره که یکی داره وصل میشه و هکر داخل همون هندشیک، دسترسی رو میگیره و تامام!
یه بدبختی دیگه هم اینه حالا کرنل که پچ میشه ولی بازم سرور شما کرنل upstream رو اجرا نمیکنه بلکه کرنل توزیعت رو اجرا میکنه و تا وقتی reboot نکنی کرنل جدید هم اصلا بالا نمیاد و در نتیجه، شما هنوز سوراخی و حالا اینو مفصل بازش میکنم جلوتر.
داستان چیه؟
خلاصه ماجرا اینه که وقتی یه کلاینت میخواد به سرور لینوکسی وصل شه، ممکنه به پکت هاش یه سری اطلاعات اضافه بچسبونه به اسم IP options. کرنل وسط handshake این اطلاعات رو نگه میداره تا تو جوابش برگردونه.
کرنل این اطلاعات رو بدون اینکه ازش کپی بگیره دست به دست میکنه و بعد بلافاصله پاکش میکنه در حالی که ممکنه یه CPU دیگه هنوز داشته میخوندتش و به این قضیه میگن use-after-free.
یعنی قبل ازینکه شما چیزیو پاک کنی باید مطمئن شی که همه کارشون باهاش تموم شده.
پکت IPv4 از نزدیک چه شکلیه؟
من یه رایت آپی نوشتم به اسم هفت خان رستم و اونجا ارژنگ میخواست برای جمشید ایمیل بفرسته و دیتا تیکه تیکه میشد و تبدیل به پکت میشد و این داستانا، حالا بهتره که خودت بری اونو بخونی الان چون کوتاهم هست، چون اینجا هم همون دو تا رفیقمون هستن و فرض بر اینکه ارژنگ یه کلاینته که میخواد به سرور جمشید وصل بشه.
هر پکت IPv4 که ارژنگ میفرسته، اولش یه header داره و این هدر مثل پاکت نامه ست و آدرس فرستنده، آدرس گیرنده، و یه سری اطلاعات دیگه که روترها و سیستم عامل برای رسوندن پکت لازم دارن داخل این هدره هست ( اگه دوست داری کل فیلدهاش رو ببینی، RFC 791 همونجاییه که IPv4 تعریف شده ).
حداقل اندازه این هدر ۲۰ بایته و تو همین هدر یه فیلد کوچیک هست به اسم IHL یا همون Internet Header Length که میگه هدر چقدر طولشه و واحدش هم بایت نیست بلکه تیکه های ۴ بایتیه. پس:
- اگه
IHLبرابر ۵ باشه، یعنی5 × 4 = 20بایت، یعنی هدر ساده و بدون هیچ چیز اضافه. - اگه از ۵ بیشتر باشه ( تا حداکثر ۱۵، یعنی ۶۰ بایت )، یعنی یه چیزی به ته هدر اضافه شده.
اون "چیز اضافه" همون IP options هست.
بیا یه نگاه به خود هدر بندازیم:
حالا IP options چی هستن؟ فرض کن ارژنگ رو پاکت نامه اش یه یادداشت منگنه کنه که روش نوشته "هر شهری که ازش رد شدی اسمش رو اینجا بنویس" و به این میگن Record Route، یعنی هر روتری که پکت ازش رد میشه آدرس خودش رو تو این آپشن مینویسه و یا یه یادداشت دیگه که میگه "ساعتش رو هم بزن"، که بهش میگن Timestamp.
حالا نکته اینجاس که ترافیک عادی TCP تقریبا هیچوقت IP options نداره و این آپشن ها یه سری ابزار قدیمی دیباگ شبکه ان و تو دنیای واقعی کسی باهاشون صفحه وب باز نمیکنه!
این نکته رو یادت بمونه، چون بعدا یکی از راه های مهار کردن این باگ دقیقا روی همین بنا شده.
کرنل وسط handshake چیکار میکنه؟
من three way handshake رو تو هفت خان رستم توضیح دادم: ارژنگ یه SYN میفرسته که "حاجی بیا حرف بزنیم"، جمشید یه SYN-ACK برمیگردونه که "اوکیه بیا"، و ارژنگ با یه ACK کار رو تموم میکنه.
بازم میگم اول برو اون رایت آپو بخون و من اینجا فقط میخوام ببینیم پشت صحنه تو کرنل جمشید چه خبره.
خب سرور جمشید چند تا CPU داره و کرنل لینوکس پکت های شبکه رو همزمان روی همه این CPU ها پردازش میکنه.
یعنی ممکنه دو تا کار که به یه کانکشن مربوطن، دقیقا همزمان روی دو تا CPU متفاوت انجام بشن و کل این باگ سر همینه.
حالا وقتی SYN ارژنگ به پورتی میرسه که جمشید روش listen کرده، کرنل هنوز نمیدونه این کانکشن واقعا کامل میشه یا نه چون ممکنه ارژنگ هیچوقت ACK آخر رو نفرسته و برای همین هنوز یه سوکت کامل نمیسازه چون سوکت کامل یه ساختار سنگینی هست و اگه برای هر SYN یکی ساخته بشه، با یه سیل SYN مموری سرور پر میشه پس به جاش یه ساختار سبک به اسم request_sock یا همون request socket میسازه ( اگه نمیدونی سوکت چیه، تو سرور، کلاینت و پَکِت راجع بهش نوشتم ) که فقط چیزای لازم برای تموم کردن هندشیک رو نگه میداره که میشه آدرس و پورت دو طرف، sequence number اولیه و آپشن های TCP ای که سرشون توافق شده.
این request socket میره تو صف کانکشن های نیمه باز همون listener.
اگه هدر IPv4 پکت SYN ارژنگ IP options داشته باشه، کرنل اونا رو parse میکنه، یه نسخه ازشون رو تو یه تیکه مموری جدا روی heap کرنل نگه میداره و آدرسش رو تو فیلد ireq_opt همون request socket میذاره.
چرا؟
چون جوابی که از روی این request socket ساخته میشه ( همون SYN-ACK، یا ارسال دوباره اش اگه گم شد ) باید این آپشن ها رو تو هدر خودش برگردونه.
بعد وقتی ACK آخر میرسه و handshake کامل میشه، کرنل از روی request socket یه سوکت کامل یا همون full socket میسازه و آپشن ها هم منتقل میشن به فیلد inet_opt این سوکت جدید. اسم این دو تا فیلد رو هم باید یادت بمونه چون عنوان کامیت پچ دقیقا همیناس: "free inet_opt and ireq_opt after an RCU grace period".
سوکت کامل میره تو صف accept، و تازه از اینجا به بعده که اپلیکیشن با صدا زدن accept() میتونه کانکشن رو تحویل بگیره.
کل این مسیر رو اینجا ببین:
حواست باشه که همه این کارا هنوز داخل کرنله و هنوز هیچ اپلیکیشنی هیچ کدی اجرا نکرده!
حافظه، free و use-after-free
قبل از اینکه بریم سراغ باگ، باید بدونیم use-after-free یعنی چی.
هر برنامه ای ( حتی خود کرنل ) وقتی یه دیتایی رو نگه میداره، یه تیکه از مموری رو برای اون دیتا میگیره و به این کار میگن allocate و وقتی کارش با اون دیتا تموم شد، اون تیکه مموری رو پس میده که دوباره قابل استفاده بشه که بهش میگن free.
به اونجایی که این تیکه های مموری ازش گرفته و بهش پس داده میشه هم میگن heap.
حالا نکته اینه که مموری ای که free شده خالی و بی صاحاب نمیمونه و خیلی زود یکی دیگه برمیداره و دیتای خودش رو توش میذاره.
use-after-free یعنی یه تیکه کد هنوز داره از مموریی استفاده میکنه که دیگه مال اون نیست.
حالا ببینیم تو کرنل دقیقا چی میشه.
وقتی کرنل یه تیکه مموری رو با kfree() آزاد میکنه و اون تیکه برمیگرده به allocator کرنل و دفعه بعد که کرنل یه آبجکت هم اندازه لازم داشته باشه، ممکنه خیلی زود همون آدرس مموری رو بهش بده ولی هر پوینتری که قبلا به اون آدرس از مموری اشاره میکرده، هنوز همون آدرس رو نگه داشته چون free کردن مموری پوینترها رو پاک نمیکنه.
به چنین پوینتری میگن dangling pointer.
حالا فرض کن CPU شماره ۱ آپشن های ارژنگ رو free کرده و CPU شماره ۲ هنوز یه پوینتر اشتباه داره و داره از روی اون پوینتر، هدر جواب هندشیک رو میسازه و اگه بین این دو تا لحظه کرنل اون آدرس رو به یه آبجکت دیگه داده باشه، CPU شماره ۲ بدون اینکه بفهمه داره با دیتای اون آبجکت جدید کار میکنه و اینجا سه حالت پیش میاد:
- محتوای اون آبجکت جدید به جای آپشن ها کپی میشه تو هدر جوابی که برای ارژنگ فرستاده میشه، یعنی محتوای
heapکرنل از طریق شبکه نشت میکنه بیرون. - کرنل بر اساس دیتای کهنه ( مثلا طول آپشن ها ) تصمیم میگیره کجا و چقدر بنویسه و ممکنه بیرون از محدوده درست بنویسه و مموری یه چیز دیگه رو خراب کنه که بهش میگن
memory corruption. - دیتای بی معنی ای میخونه که کرنل نمیتونه باهاش کنار بیاد و کرنل پنیک میکنه و کل سیستم کرش میکنه.
اینا دقیقا همون سه تا پیامدی هستن که خود کرنل تو توضیحات اسکورش برای این باگ نوشته، یعنی نشت محتوای heap کرنل، memory corruption یا کرش کردن سیستم.
حالا چرا نشت مموری کرنل اینقدر مهمه؟ چون مموری کرنل پر از چیزای حساسه از جمله آدرس هایی که یه اکسپلویت برای کار کردن لازم داره و چرا خراب کردن مموری کرنل خطرناکه؟
چون اگه آقا دزده بتونه مموری رو کنترل کنه، عملا میتونه ساختار داخلی کرنل رو به نفع خودش عوض کنه و اونجاس که بجای پنیک، میرسیم به دسترسی root ! 😛
ریس کاندیشن؟
خب حالا خود باگ.
فهمیدیم که کرنل اون آپشن های ذخیره شده رو بدون اینکه کپی بگیره از request socket میداد به کانکشن جدید، پس هر دوی اینا دقیقا به یه تیکه مموری اشاره میکردن و بعد وقتی کانکشن از بین میرفت، کرنل این آپشن ها رو بلافاصله free میکرد در حالی که یه CPU دیگه ممکن بود همون لحظه داشته اون تیکه مموری رو میخونده تا بره جواب هندشیک رو بسازه.
یعنی یه همچین وضعی داریم:
اگه بخوام دقیق تر بگم، طبق توضیح خود رکورد CVE ماجرا اینه که وقتی ACK آخر میرسه، تابع tcp_v4_syn_recv_sock() آپشن ها رو بدون کپی از ireq_opt میده به inet_opt سوکت جدید، ولی همزمان یه CPU دیگه ممکنه داره یه SYN تکراری برای همون request socket رو پردازش میکنه و برای جوابش یه SYN-ACK میسازه که اون هم همین آپشن ها رو میخونه. حالا اگه سوکت جدید وسط این کار بسته بشه، کرنل inet_opt اش رو با یه kfree() ساده آزاد میکنه و اون CPU دوم میمونه با یه پوینتر به مموری آزاد شده.
این عکسو با دقت نگاه کن، زمان از بالا به پایین جلو میره:
جالبه که خود رکورد میگه از وقتی request socket ها بدون قفل کردن listener پردازش میشن، هیچی جلوی این همزمانی رو نمیگرفته و این تغییر همونیه که باگ رو از کرنل 4.4 به بعد آورده.
اینجا دو تا CPU دقیقا همزمان با یه تیکه مموری کار میکنن و یکی داره ازش میخونه و یکی داره free اش میکنه و نتیجه بستگی داره به اینکه کدومشون زودتر کارشو انجام بده و ما میدونیم که به این وضعیت میگن race condition و اگه CPU که خواننده هست ( لفظ دیگه ای پیدا نکردم منظورم همون CPU که داره مموری رو میخونه ) زودتر کارش تموم شه، هیچ اتفاقی نمیفته و دردسری نداریم ولی اگه free زودتر انجام شه و اون مموری به یه آبجکت دیگه داده بشه، آسیب پذیری فعال میشه و برای همین این باگ همیشه پیش نمیاد و مستر ربات باید زمان بندی رو درست دربیاره.
ولی یه چیز این باگ رو خیلی بدتر میکنه و آنتروپیک میگه اگه race condition رو ببازی، فقط همون handshake شکست میخوره و کل سیستم کرش نمیکنه.
یعنی هکر میتونه هی و هی و هی امتحان کنه و هیچکس هم متوجه نشه، تا بالاخره به نتیجه برسه! 🌚
پچ: kfree در برابر kfree_rcu
پچ رو با این عنوان نوشتن:
ipv4: free inet_opt and ireq_opt after an RCU grace period
کل تغییرش تو دو تا فایله ( net/ipv4/af_inet.c و net/ipv4/tcp_ipv4.c ) و در کل دو خط حذف شده و دو خط جاش اومده: دو تا kfree() ساده تبدیل شدن به kfree_rcu().
خود پچ رو میتونی اینجا رو گیتهاب ببینی، کوتاهه و ارزش یه نگاه رو داره.
خب این یعنی چی؟
شما با kfree() میتونی در لحظه مموری رو آزاد کنی و حتی اگه یه CPU دیگه هنوز یه پوینتر بهش داشته باشه هم مهم نیست و مموری آزاد میشه.
ازونورkfree_rcu() یعنی فقط وقتی مموری رو آزاد که دیگه کسی با اون تیکه از مموری کاری نداره و همه کارشون تموم شده.
این RCU مخفف Read-Copy-Update هست و یه مکانیزم معروف تو کرنل لینوکسه و ایده اش اینه که خواننده ها بتونن خیلی سریع و بدون قفل کردن، دیتا رو بخونن، و کسی که میخواد چیزی رو پاک کنه به جای اینکه فورا پاکش کنه، صبر کنه تا یه بازه زمانی به اسم grace period تموم شه و وقتی grace period تموم شد، کرنل مطمئنه که هیچ CPU ای دیگه دستش به اون مموری بند نیست و میشه با خیال راحت آزادش کرد.
فرقشون رو اینجا ببین:
پس دیگه هیچ CPU ای با یه dangling pointer به مموری آزاد شده نمیرسه و تامام!
اسکور 9.8 یعنی چی؟
اسکور CVSS یه عدد از ۰ تا ۱۰ هست که میگه یه باگ چقدر خطرناکه، و پشتش یه بردار هست که میگه این عدد از کجا اومده.
برای این باگ، خود کرنل این بردار رو داده:
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
AV:Nیا همونAttack Vector: Network: از روی شبکه میشه بهش رسید، لازم نیست تو حلق سرور نشسته باشی.AC:Lیا همونAttack Complexity: Low: پیش شرط خاص و عجیبی نداره.PR:Nیا همونPrivileges Required: None: هیچ دسترسی و لاگینی لازم نیست.UI:Nیا همونUser Interaction: None: لازم نیست کسی رو جایی کلیک کنه.S:Uیا همونScope: Unchanged: اثرش تو همون سیستمی که آسیب پذیره میمونه.C:H/I:H/A:H: تاثیرش روی محرمانگی (Confidentiality)، سالم موندن دیتا (Integrity) و در دسترس بودن (Availability) هر سه تا "زیاد" هست.
جمعش میشه 9.8 و سطح Critical.
حالا همه هم با این عدد موافق نیستن. رد هت اول بهش 8.1 داده بود و بعد ۳۰ اکتبر بردش رو 9.8، با این دلیل که این باگ پیش شرط شبکه ای نداره و میتونه سرور رو کامل بزنه زمین و اراکل و آمازون هنوز روی 8.1 و سطح Important موندن ولی خب 8.1 هم عدد کمی نیست.
MPTCP و Go این وسط چیکاره ان؟
داخل رایت آپ های مختلفی که خوندم اسم MPTCP کنار این باگ میاد و گفتم که اونم اینجا بازش کنم.
MPTCP یا همون Multipath TCP یه نسخه از TCP هست که اجازه میده یه کانکشن همزمان از چند تا مسیر استفاده کنه مثلا گوشیت هم از وای فای و هم از دیتای موبایل استفاده کنه و اپلیکیشن باید خودش صریحا یه سوکت MPTCP باز کنه تا بعدش سرور یه MPTCP listener داشته باشه.
حالا ارتباطش با این باگ چیه؟
آنتروپیک میگه اکسپلویتی که کلاد ساخته روی "Linux servers with a multipath TCP listener" کار میکنه ولی نگفته چرا این listener لازمه و نه خود رکورد CVE اسمی از MPTCP آورده.
ماجرا وقتی جدی تر شد که تیم امنیت Go اعلام کرد از Go 1.24 به بعد، برنامه های Go روی لینوکس به طور پیش فرض برای listen کردن از MPTCP استفاده میکنن یعنی ممکنه یه سرور Go ساده داشته باشی و اصلا ندونی که MPTCP listener داری و این پیش فرض از خط go توی فایل go.mod برنامه میاد، و فقط وقتی فعاله که کرنل اجازه MPTCP رو بده.
ولی یه نکته خیلی خیلی مهم: خاموش کردن MPTCP باگ رو درست نمیکنه!
خود باگ تو TCP ساده IPv4 هست، و تیم Go هم صریحا گفته ممکنه سیستم هایی که هیچ MPTCP listener ای ندارن هم به حمله های مشابه آسیب پذیر باشن.
MPTCP صرفا همون چیزیه که اکسپلویت آنتروپیک لازم داشته.
کیا آسیب پذیرن؟
هر کرنل لینوکسی از نسخه 4.4 به بعد که پچ رو نگرفته باشه و 4.4 اوایل ۲۰۱۶ اومده، یعنی تقریبا همه سرورای لینوکسی دنیا آسیب پذیرن!
و چون این باگ تو مسیریه که هر بار یه کانکشن IPv4 TCP قبول میشه اجرا میشه، عملا هر سروری که پورت TCP باز داره درگیره یعنی وب سرور، SSH، دیتابیس، load balancer، نود های Kubernetes و این داستانا.
آپستریم در برابر کرنل توزیع
کرنل آپستریم همون کرنل رسمی لینوکسه که رو kernel.org منتشر میشه و پچ این باگ تو این نسخه ها اومده:
5.10.272، 5.15.223، 6.1.190، 6.6.159، 6.12.113، 6.18.56، 7.2.10 و 7.3
ولی تقریبا هیچکس روی سرورش مستقیم کرنل آپستریم اجرا نمیکنه و هر توزیع ( اوبونتو، دبیان، RHEL و ... ) کرنل خودش رو با شماره نسخه خودش داره و پچ ها رو از آپستریم برمیداره و میذاره رو کرنل خودش و به این کار میگن backport. یعنی اگه ببینی کرنلت مثلا 5.14.0-... هست، از روی شماره نسخه آپستریم نمیتونی بفهمی پچ رو داره یا نه، باید ترکر امنیتی توزیعت رو نگاه کنی.
یعنی مسیر پچ تا سرور شما اینجوریه:
وضعیت توزیع ها تا ظهر ۲ نوامبر این بود و اسم هر توزیع لینک ترکر امنیتی خودشه:
- دبیان: نسخه ۱۲ (
bookworm)، ۱۳ (trixie) وunstableپچ شدن. - رد هت: اول نوامبر کرنل پچ شده برای
RHEL۸، ۹ و ۱۰ رو داد ولیRHEL CoreOSتویOpenShiftهنوز آسیب پذیر لیست شده. - آلمالینوکس: ۲ نوامبر کرنل های پچ شده ۸، ۹ و ۱۰ رو برد رو مخزن اصلیش که همون نسخه های
RHELهستن. - سوزه:
SLES 16.0، کرنل هایLTSSنسخه ۱۵ وopenSUSE Leap 16.0پچ شدن و15 SP7در حال انجامه. - اراکل لینوکس: کرنل های
UEKاز ۱۶ اکتبر پچ شدن. - آمازون لینوکس: همه کرنل های
Amazon Linux 2و2023پچ شدن. - اوبونتو: هنوز همه نسخه ها،
LTSها هم آسیب پذیر هستن و رو بعضی نسخه ها نوشته "work in progress" ولی هنوز پچی منتشر نشده.
این وضعیت مال ۲ نوامبره و احتمالا تا وقتی اینو میخونی عوض شده، پس حتما ترکر توزیع خودت رو چک کن.
چطوری سرور خودم رو چک کنم؟
اول ببین چه کرنلی داری و کرنل جدید نصب شده یا نه:
uname -r # the running kernel
uname -v # Debian: the package version running
dpkg -l 'linux-image*' | grep ^ii # Debian/Ubuntu: installed kernel packages
rpm -q kernel # RHEL and derivatives: installed kernels
needs-restarting -r # RHEL (dnf-utils): is a reboot needed?
خط به خط:
uname -rمیگه همین الان چه کرنلی در حال اجرا هست.uname -vرو دبیان نسخه پکیج کرنل در حال اجرا رو هم نشون میده.dpkg -l 'linux-image*'رو دبیان و اوبونتو لیست کرنل هایی که نصب شدن رو میده.rpm -q kernelهمین کار رو برایRHELو هم خانواده هاش میکنه.needs-restarting -rروRHELبهت میگه لازمه ریبوت کنی یا نه.
نکته اینجاس که اگه کرنل نصب شده از کرنل در حال اجرا جدیدتره، یعنی پچ رو دانلود کردی ولی هنوز فعال نیست و تا وقتی ریبوت نکنی، همون کرنل آسیب پذیر داره کار میکنه.
یه نکته دیگه اینه که کامندها رو رو خود هاست و نود های کوبرنتیز اجرا کن و به کانتینر اعتبار نیست چون کانتینر همون کرنل هاست رو گزارش میده و ممکنه گولت بزنه.
بعد ببین MPTCP listener داری یا نه:
ss -Mlnp # MPTCP listening sockets and the processes that own them
sysctl net.mptcp.enabled # 1 means applications may open MPTCP sockets
ss -Mlnp فقط سوکت های MPTCP ای که دارن listen میکنن رو به همراه پروسه صاحبشون نشون میده.
اگه خروجیش خالی بود یعنی MPTCP listener نداری.
خط دوم هم میگه کرنلت اجازه MPTCP رو میده یا نه ولی حواست باشه که 1 بودنش به تنهایی یعنی "اجازه هست"، نه اینکه حتما یه listener داری.
اگه هنوز نمیتونم آپدیت کنم چی؟
راه درست فقط یکیه: کرنل پچ شده توزیعت رو نصب کن و ریبوت کن ولی اگه مثل اوبونتو هنوز پچ نیومده یا نمیتونی همین الان ریبوت کنی، این چند تا راه موقتی هست.
دراپ کردن پکت های TCP با IP options
یادته گفتم ترافیک عادی TCP تقریبا هیچوقت IP options نداره؟ خب این همینجا به کارمون میاد چون هکر برای این حمله باید تو SYN خودش آپشن هایی مثل Record Route یا Timestamp بذاره پس اگه پکت های TCP ای که IP options دارن رو قبل از رسیدن به کرنل، دراپ کنی، عملا در رو بستی.
رد هت و آلمالینوکس هر دو یه رول nftables برای این کار منتشر کردن و این نسخه ایه که پکت رو تو prerouting میگیره:
nft add table ip cve_2026_98432
nft add chain ip cve_2026_98432 pre '{ type filter hook prerouting priority -300; }'
nft add rule ip cve_2026_98432 pre meta l4proto tcp ip hdrlength gt 5 counter drop
خط به خط:
۱. یه جدول جدید به اسم cve_2026_98432 برای ترافیک IPv4 میسازه.
۲. یه زنجیره به اسم pre میسازه که به prerouting وصله، یعنی خیلی زود و قبل از اینکه کرنل تصمیم بگیره پکت مال کیه، و priority -300 هم باعث میشه این رول حتی قبل از connection tracking اجرا بشه.
۳. خود رول: اگه پکت TCP بود ( meta l4proto tcp ) و ip hdrlength ازش بیشتر از ۵ بود، بشمارش ( counter ) و بندازش دور ( drop ).
اون ip hdrlength gt 5 همون فیلد IHL هست که اون اول دیدیم و بیشتر از ۵ یعنی هدر بیشتر از ۲۰ بایته و تنها چیزی که هدر رو بلندتر میکنه IP options هست. دیدی چطور همه چی به هم وصل شد؟ :))
این رول به IPv6 و ترافیک غیر TCP دست نمیزنه و تا ریبوت بعدی میمونه. اگه خواستی برش داری:
nft delete table ip cve_2026_98432
اگه فایروالت رو با iptables مدیریت میکنی، این رول همین کار رو از جدول raw میکنه:
iptables -t raw -I PREROUTING -p tcp -m u32 --u32 "0>>24&0xF=6:15" -j DROP
اینجا ماژول u32 میاد ۴ بیت IHL رو از هدر میخونه ( 0>>24&0xF ) و اگه عددش بین ۶ تا ۱۵ بود، یعنی هدر آپشن داره و پکت رو دراپ میکنه. برای برداشتنش همین کامند رو با -D به جای -I اجرا کن و فقط حواست باشه که این رول به ماژول xt_u32 کرنل نیاز داره که رو دبیان ۱۲، اوبونتو 22.04 و 24.04 و RHEL 8 هست ولی رو RHEL 9 و 10 نیست، پس اونجا از همون رول nft استفاده کن.
حالا محدودیت هاش رو هم باید بدونی، چون این رول ها کرنل رو درست نمیکنن:
- خود رد هت گفته رولش "reduces exposure to this vulnerability but cannot fully prevent it"، یعنی ریسک رو کم میکنه ولی صفرش نمیکنه.
- ترافیکی که بین کانتینرهای روی یه هاست
bridgeمیشه ممکنه اصلا از این رول رد نشه و آلمالینوکس هم صریحا روی همین هشدار داده. - اگه شبکه ات به
CIPSO( یه سری برچسب امنیتی که داخل همینIP optionsجا به جا میشن ) وابسته ست، این رول رو استفاده نکن. - اگه فایروال اج شبکه ات میتونه پکت های
IPv4با آپشن رو بندازه، اونجا انجامش بدی همه سرورای پشتش محافظت میشن.
خاموش کردن MPTCP
اگه از MPTCP استفاده نمیکنی، خاموشش کن. همونطور که گفتم این باگ رو درست نمیکنه ولی همون پیش شرطی که اکسپلویت آنتروپیک لازم داشت رو از بین میبره و آلمالینوکس این مراحل رو پیشنهاد داده:
echo 'net.mptcp.enabled = 0' | sudo tee /etc/sysctl.d/90-disable-mptcp.conf
sudo sysctl -w net.mptcp.enabled=0
sudo ss -Mlnp # listeners opened earlier stay MPTCP: restart their services
خط اول تنظیم رو تو یه فایل مینویسه که بعد از ریبوت هم بمونه، خط دوم همین الان اعمالش میکنه، و خط سوم چک میکنه که listener قدیمی ای مونده یا نه. همونطور که کامنتش میگه، listener هایی که قبلا باز شدن همچنان MPTCP میمونن و باید سرویسشون رو restart کنی.
دو تا نکته ریز:
- این تنظیم برای هر
network namespaceجداست و هرnamespaceجدید باMPTCPروشن شروع میشه. یعنی کانتینرها روشن نگهش میدارن مگه اینکه اونجا هم خاموشش کنی ( توDockerبا--sysctl net.mptcp.enabled=0). - برای یه برنامه
Goتنها، میتونیGODEBUG=multipathtcp=0رو توenvironmentاش بذاری، ولی تیمGoخودش به این روش برنامه به برنامه گفته "a fragile mitigation" ( مستنداتGODEBUG).
و تا وقتی پچ نکردی سرورایی که MPTCP listener دارن رو از برق بکش! 🫠
از پچ تا اکسپلویت چقدر طول کشید؟
برای اینکه بدونی این ماجرا چقدر سریع پیش رفت:
- ۱ اکتبر: پچ فرستاده میشه و باگ رو یکی از محقق های آنتروپیک گزارش داده.
- ۱۱ و ۱۵ اکتبر: نسخه های
stableکرنل با پچ میان و ۱۵ اکتبر رکوردCVEهم منتشر میشه. - ۲۷ اکتبر: آنتروپیک توضیح میده که کلاد براش یه اکسپلویت
remote rootساخته. - ۲۹ اکتبر: تیم امنیت
Goاعلام میکنه برنامه هایGoاز1.24به طور پیش فرضMPTCP listenerباز میکنن. - ۳۰ اکتبر: رد هت اسکور رو میبره رو
9.8و رول مهارش رو منتشر میکنه. - ۱ و ۲ نوامبر: رد هت و بعدش آلمالینوکس کرنل پچ شده میدن و اوبونتو هنوز نه.
حالا قسمت آنتروپیک. اونا میگن کلاد این اکسپلویت رو "after initial prompting" نوشته و روی سرورایی که MPTCP listener دارن بدون هیچ کردنشال یا دخالت کاربری دستور با دسترسی root اجرا میکنه و تو proof of concept اشون ۴۸ دقیقه طول میکشه تا یه kernel pointer نشت بده و ۵ دقیقه بعدش به اجرای دستور میرسه.
ولی اینو هم بدون که آنتروپیک خود اکسپلویت رو منتشر نکرده و گزارشی هم از سو استفاده واقعی ازش ثبت نشده ولی خب خیلیا ادعا کردن که poc دارن ولی من که چیزی پیدا نکردم
آره خلاصه، نکته دیگه ای به ذهنم نمیرسه 🤔
با تشکر از شما که تا تهش خوندی. بوس بوس فعلا خدافظ