概述:在 GodPotato原理分析 文中,描述了 GodPotato 的大致逻辑,但是对核心的处理操作部分没有处理,也就是——RPCSS 的接口 Hook 部分,本文就这一部分进行记录。
调试环境
前文:GodPotato原理分析
调试
代码部分我们已经了解了,那么调试部分就比较简单了,双机调试挂载目标进程,在 combase!_UseProtseq 处下断点即可。当然,整体的流程可以结合 IDA 反汇编的代码查看。
UseProtseq 函数原型
⚠️ 注意 UseProtseq 在不同版本下的参数可能不同
1 2 3 4 5 6
| __int64 __fastcall UseProtseq( void *hRpc, unsigned __int16 wTowerId, unsigned int *pdwTCPPort, tagDUALSTRINGARRAY **ppsaNewBindings, tagDUALSTRINGARRAY **ppsaSecurity)
|
调试准备
-
添加进程断点
1 2
| !gflag +ksl; sxe cpr:GodPotato.exe
|
-
查看被调用接口
这个断点需要添加在 rpcss 服务中
1
| bu RPCRT4!Invoke+0x73-2d ".echo \"RPCRT4!Invoke Call:\";u r10 l5;gc"
|
或者我们也可以在被调试程序(GodPotato)中添加 RPCRT4!NdrClientCall2 断点查看 RPC 都调用了那些接口:
1
| bu RPCRT4!NdrClientCall2 "k"
|
-
查看 GodPotato Hook 接口
触发断点
在 GodPotato 源码中,调用 unmarshalTrigger.Trigger() 就触发相关断点了(最终调用的是 combase!CoUnmarshalInterface)。
ClientResolveOXID 断点
CoUnmarshalInterface 接口调用时,使用了正常申请的 OXID 来定位和访问服务器上的对象,这一步 RPC 操作是验证 OXID,GodPotato 发起的堆栈如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| # 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 2 3 4 5 6 7 8 9 10
| 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 调用到了 GodPotato 中combase!UseProtseq
调用堆栈如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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
相关调试信息如下所示,可以看到目标 PID 为 GodPotato:
UseProtseqIfNeed 被调用
由于通过 Marshal.WriteIntPtr(DispatchTablePtr, Marshal.GetFunctionPointerForDelegate(useProtseqDelegate)); 写入的 Hook 直接修改了RPC调用表(DispatchTable)中 UseProtseq 的地址,这一步会直接调用到如下所示的位置,最后跳转到写入的 delegatefun 接口中
在 delegatefun 接口中,写入了需要被连接的服务端信息:
- “ncacn_np:localhost/pipe/GodPotato[\pipe\epmapper]”
- “ncacn_ip_tcp:Second Call![135]”
相关调试信息如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| 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 就可以看到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| 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 获取绑定的信息,如下所示:
返回内容如下所示:
GodPotato Hook 情况下的返回
ClientResolveOXID 在启用 Hook 情况下的调试。
在 GodPotato 查看发起的调用信息:
如上图所示,从 GodPotato 的 combase!ClientResolveOXID 开始,可以看到此时发起的请求信息,所请求的 rpc 接口为:{e60c73e6-88f9-11cf-9af1-0020af6e72f4}:5,该接口如下所示:
RPCSS 服务中 _ClientResolveOXID 的第一次调用
第一次调用时为了绑定 rpc ,让 rpcss 可以调用到 rpcss!ResolveOxid2,相关信息如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 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
|
RPCSS 服务中 _ClientResolveOXID 的第二次调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 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
|
与第一次调用的区别
RPCSS 服务中 _ClientResolveOXID 的第三次调用
验证了下只有 GodPotato 内写入两条 EndPoint 时,才能触发成功,后续补充下为什么写一个 EndPoint 提权失败的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| # 两次调用的堆栈是一样的 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 对比如下:
UseProtseq 函数及参数说明
相关参考:
- 字符串绑定 - Win32 apps | Microsoft Learn
https://learn.microsoft.com/zh-cn/windows/win32/rpc/string-binding
- ncacn_np 属性 - Win32 apps | Microsoft Learn
https://learn.microsoft.com/zh-cn/windows/win32/midl/ncacn-np
- 终结点属性 - Win32 apps | Microsoft Learn
https://learn.microsoft.com/zh-cn/windows/win32/midl/endpoint
在 Hook 的 UseProtseq 函数中,主要修改了 ppdsaNewBindings 参数,写入了如下所示的两条 EndPoint 的相关信息:
- “ncacn_np:localhost/pipe/GodPotato[\pipe\epmapper]”
- “ncacn_ip_tcp:Second Call![135]”
EndPoint
[endpoint] 属性指定一个或多个已知端口, (通信终结点) 接口的服务器侦听呼叫。
1
| endpoint(``"protocol-sequence:[endpoint-port]"` `[ , ...] )
|
ncacn_np
ncacn_np 关键字 (keyword) 将命名管道标识为终结点的协议系列。
1
| 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也行,但是不能为空。
1
| public` `readonly string serverPipe = $``"\\\\.\\pipe\\{"``GodPotato``"}\\pipe\\epmapper"``;
|
通过这个管道,GodPotato 才能通过模拟来获取高权限令牌。
\pipe\pipe-name 为管道信息
总体流程如下所示:
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 断点
1 2 3
| dt /v # 或者 dx -r1 Debugger.State.Scripts.CodeFlow.Contents.host.currentThread.Stack.Frames[0]
|
主要关注参数 ppsaNewBindings 就行
代码中对 ppsaNewBindings 的使用就只有 CopyStringArray
在 call combase!CopyStringArray 处添加断点,然后看执行完成后的内容就行。单步跳过,查看写入的内容
可以看到其写入的内容由三部分,WNameEntries、wSecurityOffset、aStringArray
WNameEntries: aStringArray 的长度
wSecurityOffset:UseProtseq 的最后一个参数的长度
其中 aStringArray 的内容为 ncalrpc:[OLE9EC507FE2C47565F9B30D6BC6C29]。而 GodPotato 则修改了这一部分逻辑,写入的 ncacn_np:localhost/pipe/GodPotato[\pipe\epmapper]
获取参数个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 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; INTERPRETER_FLAGS OldOiFlags; unsigned short RpcFlagsLow; unsigned short RpcFlagsHi; unsigned short ProcNum; unsigned short StackSize; unsigned short ClientBufferSize; unsigned short ServerBufferSize; INTERPRETER_OPT_FLAGS Oi2Flags; unsigned char NumberParams; } NDR_DCOM_OI2_PROC_HEADER, * PNDR_DCOM_OI2_PROC_HEADER;
|
RPC结构体解析
这里我参考 GodPotato 的 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 管道建立调试
记录下管道连接过程
-
通过 NMP_CreateFile 连接 GodPotato 的管道
1
| bp RPCRT4!NMP_CreateFile "du @rcx"
|
-
调用 IopCreateFile
1 2 3 4 5 6
| bp nt!IopCreateFile "dt _OBJECT_ATTRIBUTES @r8;.thread"
#调用了两次 1. \??\UNC\localhost\pipe\GodPotato\pipe\epmapper· 2. \Device\NamedPipe\GodPotato\pipe\epmapper
|