概述:在 [[【RPC】GodPotato原理分析|GodPotato原理分析 文中,描述了 GodPotato 的大致逻辑,但是对核心的处理操作部分没有处理,也就是——RPCSS 的接口 Hook 部分,本文就这一部分进行记录。

调试环境

Windows Server 2016

前文:GodPotato原理分析

调试

代码部分我们已经了解了,那么调试部分就比较简单了,双机调试挂载目标进程,在 combase!_UseProtseq 处下断点即可。当然,整体的流程可以结合 IDA 反汇编的代码查看。

UseProtseq 函数原型

⚠️ 注意 UseProtseq 在不同版本下的参数可能不同

__int64 __fastcall UseProtseq(
        void *hRpc,
        unsigned __int16 wTowerId,
        unsigned int *pdwTCPPort,
        tagDUALSTRINGARRAY **ppsaNewBindings,
        tagDUALSTRINGARRAY **ppsaSecurity)

调试准备

  1. 添加进程断点

    !gflag +ksl;
    sxe cpr:GodPotato.exe
    
  2. 查看被调用接口 这个断点需要添加在 rpcss 服务中

    bu RPCRT4!Invoke+0x73-2d ".echo \"RPCRT4!Invoke Call:\";u r10 l5;gc"
    

    或者我们也可以在被调试程序(GodPotato)中添加 RPCRT4!NdrClientCall2 断点查看 RPC 都调用了那些接口:

    bu RPCRT4!NdrClientCall2 "k"
    
  3. 查看 GodPotato Hook 接口

    bp combase!_UseProtseq
    

触发断点

在 GodPotato 源码中,调用 unmarshalTrigger.Trigger() 就触发相关断点了(最终调用的是 combase!CoUnmarshalInterface)。

ClientResolveOXID 断点

CoUnmarshalInterface 接口调用时,使用了正常申请的 OXID 来定位和访问服务器上的对象,这一步 RPC 操作是验证 OXIDGodPotato 发起的堆栈如下所示:

# Child-SP          RetAddr               Call Site
00 00000000`008fde28 00007ffb`c9a3963a     RPCRT4!NdrClientCall2
01 (Inline Function) --------`--------     combase!ClientResolveOXID+0x4a [onecore\com\combase\idl\internal\daytona\objfre\amd64\lclor_c.c @ 279]
02 00000000`008fde30 00007ffb`c99db6c3     combase!CRpcResolver::ClientResolveOXID+0xca [onecore\com\combase\dcomrem\resolver.cxx @ 1207]
03 00000000`008fdec0 00007ffb`c98f3597     combase!COXIDTable::ClientResolveOXID+0x1190db [onecore\com\combase\dcomrem\ipidtbl.cxx @ 3111]
04 00000000`008fe070 00007ffb`c98efd6b     combase!ReadObjRef+0x247 [onecore\com\combase\dcomrem\marshal.cxx @ 9974]
05 00000000`008fe140 00007ffb`74d6d1e7     combase!CoUnmarshalInterface+0x7b [onecore\com\combase\dcomrem\coapi.cxx @ 1962]
06 00000000`008fe5f0 00007ffb`154fa678     mscorwks!DoNDirectCall__PatchGetThreadCall+0x7b
07 00000000`008fe690 00007ffb`154fa3d8     0x00007ffb`154fa678
08 00000000`008fe7f0 00007ffb`154fa32b     0x00007ffb`154fa3d8
09 00000000`008fe840 00007ffb`154f8c78     0x00007ffb`154fa32b
0a 00000000`008fe8b0 00007ffb`154f06e4     0x00007ffb`154f8c78
0b 00000000`008feb50 00007ffb`74d6d9c2     0x00007ffb`154f06e4
0c 00000000`008fee70 00007ffb`74c75a23     mscorwks!CallDescrWorker+0x82
0d 00000000`008feec0 00007ffb`7515d231     mscorwks!CallDescrWorkerWithHandler+0xd3
0e 00000000`008fef60 00007ffb`74cc73b3     mscorwks!MethodDesc::CallDescr+0x2b1
0f 00000000`008ff1a0 00007ffb`74ce9b88     mscorwks!ClassLoader::RunMain+0x22b
10 00000000`008ff400 00007ffb`7524c6bd     mscorwks!Assembly::ExecuteMainMethod+0xbc
11 00000000`008ff6f0 00007ffb`74cf6d73     mscorwks!SystemDomain::ExecuteMainMethod+0x47d
12 00000000`008ffcc0 00007ffb`74cda434     mscorwks!ExecuteEXE+0x47
13 00000000`008ffd10 00007ffb`b26ed6ea     mscorwks!CorExeMain+0xac
14 00000000`008ffd70 00007ffb`b4bcac42     mscoreei!CorExeMain+0xfa
15 00000000`008ffdd0 00007ffb`c8b97374     MSCOREE!CorExeMain_Exported+0x72
16 00000000`008ffe00 00007ffb`caa7cc91     KERNEL32!BaseThreadInitThunk+0x14
17 00000000`008ffe30 00000000`00000000     ntdll!RtlUserThreadStart+0x21

会通过 rpc 使用 NdrClientCall2 调用到 rpcss 服务的 ClientResolveOXID

ClientResolveOXID 功能说明:

在 COM 分布式环境中,对象导出标识符(OXID,Object Export Identifier)用于唯一标识服务器上的一个导出对象集。ClientResolveOXID 接口的主要功能是让客户端根据给定的 OXID 解析出与之关联的服务器信息和对象信息,从而建立与服务器上特定对象的连接,以便后续调用该对象的接口方法。

ResolveOxid2 调用

当一个客户端要调用一个远程COM对象时,它需要通过 ResolveOxid2 函数来获取对象的绑定信息, 获取到的绑定信息通常会指向RPC端点映射器(\pipe\epmapper),以便客户端可以进一步查询服务的端点信息

首次 rpcss!ClientResolveOXID 被调用后,rpcss 就会通过 ResolveOxid2 来获取 EndPoint 这一步仍会通过 rpc 调用,

位于 rpcss 中的 ClientResolveOXID 接口一步步(通过 ResolveOxid2)调用到 rpcss!CProcess::UseProtseqIfNeeded,这个接口同样是调用的 rpc 接口,被调用接口的相关信息如下所示:

1: kd> !NdrClientCall2
 Tue Feb 11 17:57:13.679 2025 (UTC + 8:00) NdrClientCall2() Stack 
	[RPC] Request Function: rpcss!CProcess::UseProtseqIfNeeded  
addr_rpc_interface:0x7fff330b1080
	[RPC] Caller Info:
		Name:svchost.exe
		PID:904
		Command:C:\Windows\system32\svchost.exe -k RPCSS -p
	[RPC] GUID&Procederes: {18f70770-8e64-11cf-9af1-0020af6e72f4}:1
@$NdrClientCall2()

可以看到被调用 rpc 的 GUID{18f70770-8e64-11cf-9af1-0020af6e72f4},调用接口 ID 为 1。这个 GUID 和 ID 是不是很熟悉 😂。就是 UseProtseq 接口(通过 Hook 修改的接口)。从 UseProtseqIfNeeded 调用到了 GodPotatocombase!UseProtseq

调用堆栈如下所示:

0: kd> k
 # Child-SP          RetAddr               Call Site
00 00000016`b57fed28 00007fff`330489f0     rpcss!CProcess::UseProtseqIfNeeded
01 00000016`b57fed30 00007fff`33051a27     rpcss!CServerOxid::LazyUseProtseq+0x58
02 00000016`b57fed80 00007fff`33052943     rpcss!ResolveOxidCommon+0x1bb
03 00000016`b57fef20 00007fff`36dea2d3     rpcss!ResolveOxid2+0x173
04 00000016`b57fefb0 00007fff`36d825a7     RPCRT4!Invoke+0x73
05 00000016`b57ff030 00007fff`36dd0c3a     RPCRT4!NdrStubCall2+0x3f7
06 00000016`b57ff380 00007fff`36dcb128     RPCRT4!NdrServerCall2+0x1a
07 00000016`b57ff3b0 00007fff`36da8146     RPCRT4!DispatchToStubInCNoAvrf+0x18
08 00000016`b57ff400 00007fff`36da7a98     RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1a6
09 00000016`b57ff4e0 00007fff`36da8601     RPCRT4!RPC_INTERFACE::DispatchToStub+0xf8
0a 00000016`b57ff550 00007fff`36da795f     RPCRT4!OSF_SCALL::DispatchHelper+0x125
0b 00000016`b57ff670 00007fff`36da9265     RPCRT4!OSF_SCALL::DispatchRPCCall+0x7b
0c 00000016`b57ff6a0 00007fff`36da8f49     RPCRT4!OSF_SCALL::ProcessReceivedPDU+0x2b9
0d 00000016`b57ff770 00007fff`36dbc01f     RPCRT4!OSF_SCALL::BeginRpcCall+0xbd
0e 00000016`b57ff7a0 00007fff`36db6dbd     RPCRT4!OSF_SCONNECTION::ProcessReceiveComplete+0x5eb
0f 00000016`b57ff8a0 00007fff`36e45a04     RPCRT4!CO_ConnectionThreadPoolCallback+0xed
10 00000016`b57ff920 00007fff`37a21d19     RPCRT4!CO_RecvInlineCompletion+0x24
11 00000016`b57ff960 00007fff`379fd79a     ntdll!TppSimplepExecuteCallback+0x99
12 00000016`b57ff9b0 00007fff`36847374     ntdll!TppWorkerThread+0x68a
13 00000016`b57ffcb0 00007fff`379fcc91     KERNEL32!BaseThreadInitThunk+0x14
14 00000016`b57ffce0 00000000`00000000     ntdll!RtlUserThreadStart+0x21

【INFO】UseProtseqIfNeeded 发起的调用

UseProtseqIfNeeded 发起的调用信息是通过 CProcess::GetBindingHandleCopy 返回的 RPC_BINDING_HANDLE,这个接口可以看到调用客户端的 PID

image2025-2-12_14-40-6

相关调试信息如下所示,可以看到目标 PID 为 GodPotato:

image2025-2-12_14-44-38

UseProtseqIfNeed 被调用

由于通过 Marshal.WriteIntPtr(DispatchTablePtr, Marshal.GetFunctionPointerForDelegate(useProtseqDelegate)); 写入的 Hook 直接修改了RPC调用表(DispatchTable)中 UseProtseq 的地址,这一步会直接调用到如下所示的位置,最后跳转到写入的 delegatefun 接口中

image2025-2-12_14-57-42

delegatefun 接口中,写入了需要被连接的服务端信息:

  1. “ncacn_np:localhost/pipe/GodPotato[\pipe\epmapper]”
  2. “ncacn_ip_TCP:Second Call![135]”

相关调试信息如下所示:

0:008> kvn
 # Child-SP          RetAddr               : Args to Child                                                           : Call Site
00 00000000`1bc7eba0 00007ffb`ca73a2d3     : 00000000`00eec480 00000000`00000007 00000000`00000000 00000000`00eed790 : mscorwks!UMThunkStubAMD64+0x7a
01 00000000`1bc7ec40 00007ffb`ca6d25a7     : 00007ffb`c9b407e2 00000000`00eed5c0 00000000`00edb7a0 00000000`00000006 : RPCRT4!Invoke+0x73
02 00000000`1bc7ecb0 00007ffb`ca720c3a     : 00000000`00000000 00000000`00000000 00000000`00eec5d0 00000000`1bc7f038 : RPCRT4!NdrStubCall2+0x3f7
03 00000000`1bc7f000 00007ffb`ca71b128     : 00000000`00eed400 00000000`00000001 00000000`00000000 00007ffb`ca70d84d : RPCRT4!NdrServerCall2+0x1a
04 00000000`1bc7f030 00007ffb`ca6f8146     : 00000000`1bc7f094 00000000`1bc7f090 00000000`1bc7f230 ffffffff`fffffffb : RPCRT4!DispatchToStubInCNoAvrf+0x18
05 00000000`1bc7f080 00007ffb`ca6f7a98     : 00000000`00ee0d20 00000000`00000000 00000000`00000000 00000000`00000000 : RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1a6
06 00000000`1bc7f160 00007ffb`ca7050af     : 00000000`00000000 00000000`1bc7f2f8 00000000`00eec480 00000000`00ee0d20 : RPCRT4!RPC_INTERFACE::DispatchToStub+0xf8
07 00000000`1bc7f1d0 00007ffb`ca7044b8     : 00000000`0001241b 00000000`00000001 00000000`00000000 00000000`00ee3e90 : RPCRT4!LRPC_SCALL::DispatchRequest+0x31f
08 00000000`1bc7f2a0 00007ffb`ca703aa1     : 00000000`00e10000 00000000`00ee3bd0 00000000`00000000 00000000`00000000 : RPCRT4!LRPC_SCALL::HandleRequest+0x7f8
09 00000000`1bc7f3b0 00007ffb`ca70350e     : 00000000`00000000 00000000`00000000 00000000`00000001 00000000`00edc6b0 : RPCRT4!LRPC_ADDRESS::HandleRequest+0x341
0a 00000000`1bc7f450 00007ffb`ca707b62     : 00000000`00eed480 00000000`00edb7a0 00000000`00edc7b8 00000000`1bc7f828 : RPCRT4!LRPC_ADDRESS::ProcessIO+0x89e
0b 00000000`1bc7f590 00007ffb`caa50330     : 00000000`00000000 00000000`1bc7f638 00000000`1bc7f828 00000000`00000000 : RPCRT4!LrpcIoComplete+0xc2
0c 00000000`1bc7f630 00007ffb`caa7d566     : 00000000`00000000 00000000`00eca100 00000000`00000000 00000000`00edb760 : ntdll!TppAlpcpExecuteCallback+0x260
0d 00000000`1bc7f6b0 00007ffb`c8b97374     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!TppWorkerThread+0x456
0e 00000000`1bc7f9b0 00007ffb`caa7cc91     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
0f 00000000`1bc7f9e0 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
0:008> dq 00000000`1bc7ec40
00000000`1bc7ec40  00000000`00eec480 00000000`00000007
00000000`1bc7ec50  00000000`00000000 00000000`00eed790
00000000`1bc7ec60  00000000`00eed7a0 00000000`00eed7b0
00000000`1bc7ec70  00000000`00000000 00007ffb`c9b406d2
00000000`1bc7ec80  00000000`00eec5d0 00000000`1bc7f038
00000000`1bc7ec90  00000000`00edb7a0 00000000`00000000
00000000`1bc7eca0  00000000`00eec5d0 00007ffb`ca6d25a7
00000000`1bc7ecb0  00007ffb`c9b407e2 00000000`00eed5c0
0:008> dq 00000000`00eed7a0 l1
00000000`00eed7a0  00000000`00eeeb00
0:008> du 00000000`00eeeb00
00000000`00eeeb00  "OMncacn_np:localhost/pipe/GodPot"
00000000`00eeeb40  "ato[\pipe\epmapper]"
0:008> du 00000000`00eeeb68
00000000`00eeeb68  "ncacn_ip_tcp:Second Call!"
 
 
# 写入的内容如下所示:
00000000`00a462c0  4f 00 4d 00 6e 00 63 00-61 00 63 00 6e 00 5f 00  O.M.n.c.a.c.n._.
00000000`00a462d0  6e 00 70 00 3a 00 6c 00-6f 00 63 00 61 00 6c 00  n.p.:.l.o.c.a.l.
00000000`00a462e0  68 00 6f 00 73 00 74 00-2f 00 70 00 69 00 70 00  h.o.s.t./.p.i.p.
00000000`00a462f0  65 00 2f 00 47 00 6f 00-64 00 50 00 6f 00 74 00  e./.G.o.d.P.o.t.
00000000`00a46300  61 00 74 00 6f 00 5b 00-5c 00 70 00 69 00 70 00  a.t.o.[.\.p.i.p.
00000000`00a46310  65 00 5c 00 65 00 70 00-6d 00 61 00 70 00 70 00  e.\.e.p.m.a.p.p.
00000000`00a46320  65 00 72 00 5d 00 00 00-6e 00 63 00 61 00 63 00  e.r.]...n.c.a.c.
00000000`00a46330  6e 00 5f 00 69 00 70 00-5f 00 74 00 63 00 70 00  n._.i.p._.t.c.p.
00000000`00a46340  3a 00 53 00 65 00 63 00-6f 00 6e 00 64 00 20 00  :.S.e.c.o.n.d. .
00000000`00a46350  43 00 61 00 6c 00 6c 00-21 00 00 00 00 00 00 00  C.a.l.l.!.......

ResolveOxid2 如何确定要调用的服务端是哪个

在 UseProtseq 调用返回后,如下所示的堆栈,在 ResolveOxidCommon+0x180 位置调用了 rpcss!CServerOxid::GetRemoteInfo ,其中 r8 结构体为 __MIDL_ILocalObjectExporter_0007,查看 r8 就可以看到

00 000000c7`0f8fe8d0 00007ffb`c60d2943     rpcss!ResolveOxidCommon+0x180
01 000000c7`0f8fea70 00007ffb`ca73a2d3     rpcss!ResolveOxid2+0x173
02 000000c7`0f8feb00 00007ffb`ca6d25a7     RPCRT4!Invoke+0x73
 
 
# 查看调用的服务端
1: kd> dt __MIDL_ILocalObjectExporter_0007 @r8
combase!__MIDL_ILocalObjectExporter_0007
   +0x000 dwTid            : 0xffffffff
   +0x004 dwPid            : 0xbd4
   +0x008 dwAuthnHint      : 5
   +0x00c dcomVersion      : tagCOMVERSION
   +0x010 containerVersion : CONTAINERVERSION
   +0x028 ipidRemUnknown   : _GUID {0000fc01-0bd4-ffff-204e-8c9f56b06ce7}
   +0x038 dwFlags          : 0x2000000
   +0x040 psa              : (null)
   +0x048 guidProcessIdentifier : _GUID {25897763-3dc2-4432-bd48-9d5ae6f0c61c}
   +0x058 processHostId    : 0
   +0x060 clientDependencyBehavior : 0 ( OR_OXID_CLIENT_DEPENDENCY_NONE )
   +0x068 packageFullName  : (null)
   +0x070 userSid          : (null)
   +0x078 appcontainerSid  : (null)
   +0x080 primaryOxid      : 0xb0a1aef8`d63b5b01
   +0x088 primaryIpidRemUnknown : _GUID {0000fc01-0bd4-ffff-204e-8c9f56b06ce7}
1: kd> !process 0xbd4 0
Searching for Process with Cid == bd4
PROCESS ffffd1817db76080
    SessionId: 1  Cid: 0bd4    Peb: 0071e000  ParentCid: 1aa4
    DirBase: 104e66000  ObjectTable: ffffe18381c45700  HandleCount: 237.
    Image: GodPotato.exe

正常调用下 UsePortSeq 的返回

在不Hook情况下调用时,UseProtSeq 中通过 CopyDsaWithSecurityBindings 获取绑定的信息,如下所示:

image2025-2-17_17-46-26

返回内容如下所示:

image2025-2-17_17-48-3

GodPotato Hook 情况下的返回

ClientResolveOXID 在启用 Hook 情况下的调试。

在 GodPotato 查看发起的调用信息:

image2025-2-12_18-7-0

如上图所示,从 GodPotato 的 combase!ClientResolveOXID 开始,可以看到此时发起的请求信息,所请求的 rpc 接口为:{e60c73e6-88f9-11cf-9af1-0020af6e72f4}:5,该接口如下所示:

image2025-2-12_18-23-29

RPCSS 服务中 _ClientResolveOXID 的第一次调用

第一次调用时为了绑定 rpc ,让 rpcss 可以调用到 rpcss!ResolveOxid2,相关信息如下所示:

Protocol:00007ffb`c6132400  "ncacn_ip_tcp"
NetworkAddr:0000026a`9d33d476  "127.0.0.1"
Endpoint:00007ffb`c613c5a8  "135"
rax=000000c70f8fe3f0 rbx=0000026a9d535720 rcx=0000000000000000
rdx=00007ffbc6132400 rsi=00007ffbc6132400 rdi=00007ffbc613c5a8
rip=00007ffbc6106cd7 rsp=000000c70f8fe370 rbp=0000026a9d33d476
 r8=0000026a9d33d476  r9=00007ffbc613c5a8 r10=0000000000000000
r11=0000000000000246 r12=0000000000000000 r13=0000026a9cd0d570
r14=000000c70f8fe530 r15=843a9ba89403af3c
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
rpcss!CreateRemoteBindingToOr+0x6b:
0033:00007ffb`c6106cd7 48ff151a660200  call    qword ptr [rpcss!_imp_RpcStringBindingComposeW (00007ffb`c612d2f8)] ds:002b:00007ffb`c612d2f8={RPCRT4!RpcStringBindingComposeW (00007ffb`ca6e38d0)}
0: kd> k
 # Child-SP          RetAddr               Call Site
00 000000c7`0f8fe370 00007ffb`c60d65a8     rpcss!CreateRemoteBindingToOr+0x6b
01 000000c7`0f8fe3e0 00007ffb`c6072d65     rpcss!CMid::GetBinding+0x488
02 000000c7`0f8fe4c0 00007ffb`c606e451     rpcss!ResolveClientOXID+0xa05
03 000000c7`0f8fe9d0 00007ffb`ca73a2d3     rpcss!_ClientResolveOXID+0xe1
04 000000c7`0f8fea80 00007ffb`ca6d25a7     RPCRT4!Invoke+0x73
05 000000c7`0f8feb10 00007ffb`ca720c3a     RPCRT4!NdrStubCall2+0x3f7
06 000000c7`0f8fee60 00007ffb`ca71b128     RPCRT4!NdrServerCall2+0x1a
07 000000c7`0f8fee90 00007ffb`ca6f8146     RPCRT4!DispatchToStubInCNoAvrf+0x18
08 000000c7`0f8feee0 00007ffb`ca6f7a98     RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1a6
09 000000c7`0f8fefc0 00007ffb`ca7050af     RPCRT4!RPC_INTERFACE::DispatchToStub+0xf8
0a 000000c7`0f8ff030 00007ffb`ca7044b8     RPCRT4!LRPC_SCALL::DispatchRequest+0x31f
0b 000000c7`0f8ff100 00007ffb`ca703aa1     RPCRT4!LRPC_SCALL::HandleRequest+0x7f8
0c 000000c7`0f8ff210 00007ffb`ca70350e     RPCRT4!LRPC_ADDRESS::HandleRequest+0x341
0d 000000c7`0f8ff2b0 00007ffb`ca707b62     RPCRT4!LRPC_ADDRESS::ProcessIO+0x89e
0e 000000c7`0f8ff3f0 00007ffb`caa50330     RPCRT4!LrpcIoComplete+0xc2
0f 000000c7`0f8ff490 00007ffb`caa7d566     ntdll!TppAlpcpExecuteCallback+0x260
10 000000c7`0f8ff510 00007ffb`c8b97374     ntdll!TppWorkerThread+0x456
11 000000c7`0f8ff810 00007ffb`caa7cc91     KERNEL32!BaseThreadInitThunk+0x14
12 000000c7`0f8ff840 00000000`00000000     ntdll!RtlUserThreadStart+0x21

image2025-2-13_9-12-14

RPCSS 服务中 _ClientResolveOXID 的第二次调用
Protocol:00007ffb`c613c5d8  "ncacn_np"
NetworkAddr:000000c7`0f8fe262  "localhost/pipe/GodPotato"
Endpoint:00007ffb`c613c5f0  "\pipe\epmapper"
rax=000000c70f8fe250 rbx=000000c70f8fe260 rcx=0000000000000000
rdx=00007ffbc613c5d8 rsi=00007ffbc613c5d8 rdi=00007ffbc613c5f0
rip=00007ffbc6106cd7 rsp=000000c70f8fe1d0 rbp=000000c70f8fe262
 r8=000000c70f8fe262  r9=00007ffbc613c5f0 r10=000000007fffffd4
r11=0000000080070057 r12=0000000000000000 r13=0000000000000001
r14=0000026a9d2bf940 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
rpcss!CreateRemoteBindingToOr+0x6b:
0033:00007ffb`c6106cd7 48ff151a660200  call    qword ptr [rpcss!_imp_RpcStringBindingComposeW (00007ffb`c612d2f8)] ds:002b:00007ffb`c612d2f8={RPCRT4!RpcStringBindingComposeW (00007ffb`ca6e38d0)}
0: kd> k
 # Child-SP          RetAddr               Call Site
00 000000c7`0f8fe1d0 00007ffb`c60d60ea     rpcss!CreateRemoteBindingToOr+0x6b
01 000000c7`0f8fe240 00007ffb`c60c9d39     rpcss!TestBindingGetHandle+0x18a
02 000000c7`0f8fe2f0 00007ffb`c60d5938     rpcss!CTestBindingPPing::NextCall+0x29
03 000000c7`0f8fe340 00007ffb`c60b8b26     rpcss!CParallelPing::Ping+0x1b8
04 000000c7`0f8fe3b0 00007ffb`c6073a73     rpcss!CClientOxid::GetInfo+0x449e6
05 000000c7`0f8fe4c0 00007ffb`c606e451     rpcss!ResolveClientOXID+0x1713
06 000000c7`0f8fe9d0 00007ffb`ca73a2d3     rpcss!_ClientResolveOXID+0xe1
07 000000c7`0f8fea80 00007ffb`ca6d25a7     RPCRT4!Invoke+0x73
08 000000c7`0f8feb10 00007ffb`ca720c3a     RPCRT4!NdrStubCall2+0x3f7
09 000000c7`0f8fee60 00007ffb`ca71b128     RPCRT4!NdrServerCall2+0x1a
0a 000000c7`0f8fee90 00007ffb`ca6f8146     RPCRT4!DispatchToStubInCNoAvrf+0x18
0b 000000c7`0f8feee0 00007ffb`ca6f7a98     RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1a6
0c 000000c7`0f8fefc0 00007ffb`ca7050af     RPCRT4!RPC_INTERFACE::DispatchToStub+0xf8
0d 000000c7`0f8ff030 00007ffb`ca7044b8     RPCRT4!LRPC_SCALL::DispatchRequest+0x31f
0e 000000c7`0f8ff100 00007ffb`ca703aa1     RPCRT4!LRPC_SCALL::HandleRequest+0x7f8
0f 000000c7`0f8ff210 00007ffb`ca70350e     RPCRT4!LRPC_ADDRESS::HandleRequest+0x341
10 000000c7`0f8ff2b0 00007ffb`ca707b62     RPCRT4!LRPC_ADDRESS::ProcessIO+0x89e
11 000000c7`0f8ff3f0 00007ffb`caa50330     RPCRT4!LrpcIoComplete+0xc2
12 000000c7`0f8ff490 00007ffb`caa7d566     ntdll!TppAlpcpExecuteCallback+0x260
13 000000c7`0f8ff510 00007ffb`c8b97374     ntdll!TppWorkerThread+0x456
14 000000c7`0f8ff810 00007ffb`caa7cc91     KERNEL32!BaseThreadInitThunk+0x14
15 000000c7`0f8ff840 00000000`00000000     ntdll!RtlUserThreadStart+0x21

image2025-2-13_11-47-49

与第一次调用的区别

image2025-2-17_15-31-12

RPCSS 服务中 _ClientResolveOXID 的第三次调用

验证了下只有 GodPotato 内写入两条 EndPoint 时,才能触发成功,后续补充下为什么写一个 EndPoint 提权失败的情况

# 两次调用的堆栈是一样的
1: kd> k
 # Child-SP          RetAddr               Call Site
00 00000016`b5afe5d0 00007fff`330560ea     rpcss!CreateRemoteBindingToOr+0x6b
01 00000016`b5afe640 00007fff`33049d39     rpcss!TestBindingGetHandle+0x18a
02 00000016`b5afe6b0 00007fff`33055938     rpcss!CTestBindingPPing::NextCall+0x29
03 00000016`b5afe700 00007fff`33038b26     rpcss!CParallelPing::Ping+0x1b8
04 00000016`b5afe770 00007fff`32ff3a73     rpcss!CClientOxid::GetInfo+0x449e6
05 00000016`b5afe880 00007fff`32fee451     rpcss!ResolveClientOXID+0x1713
06 00000016`b5afed90 00007fff`36dea2d3     rpcss!_ClientResolveOXID+0xe1
07 00000016`b5afee40 00007fff`36d825a7     RPCRT4!Invoke+0x73
08 00000016`b5afeed0 00007fff`36dd0c3a     RPCRT4!NdrStubCall2+0x3f7
09 00000016`b5aff220 00007fff`36dcb128     RPCRT4!NdrServerCall2+0x1a
0a 00000016`b5aff250 00007fff`36da8146     RPCRT4!DispatchToStubInCNoAvrf+0x18
0b 00000016`b5aff2a0 00007fff`36da7a98     RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1a6
0c 00000016`b5aff380 00007fff`36db50af     RPCRT4!RPC_INTERFACE::DispatchToStub+0xf8
0d 00000016`b5aff3f0 00007fff`36db44b8     RPCRT4!LRPC_SCALL::DispatchRequest+0x31f
0e 00000016`b5aff4c0 00007fff`36db3aa1     RPCRT4!LRPC_SCALL::HandleRequest+0x7f8
0f 00000016`b5aff5d0 00007fff`36db350e     RPCRT4!LRPC_ADDRESS::HandleRequest+0x341
10 00000016`b5aff670 00007fff`36db7b62     RPCRT4!LRPC_ADDRESS::ProcessIO+0x89e
11 00000016`b5aff7b0 00007fff`379d0330     RPCRT4!LrpcIoComplete+0xc2
12 00000016`b5aff850 00007fff`379fd566     ntdll!TppAlpcpExecuteCallback+0x260
13 00000016`b5aff8d0 00007fff`36847374     ntdll!TppWorkerThread+0x456
14 00000016`b5affbd0 00007fff`379fcc91     KERNEL32!BaseThreadInitThunk+0x14
15 00000016`b5affc00 00000000`00000000     ntdll!RtlUserThreadStart+0x21
 
 
 
# RPC EndPoint 信息如下所示:
Protocol:00007fff`330b2400  "ncacn_ip_tcp"
NetworkAddr:00000016`b5afe662  "Second Call!"
Endpoint:00007fff`330bc5a8  "135"

与 hook 写入的第一个 endpoint 对比如下:

image2025-2-17_15-55-1

UseProtseq 函数及参数说明

相关参考:

  1. 字符串绑定 - Win32 apps | Microsoft Learn https://learn.microsoft.com/zh-cn/windows/win32/rpc/string-binding
  2. ncacn_np 属性 - Win32 apps | Microsoft Learn https://learn.microsoft.com/zh-cn/windows/win32/midl/ncacn-np
  3. 终结点属性 - Win32 apps | Microsoft Learn https://learn.microsoft.com/zh-cn/windows/win32/midl/endpoint

在 Hook 的 UseProtseq 函数中,主要修改了 ppdsaNewBindings 参数,写入了如下所示的两条 EndPoint 的相关信息:

  1. “ncacn_np:localhost/pipe/GodPotato[\pipe\epmapper]”
  2. “ncacn_ip_tcp:Second Call![135]“

EndPoint

[endpoint] 属性指定一个或多个已知端口, (通信终结点) 接口的服务器侦听呼叫。

endpoint(``"protocol-sequence:[endpoint-port]"` `[ , ...] )

ncacn_np

ncacn_np 关键字 (keyword) 将命名管道标识为终结点的协议系列。

endpoint(``"ncacn_np:server-name[\\pipe\\pipe-name]"``)

对于 “ncacn_np:localhost/pipe/GodPotato[\pipe\epmapper]” 形式的 EndPoint 来说,解析可以分为三个部分。分别是:

  • 协议:ncacn_np
  • 服务器名称:localhost/pipe/GodPotato
  • 管道名:\pipe\pipe-name

为什么服务器名称是 localhost/pipe/GodPotato ?

因为要连接的目标对象是本地(localhost)的服务端管道,填写本地IP也行,但是不能为空。

public` `readonly string serverPipe = $``"\\\\.\\pipe\\{"``GodPotato``"}\\pipe\\epmapper"``;

通过这个管道,GodPotato 才能通过模拟来获取高权限令牌

\pipe\pipe-name 为管道信息

image2025-2-18_17-51-17

总体流程如下所示:

flowchart TD
	A[GodPotato:CoUnmarshalInterface] --> B
	B[GodPotato:ClientResolveOXID2] -- RPC调用 --> C
	C[rpcss服务中的rpcss!ClientResolveOXID2] -- "绑定 ncacn_ip_tcp:127.0.0.1[135]" --> D
	D[rpcss!ResolveOxid2] --> E
	E[rpcss!CProcess::UseProtseqIfNeeded] -- 获取 EndPoint 信息 --> F
	F[GodPotato!HookFun->combase!UseProtseq] -- 返回自己创建的 EndPoint --> G
	G[rpcss:ClientResolveOXID2] -- 解析返回的第一个 EndPoint --> H
	H[rpcss:ClientResolveOXID2] -- 解析返回的第二个 EndPoint --> I
	I[连接到 GodPotato 中创建的服务端]

UseProtseq 断点

dt /v
# 或者
dx -r1 Debugger.State.Scripts.CodeFlow.Contents.host.currentThread.Stack.Frames[0]

主要关注参数 ppsaNewBindings 就行

image-20250113163645797

代码中对 ppsaNewBindings 的使用就只有 CopyStringArray

image-20250113163811219

call combase!CopyStringArray 处添加断点,然后看执行完成后的内容就行。单步跳过,查看写入的内容

image-20250113164001427

可以看到其写入的内容由三部分,WNameEntrieswSecurityOffsetaStringArray

  • WNameEntries: aStringArray 的长度
  • wSecurityOffsetUseProtseq 的最后一个参数的长度

其中 aStringArray 的内容为 ncalrpc:[OLE9EC507FE2C47565F9B30D6BC6C29]。而 GodPotato 则修改了这一部分逻辑,写入的 ncacn_np:localhost/pipe/GodPotato[\pipe\epmapper] 调试截图

获取参数个数

typedef struct
{
    unsigned char   FullPtrUsed : 1;
    unsigned char   RpcSsAllocUsed : 1;
    unsigned char   ObjectProc : 1;
    unsigned char   HasRpcFlags : 1;
    unsigned char   IgnoreObjectException : 1;
    unsigned char   HasCommOrFault : 1;
    unsigned char   UseNewInitRoutines : 1;
    unsigned char   Unused : 1;
} INTERPRETER_FLAGS, * PINTERPRETER_FLAGS;
 
typedef struct
{
    unsigned char   ServerMustSize : 1;
    unsigned char   ClientMustSize : 1;
    unsigned char   HasReturn : 1;
    unsigned char   HasPipes : 1;
    unsigned char   Unused : 1;
    unsigned char   HasAsyncUuid : 1;
    unsigned char   HasExtensions : 1;
    unsigned char   HasAsyncHandle : 1;
} INTERPRETER_OPT_FLAGS, * PINTERPRETER_OPT_FLAGS;
 
typedef struct _NDR_DCOM_OI2_PROC_HEADER
{
    unsigned char               HandleType;        // The Oi header
    INTERPRETER_FLAGS           OldOiFlags;        //
    unsigned short              RpcFlagsLow;       //
    unsigned short              RpcFlagsHi;        //
    unsigned short              ProcNum;           //
    unsigned short              StackSize;         //
    // expl handle descr is never generated        //
    unsigned short              ClientBufferSize;  // The Oi2 header
    unsigned short              ServerBufferSize;  //
    INTERPRETER_OPT_FLAGS       Oi2Flags;          //
    unsigned char               NumberParams;      //
} NDR_DCOM_OI2_PROC_HEADER, * PNDR_DCOM_OI2_PROC_HEADER;

RPC结构体解析

这里我参考 GodPotato 的 [[【.Net】index|C#]] 版本实现了 C++ 版本的解析。同时尽可能的完善了部分细节以便于学习了解相关结构体。

链接:holdyounger/DecodeRPCStruct: Decode RPC_SERVER_INTERFACE Struct,take combase.dll as an example

对于 ProcString 字段的解析参考了 rpcrt4!MulNdrpInitializeContextFromProc 函数。

对应代码的 PE 文件可以使用 https://github.com/holdyounger/DecodeRPCStruct/actions/runs/12923652850

GodPotato 管道建立调试

记录下管道连接过程

  1. 通过 NMP_CreateFile 连接 GodPotato 的管道

    bp RPCRT4!NMP_CreateFile "du @rcx"
    
  2. 调用 IopCreateFile

    bp nt!IopCreateFile "dt _OBJECT_ATTRIBUTES @r8;.thread"
    
    
    #调用了两次
    1. \??\UNC\localhost\pipe\GodPotato\pipe\epmapper·
    2. \Device\NamedPipe\GodPotato\pipe\epmapper
    

    image-20250226124255575