Windows程序的数字签名校验“漏洞”
本文已发表于“安全客”,转载请注明出处。
在今年的黑帽大会上,国外的一个安全研究员展示了如何通过Windows的数字签名bypass对恶意程序代码的检测。下载大会的该演讲的ppt大概看了一下,报告分为两部分,第一部分展示数字签名的的校验“漏洞”,第二部分展示该作者自己研究实现的一个pe程序加载器,用来配合第一部分的“漏洞“bypass杀毒软件对恶意程序的检测。本文重点在于第一部分的这个数字签名校验”漏洞“,通过回顾分析数字签名的校验来阐述这个”漏洞“的原理。
数字签名与数字证书
讲这个”漏洞“之前先讲一下数字签名的原理,理解一般的数字签名验证过程。下面是数字签名的相关概念和验证过程:
数字签名:对一段数据摘要使用私钥进行加密,公钥进行解密校验
数字证书:对数字签名的解密公钥和身份信息使用CA的私钥进行加密,系统信任的CA公钥进行解密
如上图所示,数字签名用来保护所有者的数据,所以从这些数据出发,所有者签发数据的数字签名过程是要先计算这些数据的摘要(也就是对这些数据做个hash运算,目前由于SHA-1开始不安全,正在逐渐换成SHA-2算法),然后使用所有者的私钥进行加密(目前主流还是RSA,也有一些ECC或其他算法),加密后的数据带上所有者的信息按照一定的标准格式组织好其实就是所谓的数字签名。而接收方(使用者)要验证这个数字签名,一般通过数字证书,因为其中包含两个重要的信息,一个就是用于解密数字签名的公钥(通过这个公钥才能正确解密出数据所有者事先加密好的数据摘要值,用于验证比对数据的一致性),另一个就是指明了这个公钥的所有者的信息(当然要和数字签名所有者的信息一致)。而数字证书其实是由第三方的可信机构颁发给数字签名所有者的一份数据文件,生成算法和数字签名类似,只不过它是把所有者的公钥和身份这些固定信息数据进行了加密返回给了所有者,此时所有者的身份正常是得到了可信机构的认证了。最后一个环节,数字签名、数字证书通常都是附加到所有者的数据后面一起传送给了使用者,但是如何解密数字证书来进行验证呢?我的理解是,这个要依托于系统,比如windows,他们会维护一批可信机构的列表,在验证证书的时候,就会先从这个列表里校验和获取证书签发机构的身份和公钥,并返回到用户系统进行存储和使用。正是通过这样一整套的相对完善的机制,最终比较靠谱的保证了接收数据的有效性。而本文所指的Windows程序的数字签名,其实就是指所有者要保护的数据是一些Windows下的可执行程序这样一种情况。
可被利用的“漏洞”
从上面的签名校验流程看,由于整个过程涉及环节比较多,相应的每个环节都存在有被攻破的可能性,比如hash碰撞、根证书伪造等,但是本文不涉及此类问题。实际上,数字签名仍然是保护数据有效的一种可靠的途径,只要操作得当,算法强度足够,被攻破的可能性还是比较低。而本文所说的“漏洞”,也不是上面整个验证过程的漏洞,下面就来看一看这个“漏洞”是什么意思吧。
找一个带有数字签名的程序来实验,就以微软官方的库文件msvcr100.dll为例。右键点击文件属性,可以看到有一个数字签名的标签,依次点击可以查看到下面的签名有效状态,表示这个程序的数字签名验证成功,“程序数据没有被篡改”(这里加引号说明理解需要谨慎,详看下文)。
然后修改这个文件,比如在尾部随便添加几个字节的数据,再次查看数字签名的状态后如下图所示,说明系统检测到文件被篡改,数字签名验证为无效。
这就给我们造成一种感觉,数字签名真的可以保证数据不被篡改,只要数据受到篡改,就会被系统的验证机制检测到并提示签名无效。然而,这种感觉其实并不靠谱,因为我们实际上只是对签名文件添加了一些数据,并没有修改到程序的原始数据区域的部分,理论上只要系统能够找到程序的数字签名并通过上述的验证流程,那么数字签名就仍是有效的,受保护数据没有遭到篡改。那么,有没有办法让这个添加了数据的文件去正确识别数字签名并显示签名有效呢,答案是肯定的,而这也正是本文所指的“漏洞”。
这里直接给出这个“漏洞”的信息:对于一个Windows的可执行程序,签发数字签名的时候需要计算的数据摘要并不会是程序文件的全部数据,而是要排除一些特定区域的数据。而这些区域当然和PE文件结构有关,具体地,不管是签发时还是校验时计算的hash都会排除一个checksum字段、一个Security数据目录字段以及数字签名证书部分的数据。至于原因,当然是为了合理地组织pe程序的数字签名,符合pe文件格式的标准了。下面是三个字段的相关信息:
struct IMAGE_NT_HEADERS
…
struct IMAGE_OPTIONAL_HEADER32
…
DWORD CheckSum //pe映像校验和,dll常用
…
struct DATA_DIR Security
DWORD VirtualAddress //指向数字签名证书数据结构_WIN_CERTIFICATE
DWORD Size //_WIN_CERTIFICATE结构大小
struct _WIN_CERTIFICATE
DWORD dwLength //本结构数据的大小,一般和Security.Size相等
WORD wRevision
WORD wCertificateType
BYTE bCertificate[ANYSIZE_ARRAY] //数字签名证书数据
由于计算hash时排除以上3个相关的数据,故这三个字段本身就是Windows系统留给用户可以操作修改的区域。而实际上,要让上述在尾部添加数据的文件显示数字签名有效,即Windows系统对数字签名可正确识别,只需要更新Security.Size这个字段一致,且满足添加的数据长度为8的整数倍即可:
由此可见,数字签名状态正常,并不意味着带签名的程序就是完整未被篡改过的,通过修改程序的字段数据可以实现在带签名的程序后面添加任意的数据且签名状态验证正常。上面的三个字段如果同时修改,即checksum、Security.Size和dwLength都正常,除非很严格的去对数字签名的数据部分进行检查,否则比较难以检测出带签名的程序文件是否被恶意篡改添加了非法数据。而这一点,就构成所谓的“漏洞”,能够成为很多恶意程序的良好的藏身之处,借此bypass很多防火墙、反病毒等软件的检测。比如黑客可以将恶意代码嵌入带有正常签名的程序后面来绕过一些防护系统的检测,从而为后续的一些攻击行动起到一个很好的掩护作用。
另一种Windows程序的签名“漏洞”
经过以上的分析,所谓的签名“漏洞”,其实是数字签名文件组织上的概念误区,导致将签名的正常状态和签名文件的完整性混淆在一起。这里介绍一种更为方便的Windows程序,能够以更简单的方式实现以上同样的效果。
打开Windows系统文件夹“C:/Windows/Installer”,在这个目录下可以找到一些具有微软数字签名的msi/msp格式的文件。我们知道msi是微软格式的安装包,点击运行后系统会自动调用Msiexec.exe来启动程序,而这种程序相比于PE程序这个文件格式来说,对数字签名的识别好像更为简单,也没有PE程序结构的特殊限制。于是,随便往带有签名的msi文件尾部添加一些数据,而无需再修改文件的其他数据,msi的数字签名状态仍然校验正常。比如我系统找到的下面一个文件:
可以看到,对于msi签名文件,随意添加数据签名状态不变,说明即使被添加了非法数据,系统也能顺利找到msi程序的数字签名进行校验,而由于msi程序的原始保护部分没有受到篡改,所以数字签名状态验证正常。
总结
通过本文的分析,实际上这个程序签名的“漏洞”并算不上通常意义上的漏洞,但是却因为本身比较容易被概念混淆,使用可能没有充分得当,导致可能存在一些比较严重的可利用环节。所以本文的结论就是:程序的数字签名只能用于验证程序的代码和数据是否被篡改,而无法用于验证程序文件本身是否被篡改。
目前没有反馈
表单载入中...