در این پست، ما یک بررسی عمیق داریم از وصله امنیتی اخیر آسیب پذیری مایکروسافت دامین اکتیو دایرکتوری که ماهیت ارتقای سطح دسترسی داشته که در تاریخ، سه شنبه 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
) در آن مشخص شده است.
مطابق اسناد MS-ADTS ، یک UPN باید منحصر به فرد باشد، به این معنی که ما نمی توانیم دو کاربر با UPN یکسان داشته باشیم. به عنوان مثال، اگر بخواهیم UPN کاربر Jane
را به John@corp.local
تغییر دهیم یک خطای نقض محدودیت دریافت می کنیم، زیرا UPN با مشخصات John@corp.local
، از قبل به John
داده شده است.
همانطور که قبلا ذکر شد، کامپیوتر اکانتها UPN ندارند. پس کامپیوتر اکانتها برای احراز هویت با گواهی از چه چیزی استفاده میکنند؟ اگر به الگوی گواهی Machine نگاه کنیم، می بینیم کهSubjectAltRequireDns
(CT_FLAG_SUBJECT_ALT_REQUIRE_DNS
) به جای آن درنظر گرفته شده است.
بنابراین بیایید سعی کنیم یک کامپیوتر اکانت جدید ایجاد کنیم، یک گواهی درخواست کنیم و سپس با آن گواهی احراز هویت کنیم.
همانطور که در بالا میبینیم، گواهی با نام 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 میداند که کدام اکانت با گواهی مربوطه متناظر است؟
ادامه دارد...