Windows安全通道远程代码执行漏洞(CVE-2014-6321)浅析

  • A+
所属分类:漏洞分享
一、背景

Windows安全通道(SecureChannel,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资深信息安全工程经理RossBarrett表示,目前CVE-2014-6321漏洞的影响还不大,但是如果攻击代码泄漏,该漏洞将会成为严重的问题,且影响会扩大。

目前,微软也尚未接到有关该漏洞已公开用来攻击用户的消息,而微软在官网也表示,此安全更新可以更正Schannel清理特定封包的方式,进而消除此项安全风险。

二、探究

网上关于CVE-2014-6321漏洞的详情与利用方法鲜有披露。我们通过新发布的补丁来一探究竟。

通过对打补丁前后的schannle.dll文件进行二进制比较,可查看到一些修改过的函数,其中几个是类DTLSCookieManager的方法。由此表明了至少有一个bug位于DTLSCookieManager类中。而大家最关心的bug好像是位于schannel!DecodeSigAndReverse(…)中,DecodeSigAndReverse函数的变更如下所示。

由从上图可清楚的看到DecodeSigAndReverse函数中间添加了一些新的代码块(图右侧中的灰色模块)。新添分支往往是一个好兆头!如果我们放大打补丁的版本(图右侧),情况似乎更符合预期。

上图可看到新添代码块把运行路径指向了memcpy调用(实际上是两次memecpy函数调用)。程序是如何运行到这部分代码?可查看DecodeSigAndReverse函数在未打补丁版本中的调用路径。

由上图可知,似乎需要先命中“ProcessHandshake”函数,然后再构造一个“ClientVerifyMessage”(客户端验证消息)来命中被修改的代码。为了实现触发,我们查阅了MSDN中有关TLS/SSL的文档(如下图)。

根据调用路径中涉及的函数名称,Schannel可能是在处理一个“CertificateVerify”消息,涉及证书的认证过程(客户端发送一个证书到服务器,服务器通过证书验证客户端身份)。如果仔细查看未打补丁的函数,可从调用函数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签名)的地方设置断点(cmpebx,2F)。

令人吃惊的是,当利用OpenSSLclient第一次尝试链接IIS服务进行认证时,断点就被触发。既然能够命中存在问题的代码,下一步就是触发漏洞。为了加快分析过程,我们决定修改OpenSSl来模糊测试这个调用路径。

在OpenSSL中,源文件s3_clnt.c实现了“clientverifymessages”的ECDSA签名。客户端的已编码的ECDSA签名由客户端上OpenSSL中ECDSA_sign(…)函数生成,最终命中IIS服务器上schannel!DecodeSigAndReverse()中的函数CryptDecodeObject(…)。如果追踪函数ssl3_send_client_verify(…),发现它最终会调用ECDSA_sign(…),下图就是对“clientverifymessages”进行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中”。如果打算使用OpenSSls_client向IIS发起身份认证,然后Schannel一路运行到schannel!DecodeSigAndRevers(…)函数,我们可以看到变量“p”经ECDSA_sign(…)运算的结果会提交到schannel!CryptDecodeObject(…)函数,经过解码后并传递到存在问题代码中的memcpy调用。

因此,仅需将变量“p”中的一个字节设置为一个随机值,然后不停向IIS发送“CertificateVerify”消息,直到一些奇怪的事情发生。如果等待足够的时间,可以观察由memcyp引起的进程崩溃。

进一步的分析和漏洞利用留给读者来练手。(太给读者们面子了)

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: