安全社区
黑盾云安全社区,与你一起分享安全领域的知识与智慧
黑盾云安全社区,与你一起分享安全领域的知识与智慧
安全研究2014-11-20 10:15:21 4745次围观
一、背景
Windows安全通道(Secure Channel,Schannel)惊报漏洞,微软日前发布列为重大等级的MS14-066信息安全公告,揭露了在Schannel中的远程代码执行漏洞(CVE-2014-6321)。
受影响的微软产品包括:
个人端操作系统:Windows VistaSP2、Windows 7 SP1、Windows 8到Windows 8.1服务器操作系统:Windows Server 2003 SP2、2008 SP2、2008 R2 SP1、2012到2012 R2微软平板:Windows RT和Windows RT 8.1。
黑客可以构造特定的数据包在Schannel中远程执行恶意代码,并藉此漏洞入侵系统。因为Schannel是Windows实现SSL/TLS协议的组件之一,所以CVE-2014-6321漏洞就像是OpenSSL中的Heartbleed漏洞,危害巨大。
思科Talos团队表示,MS14-066涉及一个常见漏洞揭露CVE-2014-6321。虽然该漏洞是单一的CVE,但是实际上却包含了多个漏洞,范围广从缓冲区到凭证验证。
Rapid7资深信息安全工程经理Ross Barrett表示,目前CVE-2014-6321漏洞的影响还不大,但是如果攻击代码泄漏,该漏洞将会成为严重的问题,且影响会扩大。
目前,微软也尚未接到有关该漏洞已公开用来攻击用户的消息,而微软在官网也表示,此安全更新可以更正Schannel清理特定封包的方式,进而消除此项安全风险。
二、探究
网上关于CVE-2014-6321漏洞的详情与利用方法鲜有披露。我们通过新发布的补丁来一探究竟。
通过对打补丁前后的schannle.dll文件进行二进制比较,可查看到一些修改过的函数,其中几个是类DTLSCookieManager的方 法。由此表明了至少有一个bug位于DTLSCookieManager类中。而大家最关心的bug好像是位于 schannel!DecodeSigAndReverse(…)中,DecodeSigAndReverse函数的变更如下所示。
由从上图可清楚的看到DecodeSigAndReverse函数中间添加了一些新的代码块(图右侧中的灰色模块)。新添分支往往是一个好兆头!如果我们放大打补丁的版本(图右侧),情况似乎更符合预期。
上图可看到新添代码块把运行路径指向了memcpy调用(实际上是两次memecpy函数调用)。程序是如何运行到这部分代码?可查看DecodeSigAndReverse函数在未打补丁版本中的调用路径。
由上图可知,似乎需要先命中“ProcessHandshake”函数,然后再构造一个“Client VerifyMessage”(客户端验证消息)来命中被修改的代码。为了实现触发,我们查阅了MSDN中有关TLS/SSL的文档(如下图)。
根据调用路径中涉及的函数名称,Schannel可能是在处理一个“Certificate Verify”消息,涉及证书的认证过程(客户端发送一个证书到服务器,服务器通过证书验证客户端身份)。如果仔细查看未打补丁的函数,可从调用函数 CryptDecodeObject的参数lpszStructType发现一个关键的线索。
BOOL WINAPI CryptDecodeObject( _In_ DWORD dwCertEncodingType, //使用的编码类型 _In_ LPCSTR lpszStructType, //结构的类型 _In_ constBYTE *pbEncoded, //指向待解码的结构 _In_ DWORD cbEncoded, //待解码结构的字节长度 _In_ DWORDdwFlags, _Out_ void*pvStructInfo, //指向存储解码后的结构 _Inout_ DWORD *pcbStructInfo //表示解码后的据结构的字节长度);
通过MSDN查询,我们可获知lpszStructType参数指出证书签名的结构类型。在本例中,可以是 X509_ECC_SIGNATURE(ECDSA签名)和X509_DSS_SIGNATURE(DSS签名)。若选择ECC_SIGNATURE,该 结构在MSDN中有具体的定义。
//CERT_ECC_SIGNATURE结构包含了椭圆曲线数字签名算法(ECDSA)的r值和s值。typedef struct _CERT_ECC_SIGNATURE { CRYPT_UINT_BLOB r; // ECDSA签名的r值,Little-Endian字节顺序。 CRYPT_UINT_BLOB s; // ECDSA签名的s值,Little-Endian字节顺序。} CERT_ECC_SIGNATURE, *PCERT_ECC_SIGNATURE;
其中一个memcpys函数调用的size参数好像存在问题,可能与证书的编码过程有关。
void *memcpy(void *dest, //目的的缓冲区。const void *src, //源的缓冲区size_t size //字节长度。);
据我们所知,最快速的处理方法就是在运行状态下观察这个函数(PS当然要用调试器,不然看不到哟)。因此,利用OpenSSL创建一个ECDSA签 名的证书,并将IIS设置成证书认证的方式。然后,把远程调试器附加到运行IIS服务的服务器中的LSASS进程,并在比较 ECC_SIGNATURE(ECDSA签名)的地方设置断点(cmp ebx, 2F)。
令人吃惊的是,当利用OpenSSL client第一次尝试链接IIS服务进行认证时,断点就被触发。既然能够命中存在问题的代码,下一步就是触发漏洞。为了加快分析过程,我们决定修改OpenSSl来模糊测试这个调用路径。
在OpenSSL中,源文件s3_clnt.c实现了“client verify messages”的ECDSA签名。客户端的已编码的ECDSA签名由客户端上OpenSSL中ECDSA_sign(…)函数生成,最终命中IIS服 务器上schannel!DecodeSigAndReverse()中的函数CryptDecodeObject(…)。如果追踪函数 ssl3_send_client_verify(…),发现它最终会调用ECDSA_sign(…),下图就是对“client verify messages”进行ECDSA签名的代码块。
#ifndef OPENSSL_NO_ECDSA if(pkey->type == EVP_PKEY_EC) { if(!ECDSA_sign(pkey->save_type, &(data[MD5_DIGEST_LENGTH]), SHA_DIGEST_LENGTH,&(p[2]), (unsignedint *)&j,pkey->pkey.ec)) { SSLerr(SSL_F_SSL3_SEND_CLIENT_VERIFY, ERR_R_ECDSA_LIB); gotoerr; } s2n(j,p); n=j+2; } else#endif
为弄清楚这个调用过程,其中ECDSA_sign函数的原型如下:
int ECDSA_sign(int type, //可忽略const unsigned char *dgst, //指向待签名的散列值int dgstlen, //散列值的长度unsigned char *sig, //指向存储DER编码的签名的存储空间unsigned int *siglen, //签名的长度EC_KEY *eckey //EC_KEY对象,包含了EC私钥);
通过查阅该函数的帮助文档,可知“DER编码后的签名存储在sig中,而sig的长度存储在sig_len中”。如果打算使用OpenSSl s_client向IIS发起身份认证,然后Schannel一路运行到schannel!DecodeSigAndRevers(…)函数,我们可以看 到变量“p”经过ECDSA_sign(…)运算的结果提交给schannel!CryptDecodeObject(…)函数,经过解码后并传递给存在 问题代码中的memcpy。
因此,仅需将变量“p”中的一个字节设置为一个随机值,然后不停向IIS发送“Certificate Verify”消息,直到一些奇怪的事情发生。如果等待足够的时间,可以观察由memcyp引起的进程崩溃。
进一步的分析和漏洞利用留给读者来练手。