一、前言
我们都知道,攻击者会将合法的数字签名证书应用于他们的恶意软件中,想来应该是为了逃避签名校验。其中有个例子就是勒索软件Petya。作为一个逆向工程师或者是红队开发人员,了解如何将合法签名应用于未签名、攻击者提供的代码中是很重要的。本文将介绍代码签名机制,数字签名二进制格式,和在未签名的PE文件中应用数字证书的技术。很快你就能看到我下个月发布的一些研究与这些技术有关。
二、背景
对PE文件(exe,dll,sys等)签名了意味着什么?简单来说就是如果打开PE文件的文件属性,有个标签页是“数字签名”,那么意味着它是签名过的。当你看到标签“数字签名”,意味着PE文件是Authenticode签名,在其文件内部有个二进制数据块,它包含了证书和文件哈希(特别说明的是,在计算Authenticode哈希时不考虑PE头)。Authenticode签名的存储格式可以在PE Authenticode规范中找到。
有很多文件有签名,但是却不包含“数字签名”标签页(例如notepad.exe)。这是否意味着文件没有签名或者微软发布了未签名的代码?当然不是。尽管notepad没有内嵌的Authenticode签名,但是它有另一种签名(catalog签名)。Windows包含了一个由很多catalog文件组成的catalog存储,它基本上是个Authenticode哈希列表。每个catalog文件都被签名,以表明任何匹配哈希的文件都来自catalog文件的签名者(大部分微软文件是这样的)。因此尽管Explorer UI没有试图查找catalog签名,但是可以使用其他签名校验工具来查询catalog签名,如powershell中的Get-AuthenticodeSignature和Sysinternals中的Sigcheck工具。
注意:catalog文件位于%windir%\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}
在上面的截图中,SignatureType属性表明notepad.exe是catalog签名。还值得注意的是IsOSBinary属性。尽管其实现是未文档化的,如果签名是已知的微软根证书,那么这将显示True。可以通过逆向CertVerifyCertificateChainPolicy函数来了解其中内部原理。
使用“-i”选项调用Sigcheck来验证catalog证书,它也会显示包含匹配到Authenticode哈希的catalog文件路径。“-h”选项也会计算并显示PE文件的SHA1和SHA256 Authenticode哈希(PESHA1和PE256)。
知道了Authenticode哈希,你就可以查看catalog文件中的各种条目。你可以双击一个catalog文件以查看它的信息。我写了一个CatalogTools的PowerShell模块来解析catalog文件。“hint”元数据字段表明了确实是notepad.exe的信息。
三、数字签名二进制格式
现在,你已经了解了PE文件的签名(Authenticode和catalog),了解一些签名的二进制格式是有用的。Authenticode和catalog签名都以PKCS #7 signed data格式存储,它是ASN.1格式的二进制数据。ASN.1只是一个标准,是用来表明不同数据类型的二进制数据是如何存储的。在解析数字签名之前,你必须首先知道它在文件中是如何存储的。Catalog文件是直接包含了原始的PKCS #7数据。有个在线的ASN.1解码器可以解析ASN.1数据,并直观的显示出来。例如,尝试加载notepad.exe的catalog签名,你将直观的看到数据的布局。下面是解析结果的片段:
ASN.1编码数据中的每个属性都开始于一个对象ID(OID),这是一种表示数据类型的唯一数字序列。上面片段中值得看的OID如下:
- 1.2.840.113549.1.7.2:这表明了以下是PKCS #7签名数据,它是Authenticode和catalog签名的格式。
- 1.3.6.1.4.1.311.12.1.1:这表明下面是catalog文件哈希数据
花时间浏览数字签名中所有的字段是值得的。本文无法包含所有的字段,然而另外的加密/签名相关的OID能在这里找到。
嵌入的PE Authenticode签名
内嵌在PE文件中Authenticode签名被追加到文件的末尾(这是格式良好的PE文件)。很明显,操作系统需要一些信息以便提取出内嵌的签名偏移和大小。使用CFF Explorer看查看下kernel32.dll:
内嵌的数字签名的偏移和大小存储在可选头中的数据目录的安全目录中。数据目录包含了PE文件中各种结构的偏移和大小,如导出表,导入表,重定位等。在数据目录中的所有的偏移都是相对虚拟地址(RVA),意味着它们是PE文件加载到内存中相对基址的偏移。只有一个例外,那就是安全目录,其存储的偏移是文件偏移。原因是Windows加载其不会将安全目录的内容加载到内存中。
安全目录中文件偏移指向的二进制数据是一个WIN_CERTIFICATE结构体。下面是这个结构在010Editor中的显示(文件偏移是0x000A9600):
PE Authenticode签名应该总是包含WIN_CERT_TYPE_PKCS_SIGNED_DATA的wRevision。PKCS #7,ASN.1编码签名的数据的字节数组和catalog文件中看到的是一样的。唯一的不同是你找不到OID 1.3.6.1.4.1.311.12.1.1(其表明是catalog哈希)。
使用在线ASN.1解码器解析原始的bCertificate数据,我们能确认我们处理的是正确的PKCS #7数据:
四、将数字签名应用于未签名的代码
现在你已经对数字签名的二进制格式和存储位置有了大概的了解,你能开始将存在的签名应用于未签名的代码中了。
内嵌的Authenticode签名的应用
将签名文件中内嵌的Authenticode签名应用到未签名的PE文件中是很简单的。尽管过程可以自动化,但是我还是解释一下如何通过一个二进制编辑器和CFF Explorer来手动实现。
第1步:确定你想要盗取的Authenticode签名。在这个例子中,我使用kernel32.dll
第2步:确定安全目录中的WIN_CERTIFICATE结构体的偏移和位置
上面截图中的文件偏移是0x000A9600,大小是0x00003A68。
第3步:使用二进制编辑器打开kernel32.dll,选择开始于偏移0xA9600的0x3A68字节,并复制这些字节。
第4步:使用二进制编辑器打开未签名的PE文件(本例中是HelloWorld.exe),滚动到文件末尾,粘帖从kernel32.dll拷贝的数据。保存文件。
第5步:使用CFF Explorer打开HelloWorld.exe,并更新安全目录指向数字签名的偏移(0x00000E00)和大小(0x00003A68)。修改后保存文件。忽略“不可靠”的警告。CFF Explorer不会将安全目录作为文件偏移,当它试图引用数据所在节时就产生了“困境”。
完成了!现在,签名校验工具将解析并显示适当的签名。唯一的警告是签名是不可靠的,因为计算文件的Authenticode不能匹配存储在证书中的哈希。
现在,如果你想知道SignerCertificate thumbprint值为什么不匹配,那么你是个有追求的读者啊。考虑到我们使用了相同的签名,为什么不能匹配证书thumbprint呢?这是因为Get-AuthenticodeSignature首先会试图查询kernel32.dll的catalog文件。这个例子中,它找到了kernel32的条目,并显示了catalog文件中的签名者的签名信息。Kernel32.dll也是使用Authenticode签名的。为了校验Authenticode哈希的thumprint值是相同的,临时关闭了负责查询catalog哈希的CryptSvc服务。现在你将看到thumprint值已经匹配了,这表明catalog哈希是使用不同于kernel32.dll使用的签名证书来签名的。
将catalog签名应用于PE文件
实际上,CryptSvc一直会运行并执行catalog查询操作。假设你想注意OPSEC并想匹配用于签名你目标二进制的相同的证书。事实上,你确实能通过交换WIN_CERTIFICATE结构中的bCertificate并更新dwLength来将catalog文件的内容应用于内嵌的PE签名。注意我们的目标是将Authenticode签名应用于我们的未签名的二进制中,这和用于签名catalog文件是相同的:证书thumprint 是AFDD80C4EBF2F61D3943F18BB566D6AA6F6E5033。
第1步:确定包含你的目标二进制的Authenticode哈希的catalog文件,本例是kernel32.dll。如果一个文件使用Authenticode签名,Sigcheck解析catalog文件将失败。但是Signtool(windows SDK中包含)还是可以用。
第2步:在16进制编辑器中打开catalog文件,文件大小是0x000137C7
第3步:在16进制编辑器中手动构造WIN_CERTIFICATE结构。让我们浏览下我们使用的每个字段:
- dwLength:这是WIN_CERTIFICATE结构的全部长度,如bCertificate字节加上其他字段的大小=4(DWORD的大小)+2(WORD的大小)+0x000137C7(bCertificate,.cat文件的大小)=0x000137CF.
- wRevision: 0x0200表示WIN_CERT_REVISION_2_0
- wCertificateType: 0x0002表示WIN_CERT_TYPE_PKCS_SIGNED_DATA
- bCertificate:这包含了catalog文件的原始数据
当在16进制编辑器中构造完,注意按小端存储字段。
第4步:从构造的WIN_CERTIFICATE中复制所有的内容,将他们附加到未签名的PE文件中,并更新安全目录的偏移和大小。
现在,假设你的计算和对齐是正确的,thumbprint将匹配catalog文件。
五、异常检测
希望本文描述的技术能使人思考到如何检测数字签名的滥用。尽管我没有彻底调查过签名检测,但是让我们抛出一系列问题来激励其他人开始研究并写出签名异常的检测:
1. 对于合法签名的微软PE,PE时间戳和证书有效期是否有相关性呢?攻击者提供的代码的时间戳偏离上述的相关性吗?
2. 在阅读本文后,哈希不匹配的签名文件的信任等级是多少?
3. 如何检测内嵌Authenticode签名包含catalog文件的PE文件?Hint:上述提到的特定的OID可能是有用的。
4. 停止/禁用CryptSvc服务对本地签名验证有什么影响?如果这中情况发生了,那么大部分系统文件,其所有的意图和目的都将不会签名
5. 每个合法的PE中我都能看到有0x10字节的填充。我演示的例子中没有0对齐的x10。
6. 合法的微软数字签名和应用到自签名证书中的所有证书属性有什么不同?
7. 数字签名之外有数据追加会怎么样?参考这篇文章。
8. 专业人员在调查不同证书使用相同的代码时,应该找到Authenticode哈希。VirusTotal提供了Authentihash值,该值也能通过Sigcheck –h计算。如果我调查一个例子的不同变种对应单独的一个Authentihash,我发现那将非常有趣。