
إثبات المفهوم لـ CVE-2022-42046 لـ wfshbr64.sys تصعيد الامتيازات المحلية عبر DKOM
CVE-2022-42046 إثبات المفهوم لتصعيد الامتيازات المحلي لـ wfshbr64.sys
wfshbr64.sys و wfshbr32.sys الحمولة المصممة خصيصًا تسمح لأي مستخدم بإجراء عملية بتوزيع على إزاحة EPROCESS وقيمة الأعلام عشوائية لرفع عملية اللعبة بشكل متعمد إلى حماية كاملة من CodeGen عن طريق التلاعب بأعلام EPROCESS.Protection و EPROCESS.SignatureLevel (ثغرة أمنية كميزة).
الدرايفر موقّع من ناشر توافق أجهزة Microsoft والذي تم تقديمه عبر برنامج أجهزة Microsoft.
تم إجراء هذا البحث بالتعاون مع @DoranekoSystems
MIT. انظر LICENSE
ObRegisterCallbacks بدلاً من رفع حماية العملية بالقوة عن طريق التلاعب المباشر بكائنات النواة. يوجد مثال جيد هنا.لا تقم بالإشارة إلى IRP بعد الانتهاء. إذا كان لديك مدقق الدرايفر مفعّلاً، فسيتم اكتشافك.
IofCompleteRequest(Irp, IO_NO_INCREMENT); // IRP يتم تحريره هنا
return Irp->IoStatus.Status;
بدلاً من ذلك، يجب استخدام متغير محلي.
NTSTATUS status = STATUS_SUCCESS;
Irp->IoStatus.Status = status;
IofCompleteRequest(Irp, IO_NO_INCREMENT); // IRP يتم تحريره هنا
return status;
يبدو أنك تتحقق من المؤشر الفارغ مقابل القيمة المرجعة لـ IoGetCurrentProcess، ولكنها لا تُرجع مؤشرًا فارغًا أبدًا حسب التصميم، لذا لا داعي للتحقق منه.
PEPROCESS CurrentProcess = IoGetCurrentProcess();
if ( !CurrentProcess ) // لا حاجة للتحقق من المؤشر الفارغ
break;
بعد فترة من التقرير، قام المطور بتنفيذ "تحقق إضافي" خفي لهزيمة أول PoC لدينا بدلاً من التراجع عن جعل الثغرات الأمنية ميزة.
تمت إضافة التحققات إلى:
IOCTL_WFSHBR_REMOVE_FLAGIOCTL_WFSHBR_ADD_FLAGIOCTL_WFSHBR_AND_FLAGcase IOCTL_WFSHBR_ADD_FLAG: // 0xAA013884
if ( !KwfsVerifyCaller(Buffer) ) // التحقق من المتصل
break;
- if ( Buffer->ArbitraryEProcessOffset >= 0x1000 ) // حد التحقق من الإزاحة
+ if ( !KwfsVerifyOffsetAndFlags(Buffer->ArbitraryEProcessOffset,
+ Buffer->DesiredFlags) ) // التحقق من الإزاحة والأعلام
break;
*(ULONG*)(IoGetCurrentProcess() + Buffer->ArbitraryEProcessOffset) |= Buffer->DesiredFlags;
هذه الروتين مصممة ليتم استدعاؤها في كل مرة يطلب فيها العميل تعديل EPROCESS، وتقوم بالتحقق من Offset المقدم من حقل ArbitraryEProcessOffset في هذا PoC – وكذلك Flags المقدم من حقل DesiredFlags في هذا PoC.
التحقق بسيط جدًا حيث يقوم بعد بتات 1 في كل حقل بت من الأعلام المقدمة، وإذا كان العدد أكبر من ثمانية، يفشل.
خريطة نمط الأعلام المحتملة هي أربعة فقط:
22 00 00 0000 22 00 0000 00 22 0000 00 00 22وبناءً عليه، فإن إجراء العمليات التالية 4 مرات يضمن أن واحدة على الأقل من المحاولات ستنجح:
ArbitraryEProcessOffset حسب الفهرس: offset - index,DesiredFlags حسب الفهرس: flag << (index * 8).يتم تقليل الإزاحة، لذا فإن ضبط حقل البتات سيؤدي إلى تعديل الإزاحة في عوامل البتوزيع.
*(ULONG*)(IoGetCurrentProcess() + offset) |= flags;
*(ULONG*)(IoGetCurrentProcess() + offset) &= ~flags;
لقد أضفنا دالتين WfsProtectProcessSupreme و WfsUnprotectProcessSupreme اللتين تقومان بالمحاولة وهزيمة الحيلة الجديدة.
enum KwfsState {
KwfsStateOnceCall = 0,
KwfsStateNeedsValueEquality = 1,
KwfsStateValueHasBeenSet = 2,
};
bool KwfsVerifyOffsetAndFlags(_In_ ULONG offset, _In_ ULONG offset flags)
{
if (KwfsState::KwfsState == KwfsState::KwfsStateOnceCall) {
g_KwfsVerifyState = KwfsState::KwfsStateValueHasBeenSet;
g_KwfsVerifyStateOffset = offset;
g_KwfsVerifyStateFlags = flags;
if (offset < 0x1000) { // تم نقل حد التحقق من الإزاحة إلى هنا
auto bitcount = 0;
for (auto i = 0; i < 32; ++i) { // عد بتات `1` في الأعلام
if (flags & (1 << i)) {
++bitcount;
}
}
if (bitcount <= 8) { // يجب أن يكون العد أقل من تسعة
g_KwfsVerifyState = 1;
return true;
}
}
}
else
{
if (g_KwfsVerifyState != KwfsState::KwfsStateValueHasBeenSet
|| offset != g_KwfsVerifyStateOffset
|| flags != g_KwfsVerifyStateFlags) {
return false;
}
}
return false;
}