[原创]CVE-2015-2370之DCOM DCE/RPC协议原理详细分析
漏洞成因分析
这个CVE-2015-2370漏洞是一种DCOM DCE/RPC协议中ntlm认证后数据包重放导致的权限提升漏洞
分析的重点DCOM DCE/RPC协议原理,这个协议主要由2块内容组成,dcom的远程激活机制和ntlm身份认证
1.dcom的远程激活机制
微软官方解释
有一个运行在135端口的rpcss服务也就是dcom的激活服务负责协调本机所有com对象的激活,当本机激活时采用通过内核通信,无法捕获数据包,属于内部操作,只有当远程激活或远程重定向到本机激活这种方式才可以捕获到数据包.远程激活有2种方式,一种是采用CoCreateInstanceEx方式指定远程服务器和激活身份等参数调用rpscss的IRemoteSCMActivator接口的RemoteCreateInstance方法激活,或者CoGetClassObject =调用rpscss的IRemoteSCMActivator接口的RemoteGetClassObject方法激活同样可选指定远程服务器和激活身份等参数.还有一种方式方式是客户端marshal服务端unmarshal方式,在marshal的stream中写入OBJREF通过其中的DUALSTRINGARRAY字段指定远程解析的服务器或端口,远程服务器rpcss服务采用IObjectExporter接口中的ResolveOxid或ResolveOxid2方法实现反序列化出来需要unmarshal的远程com对象remunkown指针.CVE-2015-2370是通过在ntlm身份认证后在ResolveOxid2之后又重放了一个RemoteCreateInstance请求导致以客户端的高权限创建出来一个OLE Packager的文件实现了权限提升.用户也可以创建一个rpc服务实现自己实现这2个接口自定义的rpcss解析和激活服务.这2个模块的可以在rpcss服务加载的rpcss.dll中找到具体实现,以下是接口定义:
[
uuid(99fcfec4-5260-101b-bbcb-00aa0021347a),
pointer_default(unique)
]
//marshal方式
interface IObjectExporter
{
[idempotent] error_status_t ResolveOxid
(
[in] handle_t hRpc,
[in] OXID *pOxid,
[in] unsigned short cRequestedProtseqs,
[in, ref, size_is(cRequestedProtseqs)]
unsigned short arRequestedProtseqs[],
[out, ref] DUALSTRINGARRAY **ppdsaOxidBindings,
[out, ref] IPID *pipidRemUnknown,
[out, ref] DWORD *pAuthnHint
);
[idempotent] error_status_t SimplePing
(
[in] handle_t hRpc,
[in] SETID *pSetId
);
[idempotent] error_status_t ComplexPing
(
[in] handle_t hRpc,
[in, out] SETID *pSetId,
[in] unsigned short SequenceNum,
[in] unsigned short cAddToSet,
[in] unsigned short cDelFromSet,
[in, unique, size_is(cAddToSet)] OID AddToSet[],
[in, unique, size_is(cDelFromSet)] OID DelFromSet[],
[out] unsigned short *pPingBackoffFactor
);
[idempotent] error_status_t ServerAlive
(
[in] handle_t hRpc
);
[idempotent] error_status_t ResolveOxid2
(
[in] handle_t hRpc,
[in] OXID *pOxid,
[in] unsigned short cRequestedProtseqs,
[in, ref, size_is(cRequestedProtseqs)]
unsigned short arRequestedProtseqs[],
[out, ref] DUALSTRINGARRAY **ppdsaOxidBindings,
[out, ref] IPID *pipidRemUnknown,
[out, ref] DWORD *pAuthnHint,
[out, ref] COMVERSION *pComVersion
);
[idempotent] error_status_t ServerAlive2
(
[in] handle_t hRpc,
[out, ref] COMVERSION *pComVersion,
[out, ref] DUALSTRINGARRAY **ppdsaOrBindings,
[out, ref] DWORD *pReserved
);
}
[
uuid(000001A0-0000-0000-C000-000000000046),
pointer_default(unique)
]
//CoCreateInstanceEx方式
interface IRemoteSCMActivator
{
void Opnum0NotUsedOnWire(void);
void Opnum1NotUsedOnWire(void);
void Opnum2NotUsedOnWire(void);
HRESULT RemoteGetClassObject(
[in] handle_t rpc,
[in] ORPCTHIS *orpcthis,
[out] ORPCTHAT *orpcthat,
[in,unique] MInterfacePointer *pActProperties,
[out] MInterfacePointer **ppActProperties
);
HRESULT RemoteCreateInstance(
[in] handle_t rpc,
[in] ORPCTHIS *orpcthis,
[out] ORPCTHAT *orpcthat,
[in,unique] MInterfacePointer *pUnkOuter,
[in,unique] MInterfacePointer *pActProperties,
[out] MInterfacePointer **ppActProperties
);
}
2.ntlm身份认证机制分析
ntlm官方解释
CVE-2015-2370采用CoGetInstanceFromIStorage方式触发服务器从IStorage自身实现的IMarhal接口的MarshalInterface方法往stream中写入marshaldata
HRESULT CoGetInstanceFromIStorage( COSERVERINFO *pServerInfo, CLSID *pClsid, IUnknown *punkOuter, DWORD dwClsCtx, IStorage *pstg, DWORD dwCount, MULTI_QI *pResults );
marshaldata是一个OBJREF可以通过如下脚本使用010editor解析
local unsigned short sizetp;
struct tagOBJREF {
byte signature[4];
unsigned long flags;
struct iid
{
unsigned int Data1;
unsigned ushort Data2;
unsigned ushort Data3;
byte Data4[8];
} _iid;
if(OBJREF.flags==01h)
{
struct tagOBJREF_standard {
unsigned long flags;
unsigned long cPublicRefs;
struct oxid {
DWORD LowPart;
LONG HighPart;
} _oxid;
struct oid {
DWORD LowPart;
LONG HighPart;
} _oid;
struct ipid
{
unsigned int Data1;
unsigned ushort Data2;
unsigned ushort Data3;
byte Data4[8];
} _ipid;
struct tagDUALSTRINGARRAY {
unsigned short wNumEntries;
Printf("wNumEntries is %d",sizetp);
unsigned short wSecurityOffset;
sizetp=wSecurityOffset-2;
struct tagSTRINGBINDING {
unsigned short wTowerId;
unsigned short aNetworkAddr[sizetp];
} STRINGBINDING;
byte nullterm1[2];
struct tagSECURITYBINDING {
unsigned short wAuthnSvc; // Must not be zero
unsigned short wAuthzSvc; // Must not be zero
unsigned short aPrincName; // NULL terminated
} SECURITYBINDING;
byte nullterm2[2];
} dualstringarray;
} OBJREF_standard;
}
if(OBJREF.flags==02h)
{
struct tagOBJREF_handler {
unsigned char std[40];
struct clsid
{
unsigned int Data1;
unsigned ushort Data2;
unsigned ushort Data3;
byte Data4[8];
} _clsid;
unsigned char saResAddr[8];
} OBJREF_handler;
}
if(OBJREF.flags==04h)
{
struct tagOBJREF_custom {
struct clsid_custom
{
unsigned int Data1;
unsigned ushort Data2;
unsigned ushort Data3;
byte Data4[8];
} _clsid_custom;
unsigned long cbExtension;
unsigned long size;
unsigned byte pData;
} OBJREF_custom;
}
if(OBJREF.flags==08h)
{
unsigned byte std[40];
unsigned byte pORData[4];
unsigned byte saResAddr[12];
}
} OBJREF;
结果是一个standard的matshal模式,其中的DUALSTRINGARRAY字段指定远程解析的服务器为127.0.0.1的6666端口,也就是我们要使用中间人攻击监听端口,如下图:[图片:o6MNUt4.png]
127.0.0.1的6666监听的数据包经过中转后最终发送至135端口的rpcss服务,服务端先进行ServerAlive进行服务器时候在线确认,之后进行ntlm身份认证.
注意:上传附件及图片大小不得大于30M。
⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。
