
تحليل تقني وإثبات المفهوم لـ CVE-2020-0796 (SMBGhost)، ثغرة تجاوز عدد صحيح في ضغط SMBv3 تؤدي إلى تصعيد الامتيازات المحلية على Windows 10/Server.
تمت إضافة ميزة الضغط إلى SMBv3 بدءًا من إصدار نظام التشغيل Windows 10/Server version 1903، والتي تحتوي على ثغرة تجاوز سعة عدد صحيح (integer overflow) أكدتها Microsoft في 12/03/2020. تسمح للمهاجم بتنفيذ تصعيد الامتيازات المحلية (LPE) وتنفيذ التعليمات البرمجية عن بُعد (RCE). هنا سنتحدث فقط عن ثغرة LPE.
الإصدارات المتأثرة:
تحليل ملف srv2.sys، نلاحظ أن الدوال المتعلقة بعملية فك الضغط (Decompress) يتم استدعاؤها كما يلي:``` js
Srv2ReceiveHandler
|
|
v
Srv2DecompressMessageAsync
|
|
v
Srv2DecompressData -------> SrvNetAllocateBuffer
|
|
v
SmbCompressionDecompress
|
|
v
memcpy
أولاً، يتم استدعاء الدالة `Srv2ReceiveHandler` لاستقبال حزمة بيانات smb واستدعاء دالة مقابلة لبروتوكول `ProtocolId`. إذا كان `ProtocolId` = 0x424D53FC، فسيتم استدعاء الدالة `Srv2DecompressMessageAsync`، والتي ستقوم بدورها باستدعاء الدالة `Srv2DecompressData` لفك ضغط حزمة البيانات. تقوم الدالة `Srv2DecompressData` باستدعاء الدالة `SrvNetAllocateBuffer` لتخصيص `Alloc` لتخزين البيانات بعد فك الضغط، ثم تستدعي الدالة `SmbCompressionDecompress` لفك ضغط حزمة البيانات، وأخيراً تستدعي الدالة `memcpy`. وهكذا، تتكون عملية فك الضغط بأكملها من الخطوات الرئيسية التالية:
- 1. تخصيص (Allocate)
- 2. فك الضغط (Decompress)
- 3. نسخ (Copy)
وفقًا للوثائق التي توفرها Microsoft، يتم استخدام الهيكل `COMPRESSION_TRANSFORM_HEADER` لإرسال واستقبال البيانات المضغوطة بين العميل والخادم. وله الهيكل التالي:``` c
typedef struct _COMPRESSION_TRANSFORM_HEADER
{
ULONG ProtocolId;
ULONG OriginalCompressedSegmentSize;
USHORT CompressionAlgorithm;
USHORT Flags;
ULONG Offset;
} ;
هنا نركز فقط على الحقلين الرئيسيين أعلاه وهما:
OriginalCompressedSegmentSize هو حجم مقطع البيانات غير المضغوط، بالبايت.Offset هو الإزاحة بالبايت بين نقطة بداية البيانات المضغوطة ونقطة نهاية هيكل _COMPRESSION_TRANSFORM_HEADER.وبالتالي، فإن حزمة البيانات المضغوطة ستبدو كما يلي:
``` c
typedef struct _ALLOCATION_HEADER
{
// ...
PVOID UserBuffer;
// ...
} ALLOCATION_HEADER, *PALLOCATION_HEADER;
NTSTATUS Srv2DecompressData(PCOMPRESSION_TRANSFORM_HEADER Header, SIZE_T TotalSize) { PALLOCATION_HEADER Alloc = SrvNetAllocateBuffer( (ULONG)(Header->OriginalCompressedSegmentSize + Header->Offset), NULL); If (!Alloc) { return STATUS_INSUFFICIENT_RESOURCES; }
ULONG FinalCompressedSize = 0;
NTSTATUS Status = SmbCompressionDecompress(
Header->CompressionAlgorithm,
(PUCHAR)Header + sizeof(COMPRESSION_TRANSFORM_HEADER) + Header->Offset,
(ULONG)(TotalSize - sizeof(COMPRESSION_TRANSFORM_HEADER) - Header->Offset),
(PUCHAR)Alloc->UserBuffer + Header->Offset,
Header->OriginalCompressedSegmentSize,
&FinalCompressedSize);
if (Status < 0 || FinalCompressedSize != Header->OriginalCompressedSegmentSize) {
SrvNetFreeBuffer(Alloc);
return STATUS_BAD_DATA;
}
if (Header->Offset > 0) {
memcpy(
Alloc->UserBuffer,
(PUCHAR)Header + sizeof(COMPRESSION_TRANSFORM_HEADER),
Header->Offset);
}
Srv2ReplaceReceiveBuffer(some_session_handle, Alloc);
return STATUS_SUCCESS;
}
تحليل الدالة `Srv2DecompressData`، نجد أن الدالة تستقبل حزمة بيانات مضغوطة `COMPRESSION_TRANSFORM_HEADER` (Header)، ثم تقوم بتخصيص منطقة ذاكرة (Alloc) باستخدام الدالة `SrvNetAllocateBuffer` مع معامل هو مجموع `Header->OriginalCompressedSegmentSize` + `Header->Offset`، ثم تقوم بفك ضغط البيانات المضغوطة ونسخ البيانات غير المضغوطة إلى `Alloc->Buffer`.

يحدث خطأ تجاوز سعة عدد صحيح (integer overflow) عندما تستدعي الدالة `Srv2DecompressData` الدالة `SrvNetAllocateBuffer`، حيث تستقبل الدالة `SrvNetAllocateBuffer` في الواقع قيمتين 64 بت، ولكن عند استدعاء `SrvNetAllocateBuffer`، تمرر إليها `Srv2DecompressData` قيمتين 32 بت فقط (ULONG). بينما كل من `OriginalCompressedSegmentSize` و `Offset` هما من نوع ULONG، وعند جمعهما معًا، قد ينتج رقم أكبر من 32 بت. ولهذا يحدث خطأ تجاوز السعة (ببساطة، عند جمع 0xffffffff (`OriginalCompressedSegmentSize`) مع 0x10 (`Offset`) ينتج القيمة 0xf0000000f ولكن الدالة `SrvNetAllocateBuffer` تستقبل فقط القيمة 0x0000000f).

سيؤدي خطأ تجاوز السعة إلى تخصيص خاطئ لمنطقة الذاكرة Alloc (حجم التخصيص المطلوب أصغر من الحجم الفعلي)، مما قد يسبب خطأ تجاوز سعة المخزن المؤقت (buffer overflow):
