Authenticode签名对未签名代码的应用
众所周知,攻击者将合法的数字证书应用于其恶意软件,这大概是为了躲避基本的签名验证程序。Petya勒索病毒便是这种情况。作为一名逆向工程师或red team的开发者,了解哪些合法的签名可以被应用到其他的未签名代码以及攻击者提供的代码的方法。这篇博客将会给出代码签名机制、二进制数字签名,以及对数字证书应用于未签名的PE文件的相关技术背景的介绍。不久,你将会在我下个月发布的研究中看到相关技术的联系。
背景
对PE文件(如exe、dll、sys等文件)进行签名是什么意思呢?一个比较简明的答案就是选择相应的PE文件,右键单击之后,查看文件属性,如果存在“数字签名”选项卡,则表示已经该文件已经被签名。当你看到文件属性中的这个“数字签名”选项卡的时候,实际上意味着该PE文件是Authenticode签名的,在这个文件中有一个二进制的数据包,其中包括一个证书和这个文件的签名哈希值(更具体点说,Authenticode哈希值就是在散列计算中去除PE头的那部分数据的哈希值)。存储Authenticode签名的格式在PE代码签名规范【见附件1】中有详细说明。
然而,很多本应该被签名的文件(以notepad.exe为例),事实上却并没有我们所说的“数字签名”选项卡。那这是否意味着该文件没有签名,并且微软也运行了这些未签名的代码呢?这个要视情况而定。虽然notepad.exe本身没有嵌入Authenticode签名,但实际上它通过目录签名这种方式进行过签名。Windows包含由许多目录文件组成的目录存储库,基本上只是Authenticode的散列表。然后,每个目录文件都被签名,以证明任何具有匹配哈希的文件源自目录文件的签名者(基本上都是Microsoft的程序)。因此,当Explorer UI不尝试查找目录签名时,几乎其他的所有签名验证工具都将执行目录查找。在PowerShell和Sysinternals Sigcheck中获取可以Authenticode签名。
注:目录文件存储在%windir%\System32\CatRoot{F750E6C3-38EE-11D1-85E5-00C04FC295EE}
在上述屏幕截图中,SignatureType属性指示notepad.exe是目录签名(”Catalog”)。还值得注意的是IsOSBinary属性。虽然具体的实现没有文档化,但如果签名链接到几个已知的,散列的Microsoft根证书之一,则将显示“True”。有兴趣了解更多有关如何工作的人可以逆向CertVerifyCertificateChainPolicy函数【https://msdn.microsoft.com/en-us/library/windows/desktop/aa377163(v=vs.85).aspx】。
Sigcheck工具使用“-i”参数,执行目录证书验证,并显示包含匹配的Authenticode哈希的目录文件路径。 “-h”参数还将计算并显示PE文件的SHA1和SHA256形式的Authenticode散列(分别为PESHA1和PE256):
理解Authenticode哈希的概念会使你比较容易的在目录文件中找到相应的条目。你也可以双击目录文件来查看其它条目。我还编写了CatalogTools【https://github.com/mattifestation/CatalogTools 】,这是用于解析目录文件的PowerShell模块。在“hint”元数据字段说明了notepad.exe确实是相应的条目:
数字签名的二进制格式
现在你已经了解了可以对PE文件进行签名的方法(Authenticode签名和目录签名),那么对签名的二进制格式有所了解也是十分必要的。无论是Authenticode签名还是目录签名,其签名都存储PKCS#7【https://tools.ietf.org/html/rfc2315 】签名数据中,这种数据是一种ASN.1格式的二进制数据。 ASN.1只是一个标准,用于说明应该存储不同数据类型的二进制数据。在观察/解析数字签名的字节之前,你必须首先知道它如何存储在文件中。目录文件很简单,因为文件本身由原始的PKCS#7数据组成。 网上有在线的ASN.1解析器【https://lapo.it/asn1js/ 】解析ASN.1数据,并以直观的方式呈现出来。例如,尝试将包含notepad.exe的哈希的目录文件加载到解析器中,你将可以看到一个层次分明的数据布局的视图。 以下是解析输出的代码段:
ASN.1编码数据中的每个属性都以对象标识符(OID,object identifier)开头,这是一个唯一的数字序列,用于标识以下数据的类型。上述代码段中值得注意的OID如下:
1.2.840.113549.1.7.2 - 这表明PKCS#7签名数据 – 后面跟着Authenticode和目录签名代码。
1.3.6.1.4.1.311.12.1.1 - 这表明后面会跟着目录文件散列数据
建议想要深入理解的读者花点时间探索数字签名中包含的所有字段。然而,图中的字段都不在本博客的讨论范围之内。这里【https://support.microsoft.com/en-us/help/287547/object-ids-associated-with-microsoft-cryptography】列出了附加的加密/签名相关OID。
嵌入式PE Authenticode签名检索
具有嵌入式Authenticode签名的PE文件中的数字签名数据附加在文件末尾(这里说的是格式正常的的PE文件)。操作系统显然需要一些信息来检索嵌入式签名的确切偏移量和大小。 给大家介绍一款我最喜欢PE解析/编辑工具CFF Explorer【http://www.ntcore.com/exsuite.php】,下面是使用CFF Explorer来解析kernel32.dll:
嵌入式数字签名的偏移量和大小存储在PE的可选头内的“security directory”数组中的“data directories”偏移量中。 数据目录包含PE文件中的各种结构的偏移量和大小——导出表,导入表,重定位表等。数据目录中的所有偏移量都是相对虚拟地址(RVA),这意味着它们是加载时PE相应部分的在内存中的偏移量。只有一个例外——安全目录表将其偏移量存储为文件偏移量,这是因为Windows加载程序实际上并没有在内存中加载安全目录表的内容。
安全目录表的二进制数据是WIN_CERTIFICATE结构【https://msdn.microsoft.com/en-us/library/windows/desktop/dn582059(v=vs.85).aspx】的。下面是kernel32.dll在010Editor中解析的结果【https://gist.github.com/mattifestation/d10f91859bd0dffd4a539945ae02eccb, 相应文件见附件2 】(文件偏移是0x000A9600):
PE文件的Authenticode签名应始终具有WIN_CERT_TYPE_PKCS_SIGNED_DATA的wRevision。后续的字节数组是与目录文件内容相同的PKCS#7以及ASN.1编码签名数据。唯一的区别是你应该找不到1.3.6.1.4.1.311.12.1.1这样的OID,因为这表明存在的目录散列签名。
在网上在线的ASN.1解码器解析出的原始bCertificate数据证实我们正在处理正确的PKCS#7的数据:
数字签名对未签名PE文件的应用
现在你已经了解了数字签名的二进制格式和存储位置的基本概念,接下来可以开始将现有签名应用于未签名的代码。
嵌入式签名认证的应用
将嵌入的Authenticode签名从签名文件应用到未签名的PE文件是非常简单的。虽然这个过程可以自动化完成,这里我将用十六进制编辑器和CFF Explorer来讲解如何手动实现。
步骤1: 识别要窃取的Authenticode签名。在实例中,我将使用kernel32.dll中的一个签名作为演示。
步骤2: 识别“security directory”中的WIN_CERTIFICATE结构的偏移量和大小。
可以看到上述截图中的文件偏移量为0x000A9600,大小为0x00003A68。
步骤3: 在一款十六进制编辑器中打开kernel32.dll,从偏移量0xA9600处选择0x3A68字节的数据,然后复制这些数据。
步骤4:在十六进制编辑器中打开你的未签名PE(在此示例中为HelloWorld.exe),滚动到最后,粘贴从kernel32.dll复制的3A68字节的数据。 注意签名开头的文件偏移量(在我的机器环境下为0x00000E00)。粘贴完数据之后记得保存文件。
步骤5:在CFF Explorer中打开HelloWorld.exe,并更新安全目录以指向应用的数字签名:offset - 0x00000E00,size - 0x00003A68。 进行修改后保存文件。 忽略“无效”警告,这是因为CFF Explorer不将安全目录视为文件偏移量,并在尝试引用数据所在的位置时发出警告。
这就完成了所有操作。现在,签名验证实用程序将正确解析和显示签名。 唯一需要注意的是,它们将报告签名无效,因为所计算的文件的Authenticode与证书中存储的签名哈希的Authenticode不匹配。
现在,如果你想知道为什么SignerCertificate指纹符号不匹配,那么你是一个精明的读者。考虑到我们应用了相同的签名,为什么证书指纹不符合?这是因为Get-AuthenticodeSignature首先尝试对kernel32.dll进行目录文件查找。在这种情况下,它找到了kernel32.dll的目录条目,并显示目录文件的签名者的签名信息。kernel32.dll也是Authenticode签名的。 所以要验证Authenticode散列的指纹值是否相同,请临时停止CryptSvc服务,该服务负责执行目录哈希查询的服务。如此,你将看到指纹值匹配。 这表示目录哈希是使用不同的代码签名证书用于签署kernel32.dll本身的证书。
目录签名对PE文件的应用
实际上,CryptSvc一直在运行,并且将会执行目录查找。假设你想要注意OPSEC,并匹配用于签署目标二进制文件的相同证书。事实证明,您可以通过在WIN_CERTIFICATE结构中交换bCertificate的内容并相应地更新dwLength来将目录文件的内容实际应用于嵌入式PE签名。 可以跟着后续的实例一起操作。请注意,我们的目标(在本实例中)是将Authenticode签名应用于我们的无符号二进制文件,与用于对包含的目录文件进行签名的二进制文件相同:本实例中,证书指纹为: AFDD80C4EBF2F61D3943F18BB566D6AA6F6E5033。
步骤1:识别包含目标二进制文件的Authenticode哈希的目录文件——kernel32.dll。 如果一个文件是Authenticode签名的,Sigcheck工具将无法解析目录文件。然而,Signtool(包括在Windows SDK中)可以做到。
步骤2:在十六进制编辑器中打开目录文件,并获取文件大小为0x000137C7
步骤3:我们将在十六进制编辑器中手动构造一个WIN_CERTIFICATE结构。我们来看看我们提供的每个成员:
dwLength:这是WIN_CERTIFICATE结构的总长度,即bCertificate字节加上其他字段的大小= 4(DWORD大小)+ 2(WORD大小)+ 2(WORD大小)+ 0x000137C7(bCertificate —— .cat文件的文件大小)= 0x000137CF。
wRevision:这将是0x0200来表示WIN_CERT_REVISION_2_0。
wCertificateType:这将是0x0002来表示WIN_CERT_TYPE_PKCS_SIGNED_DATA。51 29651 51 15287 0 0 3151 0 0:00:09 0:00:04 0:00:05 3151>
bCertificate:由目录文件的原始字节组成。
在十六进制编辑器中制作数据时,请注意数据以小端格式存储。
本文由看雪翻译小组 skeep 编译,来源exploitmonday@mattifestation
转载请注明来自看雪社区
热门阅读
点击阅读原文/read,
更多干货等着你~