بررسی عمیق آسیب پذیری اکتیو دایرکتوری CVE-2022-26923

در این پست، ما یک بررسی عمیق داریم از وصله امنیتی اخیر آسیب پذیری مایکروسافت دامین اکتیو دایرکتوری که ماهیت ارتقای سطح دسترسی داشته که در تاریخ، سه شنبه 20 اردیبهشت 1401 ریلیز شد. این باگ توسط آقای الیور لیاک تحت پروژه ZDI به مایکروسافت گزارش شده بوده و این پست هم در اصل ترجمه ای از مقاله ایشون هست.در اصل، این آسیب پذیری اینجوریه که به یک کاربر با سطح دسترسی پایین داخل یک دامنه تحت اکتیو دایرکتوری مایکروسافت این اجازه رو پیدا میکنه تا سطح دسترسی خودشو تا حد دامین ادمین بالا ببره. البته سواستفاده از این باگ با استفاده از سرویس گواهی اکتیو دایرکتوری Active Directory Certificate Services - (AD CS) - که اگه سرور با این رول پیشفرض نصب شده باشه ممکن هست.خب این سرویس گواهی اکتیو دایرکتوری چی هست؟ اصلا نصب هست روی سیستم ها؟ ما تقریبا در هر تعاملی ردپای سرویس AD CS رو داخل اکتیو دایرکتوری میبینیم. به ندرت پیش میاد که محیط های متوسط و بزرگ سازمانی یا شرکتی که دامنه تحت اکتیو دایرکتوری دارند، سرویس AD CS رو نصب نداشته باشند.این داستان از کی شروع شد؟تابستان سال 2021، ویل شرودر و لی کریستنسن وایت‌پیپرشون رو با عنوان Certified Pre-Owned: Abusing Active Directory Certificate Services انتشار دادن که در اصل یک نگاه عمیق داشتن به امنیت در سرویس گواهی اکتیو دایرکتوری (AD CS). این وایت‌پیپر به طور کامل ترفندهای مختلفی رو برای تداوم، سرقت و افزایش سطح دسترسی با AD CS توضیح داد. اما همچنین راهنمایی های دفاعی و کلی هم مستندات در خصوص AD CS داخلش بود.زمانی که در ابتدا وایت‌پیپر ویل شرودر و لی کریستنسن را خوندم، تازه شروع به تحقیق در مورد سواستفاده از تنظیمات غلط یا ناصحیح یا همون misconfiguration کردم. تا دسامبر 2021 که از پست وبلاگ چارلی کلارک در خصوص باگ های CVE-2021–42287 و CVE-2021–42278 الهام گرفتم. شما میتوانید به آسیب پذیری های واقعی مربوط به AD CS هم نگاه کنید.مقدمه ای بر خدمات گواهی الکترونیکی اکتیو دایرکتوریاگر از قبل با اصول اولیه خدمات گواهی اکتیو دایرکتوری آشنا هستید، می توانید از این بخش صرف نظر کنید. از سوی دیگر، اگر پس از خواندن این بخش همچنان در مورد زیرساخت کلید عمومی (PKI) و گواهینامه ها گیج هستید، نگران نباشید. برای این آسیب‌پذیری، می‌توانید گواهی را صرفاً به عنوان یک مدرک شناسایی، مشابه یک تیکت کربروس (Kerberos ticket) در نظر بگیرید.اگر تا حالا مقاله ویل شرودر و لی کریستنسن رو نخوانده اید، به شدت توصیه می‌کنم قبل از ادامه، نسخه کوتاه‌شده “Certified Pre-Owned” را مطالعه کنید. من سعی میکنم جزئیات را در سراسر این پست نیز پوشش دهم، اما ویل شرودر و لی کریستنسن قبلاً کار خفنی در توضیح موارد ضروری انجام داده اند، بنابراین در اینجا یک قطعه از پست وبلاگ آنها است که به طور کامل AD CS را خلاصه می کند.سرویس گواهی اکتیودایرکتوری (AD CS) یک Server Role است که به عنوان زیرساخت کلید عمومی PKI مایکروسافت عمل می‌کند. همانطور که انتظار می رود، به شدت با اکتیودایرکتوری ادغام می شود و صدور گواهینامه ها را امکان پذیر می‌کند. گواهی ها، اسناد الکترونیکی امضا شده دیجیتال با فرمتX.509 هستند که می‌توانند برای رمزگذاری، امضای پیام، و/یا احراز هویت استفاده شوند.اطلاعات موجود در یک گواهی، یک هویت (subject) را به یک جفت کلید عمومی/خصوصی متصل می‌کند. سپس یک برنامه کاربردی می تواند از جفت کلید در عملیات به عنوان اثبات هویت کاربر استفاده کند. مقامات صدور گواهی (CAs) مسئول صدور گواهی هستند.در سطح بالایی، مشتریان یک جفت کلید عمومی-خصوصی تولید می‌کنند و کلید عمومی در پیام درخواست امضای گواهی (CSR) -certificate signing request- همراه با جزئیات دیگری مانند موضوع گواهی و نام الگوی گواهی قرار می‌گیرد. سپس مشتریان CSR را به سرور Enterprise CA ارسال می‌کنند. سپس سرور CA بررسی می کند که آیا مشتری مجاز به درخواست گواهی است یا خیر. در این صورت، با جستجوی شیء الگوی گواهی اکتیودایرکتوری [AD] که در CSR مشخص شده است، تعیین می کند که آیا گواهی را صادر خواهد کرد یا خیر.در این صورت، CA یک گواهی را با استفاده از تنظیمات "blueprint" و سایر اطلاعات ارائه شده در CSR ، در صورتی که تنظیمات الگوی گواهی مجاز باشد را تولید می‌کند. CA گواهی را با استفاده از کلید خصوصی خود امضا می‌کند و سپس آن را به مشتری برمی‌گرداند.برای مشخص تر شدن قضیه نگاهی به تصویر زیر بیاندازید:فرایند درخواست و صدور گواهیدر اصل، کاربران می توانند یک گواهی را بر اساس یک الگوی گواهی از پیش تعریف شده درخواست کنند. این الگوها تنظیمات گواهی نهایی را مشخص می‌کند، به عنوان مثال اینکه آیا می توان از آن برای احراز هویت مشتری استفاده کرد؟، چه ویژگی هایی باید تعریف شود؟، چه کسی مجاز به ثبت نام است؟، و غیره. در حالی که AD CS می تواند برای اهداف مختلف مورد استفاده قرار گیرد، ما فقط بر جنبه تأیید اعتبار مشتری AD CS تمرکز خواهیم کرد. وگرنه AD CS می تواند برای اهداف مختلف مورد استفاده قرار گیرد.بنابراین، اجازه دهید یک مثال سریع در مورد نحوه استفاده از گواهینامه ها برای احراز هویت در اکتیودایرکتوری ایجاد کنیم. ما از ابزار Certipy برای درخواست و احراز هویت با گواهی استفاده خواهیم کرد. من دامنه‌ی جدیدی که AD CS را داشته باشد با نام CORP.LOCAL ایجاد کردم. همچنین یک کاربر پیش فرض و کم امتیاز به نام JOHN ایجاد کرده ام. در مثال زیر، ما یک گواهی از CORP-DC-CA بر اساس الگوی User درخواست می‌کنیم. سپس از گواهی صادر شده john.pfx، برای احراز هویت در مرکز توزیع کلید مایکروسافت (KDC) استفاده می کنیم. هنگام احراز هویت با یک گواهی، ابزار Certipy سعی می کند یک Kerberos TGT درخواست کند و hash NT آن اکانت را از سرور بگیرد.درخواست و احراز هویت با استفاده از گواهی الکتریکیآسیب‌پذیریکشفبه طور پیش فرض، کاربران دامنه می توانند در قالب گواهی کاربر User ثبت نام کنند.کامپیوترهای دامنه هم می توانند در قالب گواهی ماشین Machine ثبت نام کنند.هر دو الگوی گواهی امکان احراز هویت مشتری را فراهم می کنند. این به این معنی است که گواهی صادر شده می تواند برای احراز هویت در KDC از طریق PKINIT Kerberos extension استفاده شود.خب حالا شاید از خودمان بپرسیم چرا AD CS قالب های مختلفی برای کاربران و رایانه ها دارد؟ به طور خلاصه، یوزر اکانت‌ها یک ویژگی به نام User Principal Name- (UPN)- دارند، در حالی که کامپیوتر اکانت‌ها اینطور نیستند. هنگامی که بر اساس الگوی User درخواست گواهی می‌کنیم ، یک بخش UPN برای شناسایی در گواهی جاسازی می‌شود. وقتی که از آن گواهی برای احراز هویت استفاده کنیم، KDC سعی می‌کند UPN ای که در داخل گواهی هست را به یک کاربر نگاشت کند. اگر به ویژگی msPKI-Certificate-Name-Flag داخل قالب نگاهی کنیم، می بینیم که بخش SubjectAltRequireUpn (CT_FLAG_SUBJECT_ALT_REQUIRE_UPN) در آن مشخص شده است.قالب گواهی [User]مطابق اسناد MS-ADTS ، یک UPN باید منحصر به فرد باشد، به این معنی که ما نمی توانیم دو کاربر با UPN یکسان داشته باشیم. به عنوان مثال، اگر بخواهیم UPN کاربر Jane را به John@corp.local تغییر دهیم یک خطای نقض محدودیت دریافت می کنیم، زیرا UPN با مشخصات John@corp.local ، از قبل به John داده شده است.نقض محدودیت هنگام تلاش برای تغییر UPN کاربر "Jane" به "John@corp.local" همانطور که قبلا ذکر شد، کامپیوتر اکانت‌ها UPN ندارند. پس کامپیوتر اکانت‌ها برای احراز هویت با گواهی از چه چیزی استفاده می‌کنند؟ اگر به الگوی گواهی Machine نگاه کنیم، می بینیم کهSubjectAltRequireDns (CT_FLAG_SUBJECT_ALT_REQUIRE_DNS) به جای آن درنظر گرفته شده است."Machineگوی گواهی "بنابراین بیایید سعی کنیم یک کامپیوتر اکانت جدید ایجاد کنیم، یک گواهی درخواست کنیم و سپس با آن گواهی احراز هویت کنیم."Machineمایش الگوی گواهی "همانطور که در بالا می‌بینیم، گواهی با نام DNS ای JOHNPC.corp.local صادر می شود و اگر به کامپیوتر اکانت JOHNPC نگاه کنیم متوجه می‌شویم که این مقدار در فیلد dNSHostName تعریف شده است.حالا اگر به مجوزهای آبجکت JOHNPC نگاهی بیندازیم، میبینیم که سازنده کامپیوتر اکانت یا همان ( John ) مجوز نوشتن فیلد نام DNS را دارد. در شکل زیر “Validated write to DNS host name” را ببینید:مجوز “Validated write to DNS host name” در اینجا توضیح داده شده است، و به عنوان «مجوز Write برای فعال کردن نام میزبان DNS که با نام رایانه و نام دامنه تطابق دارد» توصیف می‌شود. بنابراین "تطابق با نام کامپیوتر و نام دامنه" به چه معناست؟اگر ما تحت عنوان ( John ) بخواهیم فیلد DNS host name مربوط به آبجکت کامپیوتر JOHNPC را به TEST.corp.local تغییر دهیم، با خطای نقض محدودیت مواجه نمی‌شویم و در داخل فایل SAM ، بخش نام JOHNPC همچنان JOHNPC$ باقی مانده است.خب پس حالا بیایید یک گواهی الکترونیکی درخواست کنیم:دیاتفاقی که افتاد این بود که ما متوجه شدیم گواهی با نام دامنه‌ای هاستTEST.corp.local صادر شده است. بنابراین ما کاملا مطمئن هستیم که DNS host name از فیلد dNSHostName استخراج شده است و John (به عنوان سازنده کامپیوتر اکانت) مجوز “Validated write to DNS host name” را دارد.آسیب‌پذیریاگر اسناد MS-ADTS را بخوانیم، متوجه می‌شویم که در هیچ جای آن اشاره ای نشده است که فیلد dNSHostName حتما باید یکتا باشد.خب حالا میخواهیم یک کار باحال انجام دهیماگر در آبجکت دامین کنترلر (DC$) دنبال فیلد dNSHostName بگردیم متوجه می‌شویم که مقدار آن برابر است با : DC.CORP.LOCAL بنابراین بیاید سریع و بدون هیچ بحثی سعی کنیم مقدار ویژگی dNSHostName مربوط به کامپیوتر اکانت JOHNPC را به DC.CORP.LOCAL تغییر بدهیم.این بار با پیغام خطایی مبنی بر “An operations error occurred” مواجه می‌شویم. این خطا با آن زمانی که ما سعی کردیم UPN یک کاربر را با UPN کاربر دیگری تغییر دهیم، متفاوت است چون آنجا دقیقا با یک خطای نقض محدودیت موجه شده بودیم. خب پس دقیقا چه اتفاقی افتاده است؟خب، اگر با دقت بیشتری نگاه کنیم میبینیم که ما dNSHostName مربوط به کامپیوتر اکانتJOHNPC از مقدار JOHNPC.corp.local به مقدار TEST.corp.local تغییر داده ایم، همچنین میبینیم که مقدار فیلد servicePrincipalName آپدیت شده تا مقدار جدید dNSHostName را نشان دهد.و با توجه به مستندات MS-ADTS، فیلد servicePrincipalName برای یکتا بودن مورد بررسی قرار می‌گیرد. بنابراین وقتی ما میخواهیم فیلد dNSHostName را به DC.corp.local تغییر دهیم، دامین کنترلر سعی می‌کند تا فیلدservicePrincipalName را هم آپدیت کند که این آپدیت شامل دو عبارت RestrictedKrbHost/DC.corp.local و HOST/DC.corp.local می‌شود که آنوقت با مقدارservicePrincipalName مربوط به دامین کنترلر در تضاد است که باید یکتا باشد. بنابراین با آپدیت کردن فیلدdNSHostName مربوط بهJOHNPC ، در حقیقت ما به صور غیرمستقیم باعث نقض محدودیت دامین کنترلر در زمانی که سعی می‌کردهservicePrincipalName مربوط به JOHNPC را آپدیت کند، شده ایم.اگر نگاهی به مجوزهای آبجکتJOHNPC بیاندازیم، می‌بینیم که در آن نام کاربریJohn (ایجاد کننده کامپیوتر اکانت) مجوزی با عنوان “Validated write to service principal name” را دارد.مجوز “Validated write to service principal name” در اینجا توضیح داده شده است، و این مجوز به این صورت توضیح داده شده است که "دسترسی write برای فعال کردن تنظیمات SPN ای که با DNS مطابق دارد". بنابراین ما اگر بخواهیم فیلدservicePrincipalName مربوط بهJOHNPC را آپدیت کنیم، آپدیت باید در تطابق با ویژگیdNSHostName باشد.خب دوباره، تطابق در اینجا چه معنی ای میدهد؟ متوجه شدیم که فقط دو مقدار هستند که زمانی کهdNSHostName را آپدیت کردیم، بررسی و آپدیت شدند. این دو مقدارRestrictedKrbHost/TEST.corp.local وHOST/TEST.corp.local هستند که دربرگیرنده مقدار فیلدdNSHostName هستند. دو مقدار دیگر این فیلدRestrictedKrbHost/JOHNPC وHOST/JOHNPC هستند که شامل مقدارsAMAccountName بدون $ در انتهای آن هستند.بنابراین نتیجه می‌گیریم که فقط مقادیری از فیلد servicePrincipalName که دربرگیرنده dNSHostName هستند باید با ویژگیdNSHostName منطبق باشند. ولی آیا ما میتوانیم فقط مقادیری ازservicePrincipalName را که شامل dNSHostName می شود را حذف کنیم؟به میتوانیم :دیبنابراین حالا بیاید سعی کنیم مقدارdNSHostName مربوط بهJOHNPC را به DC.corp.local تغییر دهیم، دامین کنترلر نیازی به به روزرسانیservicePrincipalName نخواهد داشت زیرا هیچ یک از مقادیر حاوی dNSHostName نیستند.بیاییدdNSHostName مربوطه بهJOHNPC را بهDC.corp.local تغییر دهیم.موفقیت آمیز بود!میتوانیم ببینیم که ویژگیdNSHostName بهDC.corp.local آپدیت شده است، و servicePrincipalName تحت تاثیر این تغییر قرار نگرفته است، که این به این معنی است که ما هیچگونه نقض محدودیتی را ایجاد نکرده ایم.بنابراین حالاJOHNPC مقدارdNSHostName دقیقا مشابه دامین کنترلرDC$ را دارد.حالا بیاید یک گواهی برایJOHNPC با استفاده از الگوی گواهیMachine درخواست بدهیم که در آن ویژگیdNSHostName به عنوان احراز هویت درج می‌شود.یک موفقیت دیگر:دیما حالا گواهی ای را داریم که با مقدارDC.corp.local در ویژگی DNS host name خود دارد. حالا بیاید با این گواهی احراز هویت کنیم.احراز هویت هم موفقیت آمیز بود، و همانطور که می‌بینید Certipy به عنوان Proof-of-Concept، مقدار NT Hash مربوط به دامین کنترلر را استخراج کرده است. حالا ما با استفاده از NT Hash دامین کنترلر میتوانیم حمله ی DCSync attack را برای استخراج هش تمامی کاربران استفاده کنیم و یا اینکه با فرایند Pass-The-Hash از آن برای گرفتن تیکت کربروس استفاده کنیم.حتما برایتان تعجب آور است که چرا ما مجبور نیستیم DNS host name مربوط بهJOHNPC را به چیزی که قبل از احراز هویت با گواهی بوده است، تغییر دهیم. چگونه KDC میداند که کدام اکانت با گواهی مربوطه متناظر است؟ادامه دارد...منبع نوشته