
Thread Stack Spoofing - PoC एक उन्नत In-Memory evasion तकनीक के लिए है जो स्कैनर्स और विश्लेषकों से इंजेक्ट किए गए शेलकोड के मेमोरी आवंटन को बेहतर ढंग से छिपाने की अनुमति देती है।
यह एक उन्नत इन-मेमोरी एवेज़न तकनीक का PoC कार्यान्वयन है जो थ्रेड कॉल स्टैक को स्पूफ करता है। यह तकनीक थ्रेड-आधारित मेमोरी जांच नियमों को बायपास करने और इन-प्रोसेस मेमोरी में शेलकोड को बेहतर ढंग से छिपाने की अनुमति देती है।
यह थ्रेड स्टैक स्पूफिंग तकनीक का एक उदाहरण कार्यान्वयन है जिसका उद्देश्य मैलवेयर विश्लेषकों, AV और EDR से बचना है जो जांचे गए थ्रेड के कॉल स्टैक में शेलकोड के फ्रेम के संदर्भों की तलाश करते हैं। विचार थ्रेड के कॉल स्टैक पर शेलकोड के संदर्भों को छिपाना है, इस प्रकार मैलवेयर कोड वाले आवंटन को छुपाना है।
मेरे ShellcodeFluctuation के साथ यह कार्यान्वयन Offensive Security समुदाय को वाणिज्यिक C2 उत्पादों द्वारा दी जाने वाली सुविधाओं के साथ तालमेल बिठाने के लिए नमूना कार्यान्वयन प्रदान करता है, ताकि हम अपने Red Team टूलिंग में कमतर न रहें। 💪
वर्तमान कार्यान्वयन मूल रूप से प्रकाशित से काफी भिन्न है। ऐसा इसलिए है क्योंकि मुझे एहसास हुआ कि थ्रेड के कॉल स्टैक प्रोसेसिंग को समाप्त करने और शेलकोड से संबंधित फ्रेम को छिपाने का एक सरल तरीका है: केवल उस पहले फ्रेम के रिटर्न एड्रेस पर 0 लिखना जिसे हम नियंत्रित करते हैं:
void WINAPI MySleep(DWORD _dwMilliseconds)
{
[...]
auto overwrite = (PULONG_PTR)_AddressOfReturnAddress();
const auto origReturnAddress = *overwrite;
*overwrite = 0;
[...]
*overwrite = origReturnAddress;
}
पिछला कार्यान्वयन, जो StackWalk64 का उपयोग करता है, इस कमिट c250724 में देखा जा सकता है।
यह कार्यान्वयन अधिक स्थिर है और दोनों आर्किटेक्चर - x64 और x86 पर Debug और Release दोनों में अच्छी तरह काम करता है।
यह इस प्रकार दिखता है जब कॉल स्टैक स्पूफ नहीं किया गया है:

इसके विपरीत, जब थ्रेड स्टैक स्पूफिंग सक्षम है:

ऊपर हम देख सकते हैं कि हमारे कॉल स्टैक पर अंतिम फ्रेम हमारा MySleep कॉलबैक है। कोई सोच सकता है कि क्या यह तुरंत नए IOC के अवसर लाता है? हंटिंग नियम उन थ्रेड्स की तलाश कर सकते हैं जिनके कॉल स्टैक सिस्टम लाइब्रेरीज में स्थित अपेक्षित थ्रेड एंट्री पॉइंट्स में नहीं खुलते:
kernel32!BaseThreadInitThunk+0x14
ntdll!RtlUserThreadStart+0x21
हालांकि स्पूफ किए गए थ्रेड का कॉल स्टैक पहली नज़र में अजीब लग सकता है, मेरे सिस्टम की एक संक्षिप्त जांच से पता चला कि ऐसे अन्य थ्रेड भी हैं जो उपरोक्त एंट्री पॉइंट्स पर नहीं खुलते:

उपरोक्त स्क्रीनशॉट एक अपरिवर्तित Total Commander x64 के थ्रेड को दिखाता है। जैसा कि हम देख सकते हैं, इसका कॉल स्टैक प्रारंभिक कॉल स्टैक फ्रेम के मामले में हमारे जैसा ही दिखता है।
जब ऐसी प्रक्रियाएं हैं जो ऐसे लक्षण प्रदर्शित करती हैं जिन्हें हम आसानी से नकल कर सकते हैं, तो हमें अपने कॉल स्टैक को सावधानीपूर्वक नकली बनाने की चिंता क्यों करनी चाहिए?
मोटा एल्गोरिथ्म इस प्रकार है:
dbghelp.dll से सभी आवश्यक फ़ंक्शन पॉइंटर्स प्राप्त करें, SymInitialize को कॉल करेंkernel32!Sleep को हुक करें जो हमारे कॉलबैक की ओर इंगित करता है।VirtualAlloc + memcpy + CreateThread के माध्यम से शेलकोड इंजेक्ट और लॉन्च करें। थ्रेड को हमारे runShellcode फ़ंक्शन से शुरू होना चाहिए ताकि थ्रेड का StartAddress किसी अप्रत्याशित और असामान्य स्थान (जैसे ntdll!RtlUserThreadStart+0x21) पर इंगित न हो।MySleep कॉलबैक आमंत्रित होता है।0 से ओवरराइट करते हैं जो प्रभावी रूप से कॉल स्टैक को समाप्त कर देता है।::SleepEx पर कॉल किया जाता है।फ़ंक्शन रिटर्न एड्रेस थ्रेड के स्टैक मेमोरी क्षेत्र में बिखरे होते हैं, जो RBP/EBP रजिस्टर द्वारा इंगित होते हैं। उन्हें स्टैक पर खोजने के लिए, हमें पहले फ्रेम पॉइंटर्स एकत्र करने होंगे, फिर ओवरराइट करने के लिए उन्हें डीरेफरेंस करना होगा:

(उपरोक्त छवि Eli Bendersky की पोस्ट Stack frame layout on 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
[.] Reading shellcode bytes...
[.] Hooking kernel32!Sleep...
[.] Injecting shellcode...
[+] Shellcode is now running.
[>] Original return address: 0x1926747bd51. Finishing call stack...
===> MySleep(5000)
[<] Restoring original return address...
[>] Original return address: 0x1926747bd51. Finishing call stack...
===> MySleep(5000)
[<] Restoring original return address...
[>] Original return address: 0x1926747bd51. Finishing call stack...
कोड और उसके कार्यान्वयन को देखें, अवधारणा को समझें और अवधारणा को अपने स्वयं के शेलकोड लोडर में पुनः लागू करें जिनका उपयोग आप अपने Red Team अभियानों को वितरित करने के लिए करते हैं। यह उन्नत इन-मेमोरी एवेज़न के लिए एक और तकनीक है जो आपकी टीमों के एंटी-वायरस, EDR और आपके इम्प्लांट की जांच करने वाले मैलवेयर विश्लेषकों द्वारा पकड़े जाने की संभावनाओं को कम करती है।
अपने उन्नत शेलकोड लोडर को विकसित करते समय, आप निम्नलिखित को भी लागू करना चाह सकते हैं:
BeaconEye जैसे Beacon कॉन्फ़िगरेशन एक्सट्रैक्टर से बचने में मदद कर सकता हैRW में बदलें (RX/RWX से) और उनकी सामग्री को एन्क्रिप्ट करें - Shellcode Fluctuation तकनीक का उपयोग करके - स्लीप करने से ठीक पहले (जो Moneta या pe-sieve जैसे स्कैनर से बच सकता है)जैसा कि मुझे बताया गया है, यहां तकनीक अभी तक स्टैक स्पूफर होने के अपने नाम पर सही मायने में खरी नहीं उतरती है। चूंकि हम केवल थ्रेड के स्टैक पर रिटर्न एड्रेस को ओवरराइट कर रहे हैं, हम स्टैक के शेष क्षेत्रों को स्पूफ नहीं कर रहे हैं। इसके अलावा हम अपने कॉल स्टैक को अनविंडेबल छोड़ रहे हैं जो इसे असामान्य बनाता है क्योंकि सिस्टम पूरी कॉल स्टैक फ्रेम श्रृंखला को ठीक से नहीं चल सकेगा।