10月末时打算写篇DCE/MS RPC的科普类文章,大致介绍一下涉及的协议层次,然后提 供一批CAP文件,让读者有个感性认识。计划面向的读者群是网管和协议分析爱好者, 非DCE/MS RPC协议开发人员、非DCE/MS RPC漏洞分析人员。 筹备了一番,主要是抓最简版的CAP文件,然后写一些上下文相关的介绍文字。结果 发现这个科普类文章扯大了,就是说涉及面太多,出现很多当初没预料到的细节问题。 更郁闷的是,原以为熟得一塌糊涂的东西出现了新变化。一连串的打击后,没法再写 成科普类文章了,只能按纯技术文档写下去。 但是,这个东西要按纯技术文档写,就得耗些日子,每一处细节与新变化都需要实践、 理论、再实践。而且还得尽力用自然语言去解释清楚。最后折衷一下,将一些相对独 立的小节抽出来,写一段发一段。不定期更新、追加内容。 灌水原则一向是"要么不提,提了就讲清楚"。前半句属于人在江湖的事,谁都会碰上, 毕竟不是学生时代了。后半句当然是对"炫"的烦躁使然。因此本文的外发文字绝不出 现让人看了心痒难耐继而不爽的内容,力求让读者看了之后有所收获,最终忘了TA本 来想问候我母亲来着。 一下子由科普类转换成纯技术类文章,内容太多,提纲只好完工时后补了。最终会提 供一个"SMB系列(37)--DCE/MS RPC旁窥"的RAR文件供下载。起名"旁窥",概因重点在 DCE/MS RPC协议的正常封装、解码上,至于"序列化/反序列化"(marshal/unmarshal)、 IDL文件逆向还原等等,不在本文范畴。 当然,不会忘记初衷--科普。会在技术细节逐一展现后,回过头来提纲挈领式地介绍 涉及的协议层次,澄清某些人混淆着的一些概念,以此指引那些刚接触DCE/MS RPC协 议的爱好者设置合适的过滤规则、较快地看明白别人提供的CAP文件,能在出现蠕虫 后自行分析如何报警(这个要求稍高点)。 文字版中为了清晰起见,扔掉了不必要的协议层次,但SMB_37_*.cap未做马赛格处理。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "BIND Over TCP"简介 一个最简单、常见的Bind_ack报文的例子如下(SMB_37_0.cap): -------------------------------------------------------------------------- Transmission Control Protocol, Src Port: 4166 (4166), Dst Port: 135 (135), Len: 72 DCE RPC Bind, Fragment: Single, FragLen: 72, Call: 1 Version: 5 Version (minor): 0 Packet type: Bind (11) Packet Flags: 0x03 0... .... = Object: Not set .0.. .... = Maybe: Not set ..0. .... = Did Not Execute: Not set ...0 .... = Multiplex: Not set .... 0... = Reserved: Not set .... .0.. = Cancel Pending: Not set .... ..1. = Last Frag: Set .... ...1 = First Frag: Set Data Representation: 10000000 Byte order: Little-endian (1) Character: ASCII (0) Floating-point: IEEE (0) Frag Length: 72 Auth Length: 0 Call ID: 1 Max Xmit Frag: 5840 Max Recv Frag: 5840 Assoc Group: 0x00000000 Num Ctx Items: 1 Context ID: 0 Num Trans Items: 1 Interface UUID: e1af8308-5d1f-11c9-91a4-08002b14a0fa Interface Ver: 3 Interface Ver Minor: 0 Transfer Syntax: 8a885d04-1ceb-11c9-9fe8-08002b104860 Syntax ver: 2 0030 05 00 0b 03 10 00 00 00 48 00 ........H. 0040 00 00 01 00 00 00 d0 16 d0 16 00 00 00 00 01 00 ................ 0050 00 00 00 00 01 00 08 83 af e1 1f 5d c9 11 91 a4 ...........].... 0060 08 00 2b 14 a0 fa 03 00 00 00 04 5d 88 8a eb 1c ..+........].... 0070 c9 11 9f e8 08 00 2b 10 48 60 02 00 00 00 ......+.H`.... Transmission Control Protocol, Src Port: 135 (135), Dst Port: 4166 (4166), Len: 60 DCE RPC Bind_ack, Fragment: Single, FragLen: 60, Call: 1 Version: 5 Version (minor): 0 Packet type: Bind_ack (12) Packet Flags: 0x03 0... .... = Object: Not set .0.. .... = Maybe: Not set ..0. .... = Did Not Execute: Not set ...0 .... = Multiplex: Not set .... 0... = Reserved: Not set .... .0.. = Cancel Pending: Not set .... ..1. = Last Frag: Set .... ...1 = First Frag: Set Data Representation: 10000000 Byte order: Little-endian (1) Character: ASCII (0) Floating-point: IEEE (0) Frag Length: 60 Auth Length: 0 Call ID: 1 Max Xmit Frag: 5840 Max Recv Frag: 5840 Assoc Group: 0x00012bee Scndry Addr len: 4 Scndry Addr: 135 Num results: 1 Ack result: Acceptance (0) Transfer Syntax: 8a885d04-1ceb-11c9-9fe8-08002b104860 Syntax ver: 2 0030 05 00 0c 03 10 00 00 00 3c 00 ........<. 0040 00 00 01 00 00 00 d0 16 d0 16 ee 2b 01 00 04 00 ...........+.... 0050 31 33 35 00 00 00 01 00 00 00 00 00 00 00 04 5d 135............] 0060 88 8a eb 1c c9 11 9f e8 08 00 2b 10 48 60 02 00 ..........+.H`.. 0070 00 00 .. -------------------------------------------------------------------------- 试图绑定DCE/MS RPC Endpoint Mapper Interface UUID时引发上述报文。 Auth Length一般情况下为0,但并非总为0。关于这个字段,参看MSDN中如下函数: RpcServerRegisterAuthInfo RpcBindingInqAuthClient RpcBindingSetAuthInfo Auth Length为0的情况下,Bind(11)报文的RPC层大小固定为72。 Bind_ack(12)报文的RPC层大小变动较大,一是受Auth Length的影响,二是受Scndry Addr的影响,Scndry Addr后面的Num results要求对齐在四字节边界上。当Scndry Addr对应字符串表示的端口号时,Scndry Addr len最大等于6,即"65535"所占字节 数,包括结尾的NUL字符。由于Num results四字节对齐的缘故,"65535"不会比"135" 多占任何字节,因此当Scndry Addr对应字符串表示的端口号时,Auth Length为0的 情况下,Bind_ack(12)报文的RPC层大小固定为60。这种情形很常见,ncacn_ip_tcp 协议序列对应的BIND操作多半是这种情形,换句话说,BIND Over TCP多半是这种情 形。但是,ncacn_np协议序列对应的BIND操作就不是这种情形,后面再介绍。 收到Bind_ack(12)报文并不意味着BIND操作成功,要检查Ack result字段: 0 Acceptance 2 Provider rejection 应该还有其它值,但那不重要。解析Bind_ack报文时,务必判断Ack result字段是否 等于Acceptance(0),此时意味着BIND操作成功。以前一直以为收到Bind_nak(13)报 文才意味着BIND操作失败,不想近日做实验时意外地发现结论错误。 一个解码陷阱源于Ack result在Scndry Addr之后。ncacn_ip_tcp协议序列下Ack result在RPC层的偏移可以认为是固定的+0x024,ncacn_np协议序列下这个偏移就变 了。可移植的解决方案是无论哪种协议序列,先取Scndry Addr len,再考虑四字节 对齐的事,动态计算Ack result的偏移。这个方案不受Auth Length的影响,认证相 关的数据位于尾部。另一个方案是先获取Auth Length,并确保收到的是完整的非畸 型的Bind_ack报文,然后从尾部倒推偏移-Auth Length-0x018。这个方案并不比前一 个方案更有优势,看个人喜好了。为此修正了一批早期编写的代码。 下面是一个Provider rejection(2)的例子(SMB_37_1.cap): -------------------------------------------------------------------------- Transmission Control Protocol, Src Port: 60367 (60367), Dst Port: 135 (135), Len: 72 DCE RPC Bind, Fragment: Single, FragLen: 72, Call: 1 Version: 5 Version (minor): 0 Packet type: Bind (11) Packet Flags: 0x03 0... .... = Object: Not set .0.. .... = Maybe: Not set ..0. .... = Did Not Execute: Not set ...0 .... = Multiplex: Not set .... 0... = Reserved: Not set .... .0.. = Cancel Pending: Not set .... ..1. = Last Frag: Set .... ...1 = First Frag: Set Data Representation: 10000000 Byte order: Little-endian (1) Character: ASCII (0) Floating-point: IEEE (0) Frag Length: 72 Auth Length: 0 Call ID: 1 Max Xmit Frag: 4280 Max Recv Frag: 4280 Assoc Group: 0x00000000 Num Ctx Items: 1 Context ID: 0 Num Trans Items: 1 Interface UUID: ffffffff-ffff-ffff-ffff-ffffffffffff Interface Ver: 0 Interface Ver Minor: 0 Transfer Syntax: 8a885d04-1ceb-11c9-9fe8-08002b104860 Syntax ver: 2 0040 05 00 0b 03 10 00 00 00 48 00 00 00 01 00 ........H..... 0050 00 00 b8 10 b8 10 00 00 00 00 01 00 00 00 00 00 ................ 0060 01 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................ 0070 ff ff 00 00 00 00 04 5d 88 8a eb 1c c9 11 9f e8 .......]........ 0080 08 00 2b 10 48 60 02 00 00 00 ..+.H`.... Transmission Control Protocol, Src Port: 135 (135), Dst Port: 60367 (60367), Len: 60 DCE RPC Bind_ack, Fragment: Single, FragLen: 60, Call: 1 Version: 5 Version (minor): 0 Packet type: Bind_ack (12) Packet Flags: 0x03 0... .... = Object: Not set .0.. .... = Maybe: Not set ..0. .... = Did Not Execute: Not set ...0 .... = Multiplex: Not set .... 0... = Reserved: Not set .... .0.. = Cancel Pending: Not set .... ..1. = Last Frag: Set .... ...1 = First Frag: Set Data Representation: 10000000 Byte order: Little-endian (1) Character: ASCII (0) Floating-point: IEEE (0) Frag Length: 60 Auth Length: 0 Call ID: 1 Max Xmit Frag: 4280 Max Recv Frag: 4280 Assoc Group: 0x00012bf6 Scndry Addr len: 4 Scndry Addr: 135 Num results: 1 Ack result: Provider rejection (2) Ack reason: Abstract syntax not supported (1) Transfer Syntax: 00000000-0000-0000-0000-000000000000 Syntax ver: 0 0040 05 00 0c 03 10 00 00 00 3c 00 00 00 01 00 ........<..... 0050 00 00 b8 10 b8 10 f6 2b 01 00 04 00 31 33 35 00 .......+....135. 0060 00 00 01 00 00 00 02 00 01 00 00 00 00 00 00 00 ................ 0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .............. -------------------------------------------------------------------------- 此次Ack result等于Provider rejection(2)时,Ack reason字段开始有意义,另一 明显变化是Transfer Syntax字段。 Bind_ack(12)报文的RPC层大小与协议序列有关,但与Ack result、Ack reason字段 无关,这是两个短整型,始终占去4字节。因此上述Bind_ack(12)报文的RPC层大小仍 等于60。 向135/TCP发送Bind报文试图绑定不存在的接口UUID,就引发出如上Bind_ack报文。 这是一次实验意外出错后的结果,现实环境中这样的报文相当罕见,折腾DCE/MS RPC 这么久,还是第一次看到,为此修正了一批早期编写的代码。