
تزوير مكدس الخيط - إثبات مفهوم لتقنية متقدمة للتهرب داخل الذاكرة تسمح بإخفاء تخصيص الذاكرة للشيل كود المحقون بشكل أفضل من الماسحات الضوئية والمحللين.
تنفيذ إثبات مفهوم لتقنية متقدمة للتهرب من الذاكرة تقوم بتزوير مكدس استدعاء الخيط. تسمح هذه التقنية بتجاوز قواعد فحص الذاكرة القائمة على الخيوط وإخفاء الشيلكود بشكل أفضل أثناء وجودها في ذاكرة العملية.
هذا مثال تنفيذ لتقنية تزوير مكدس الخيط بهدف التهرب من محللي البرامج الضارة وبرامج مكافحة الفيروسات وحلول الكشف والاستجابة الطرفية (EDR) التي تبحث عن مراجع لإطارات الشيلكود في مكدس استدعاء الخيط قيد الفحص. الفكرة هي إخفاء المراجع إلى الشيلكود في مكدس استدعاء الخيط وبالتالي إخفاء التخصيصات التي تحتوي على كود البرامج الضارة.
التنفيذ مع مشروعي ShellcodeFluctuation يقدم لمجتمع الأمن الهجومي تطبيقات نموذجية للحاق بالعروض التي تقدمها منتجات C2 التجارية، حتى نتمكن من تحقيق نتائج لا تقل عنها في أدوات الفريق الأحمر لدينا. 💪
يختلف التنفيذ الحالي اختلافاً كبيراً عما تم نشره أصلاً.
وذلك لأنني أدركت أن هناك نهجاً أبسط لإنهاء معالجة مكدس استدعاء الخيط وإخفاء الإطارات المتعلقة بالشيلكود عن طريق كتابة 0 على عنوان الإرجاع لأول إطار نتحكم به:
void WINAPI MySleep(DWORD _dwMilliseconds)
{
[...]
auto overwrite = (PULONG_PTR)_AddressOfReturnAddress();
const auto origReturnAddress = *overwrite;
*overwrite = 0;
[...]
*overwrite = origReturnAddress;
}
يمكن الوصول إلى التنفيذ السابق، الذي يستخدم StackWalk64، في هذا الالتزام c250724.
هذا التنفيذ أكثر استقراراً بكثير ويعمل بشكل جيد على كل من Debug و Release تحت معماريتين - x64 و x86.
هذا ما قد يبدو عليه مكدس الاستدعاء عندما لا يكون مزوراً:

وهذا بدوره، عندما يكون تزوير مكدس الخيط ممكّناً:

أعلاه يمكننا رؤية أن الإطار الأخير في مكدس الاستدعاء لدينا هو رد النداء MySleep الخاص بنا.
قد يتساءل المرء عما إذا كان هذا يتيح على الفور مؤشرات اكتشاف جديدة (IOCs)؟ يمكن لقواعد الصيد البحث عن خيوط ذات مكدسات استدعاء لا تتفكك إلى نقاط إدخال الخيط المتوقعة التالية الموجودة داخل المكتبات النظامية:
kernel32!BaseThreadInitThunk+0x14
ntdll!RtlUserThreadStart+0x21
ومع ذلك، قد يبدو مكدس استدعاء الخيط المزور غريباً بعض الشيء في البداية، لكن فحصاً موجزاً لنظامي أظهر وجود خيوط أخرى لا تتفكك إلى نقاط الإدخال المذكورة أعلاه أيضاً:

تُظهر لقطة الشاشة أعلاه خيطاً من برنامج Total Commander x64 غير المعدل. كما نرى، يشبه مكدس استدعائه إلى حد كبير مكدسنا الخاص من حيث إطارات مكدس الاستدعاء الأولية.
لماذا يجب أن نهتم بتزييف مكدس الاستدعاء الخاص بنا بعناية عندما تكون هناك عمليات تظهر خصائص يمكننا ببساطة تقليدها؟
الخوارزمية التقريبية هي كما يلي:
dbghelp.dll، واستدعاء SymInitialize.kernel32!Sleep للإشارة مرة أخرى إلى رد النداء الخاص بنا.VirtualAlloc + memcpy + CreateThread. يجب أن يبدأ الخيط من الدالة runShellcode الخاصة بنا لتجنب أن يشير عنوان بداية الخيط إلى مكان غير متوقع وشاذ (مثل ntdll!RtlUserThreadStart+0x21).MySleep الخاص بنا.0 مما ينهي مكدس الاستدعاء بشكل فعال.::SleepEx للسماح للبيكون بالسكون أثناء انتظار مزيد من الاتصالات.تنتشر عناوين إرجاع الدوال في جميع أنحاء منطقة ذاكرة مكدس الخيط، المشار إليها بواسطة سجل RBP/EBP.
للعثور عليها على المكدس، نحتاج أولاً إلى جمع مؤشرات الإطارات، ثم إلغاء الإشارة إليها للكتابة فوقها:

(الصورة أعلاه مقتبسة من منشور Eli Bendersky بعنوان تخطيط إطار المكدس على x86-64)
*(PULONG_PTR)(frameAddr + sizeof(void*)) = Fake_Return_Address;
قام التنفيذ الأولي لـ ThreadStackSpoofer بذلك في دالتي walkCallStack و spoofCallStack، لكن التنفيذ الحالي يظهر أن هذه الجهود ليست مطلوبة للحفاظ على مكدس استدعاء متخفي.
حالة الاستخدام:
C:\> ThreadStackSpoofer.exe <shellcode> <spoof>
حيث:
<shellcode> هو مسار إلى ملف الشيلكود<spoof> عندما 1 أو true سيمكن تزوير مكدس الخيط وأي شيء آخر يعطله.مثال تشغيل يقوم بتزوير مكدس استدعاء خيط البيكون:
PS D:\dev2\ThreadStackSpoofer> .\x64\Release\ThreadStackSpoofer.exe .\tests\beacon64.bin 1
[.] قراءة بايتات الشيلكود...
[.] ربط kernel32!Sleep...
[.] حقن الشيلكود...
[+] الشيلكود يعمل الآن.
[>] عنوان الإرجاع الأصلي: 0x1926747bd51. إنهاء مكدس الاستدعاء...
===> MySleep(5000)
[<] استعادة عنوان الإرجاع الأصلي...
[>] عنوان الإرجاع الأصلي: 0x1926747bd51. إنهاء مكدس الاستدعاء...
===> MySleep(5000)
[<] استعادة عنوان الإرجاع الأصلي...
[>] عنوان الإرجاع الأصلي: 0x1926747bd51. إنهاء مكدس الاستدعاء...
انظر إلى الكود وتنفيذه، وافهم المفهوم وأعد تنفيذه ضمن أدوات تحميل الشيلكود الخاصة بك التي تستخدمها لتوصيل مشاركات فريقك الأحمر. هذه تقنية أخرى للتهرب المتقدم في الذاكرة تزيد من فرص فريقك في عدم الوقوع بواسطة برامج مكافحة الفيروسات وحلول الكشف والاستجابة الطرفية (EDR) ومحللي البرامج الضارة الذين يفحصون برامجك الضارة.
أثناء تطوير أداة تحميل الشيلكود المتقدمة الخاصة بك، قد ترغب أيضاً في تنفيذ:
BeaconEyeRW (من RX/RWX) وتشفير محتوياتها - باستخدام تقنية Shellcode Fluctuation - قبل السكون مباشرة (مما قد يسمح بالتهرب من الماسحات الضوئية مثل Moneta أو pe-sieve)كما أشير إلي، فإن التقنية هنا ليست بعد حقيقية تستحق اسمها كـ مزور مكدس. نظراً لأننا نكتفي بالكتابة فوق عناوين الإرجاع على مكدس الخيط، فإننا لا نقوم بتزوير المناطق المتبقية من المكدس نفسه. علاوة على ذلك، فإننا نترك مكدس الاستدعاء الخاص بنا غير قابل للتفكك مما يجعله يبدو شاذاً لأن النظام لن يكون قادراً على اجتياز سلسلة إطارات مكدس الاستدعاء بأكملها بشكل صحيح.
ومع ذلك، أنا على دراية بهذه القصور، وقد تركته كما هو في الوقت الحالي لأنني اهتممت بشكل أساسي بالتهرب من الماسحات الضوئية الآلية التي يمكنها التكرار على العمليات، وتعداد خيوطها، واجتياز مكدسات تلك الخيوط والتقاط أي عنوان إرجاع يشير إلى ذاكرة غير مصورة (مثل SEC_PRIVATE - الذاكرة المخصصة ديناميكياً بواسطة VirtualAlloc وما شابه ذلك). سيتمكن محلل البرامج الضارة المركز على الفور من ملاحظة الشذوذ واعتبار الخيط غير عادي إلى حد ما، متتبعاً برنامجنا الضار. أنا متأكد من ذلك أكثر من كافي. ومع ذلك، لا أعتقد أن الماسحات الضوئية الآلية في الوقت الحالي مثل AV/EDR لديها أنواع من الاستدلالات المطبقة التي تقوم بالفعل باجتياز مكدس كل خيط للتحقق مما إذا كان غير قابل للتفكك ¯\_(ツ)_/¯.
بالتأكيد، هذا المشروع (والتنفيذ التجاري الموجود في أطر عمل C2) يعطي لبائعي AV و EDR حججاً للنظر في تنفيذ استدلالات مناسبة تغطي مثل هذه التقنية الجديدة للتهرب.