查看原文
其他

颠覆windows的信任体系——实现任意代码签名劫持(二)

2017-11-20 龙幽 看雪学院

上一部分参考文章:颠覆windows的信任体系——实现任意代码签名劫持(一)





信任提供者和SIP注册



了解信任提供者和 sip 在注册表中注册的合法方法, 以便了解攻击者如何利用注册过程 (或完全颠覆它),这非常重要。





SIP注册



SIP通过调用DllRegisterServer.aspx)的导出函数“wintrust!
CryptSIPAddProvider.aspx)”来完成注册。这使得SIP可以通过调用 “regsvr32.exe SIPfilename.dll” 来完成注册。CryptSIPAddProvider 需要 SIP_ADD_NEWPROVIDER.aspx) 结构体, 它由在实现签名功能的SIP DLL中的导出函数组成。需要以下SIP_ADD_NEWPROVIDER字段:

  1. pwszDLLFileName:
    SIP DLL 的名称。这可能只是文件名, 但它应该是完整的路径。

  2. pwszGetFuncName:
    实现CryptSIPGetSignedDataMsg.aspx)的导出函数名

  3. pwszPutFuncName:
    实现CryptSIPPutSignedDataMsg.aspx)的导出函数名

  4. pwszCreateFuncName:
    实现CryptSIPCreateIndirectData.aspx)的导出函数名

  5. pwszVerifyFuncName:
    实现CryptSIPVerifyIndirectData.aspx)的导出函数名

  6. pwszRemoveFuncName:
    实现CryptSIPRemoveSignedDataMsg.aspx)的导出函数名


下列 SIP_ADD_NEWPROVIDER 字段是可选的:

  1. pwszIsFunctionNameFmt2:
    实现pfnIsFileSupportedName.aspx)

  2. pwszGetCapFuncName:
    实现pCryptSIPGetCaps.aspx)

  3. pwszIsFunctionName:
    实现pfnIsFileSupported.aspx)


在调用 CryptSIPAddProvider 时, wintrust.dll 将各自的导出函数名和实现 dll 添加到HKLM\SOFTWARE\[WOW6432Node\]Microsoft\Cryptography\OID\EncodingType 0 注册表子键中。


SIP dll 还应实现 DllUnregisterServer.aspx) 注销功能,该函数调用 CryptSIPRemoveProvider.aspx) 删除所有相关的 SIP 注册表项。


信任提供者注册


信任提供者通过调用 DllRegisterServer的导出函数wintrustWintrustAddActionID.aspx)实现。这使得信任提供者可以通过调用 "regsvr32.exe TrustProviderfilename.dll" 来正式注册。WintrustAddActionID 需要一个
CRYPT_REGISTER_ACTIONID.aspx)——由在执行所有信任验证步骤的信任提供程序 DLL 中的导出函数组成的结构体,。信任提供程序注册功能可以与 SIP 注册的函数共享, 也可以在专用 DLL 中独立。

 

在调用 WintrustAddActionID 时, wintrust.dll 将各自的导出函数名和实现 dll 添加到
HKLM\SOFTWARE\[WOW6432Node\]Microsoft\Cryptography\Providers\Trust
注册表子键中。
信任提供者通过调用DllUnregisterServer的导出函数 wintrust! WintrustRemoveActionID.aspx) 来取消注册。

信任提供者和 SIP 注册示例

最重要的信任提供者注册位于wintrust!DllRegisterServer中,执行以下注册步骤:

  1. 调用 WintrustDllRegisterServer
    a.  调用 wintrust!CryptRegisterOIDFunction函数,使用CryptEncodeObject 和 CryptDecodeObject注册 ASN.1编码/解码例程。这类的许多函数在创建数字签名时被调用。在分析数字签名以进行验证时, 通常会调用它们的解码对应函数。与 SIP 和信任提供程序注册一样, 这些实现函数也存储在注册表中:

    所有这些编码函数都接受以下函数签名:

    WintrustDllRegisterServer 注册以下编码/解码例程:

    i. 1.3.6.1.4.1.311.2.1.15 (SPC_PE_IMAGE_DATA_OBJID)
    函数: wintrust!WVTAsn1SpcPeImageDataEncode
    ii.  1.3.6.1.4.1.311.2.1.25 (SPC_CAB_DATA_OBJID)
    函数: wintrust!WVTAsn1SpcLinkEncode
    iii.  1.3.6.1.4.1.311.2.1.20 (SPC_JAVA_CLASS_DATA_OBJID)
    函数: wintrust!WVTAsn1SpcLinkEncode
    iv.  1.3.6.1.4.1.311.2.1.28 (SPC_LINK_OBJID)
    函数: wintrust!WVTAsn1SpcLinkEncode
    v.  1.3.6.1.4.1.311.2.1.30 (SPC_SIGINFO_OBJID)
    函数: wintrust!WVTAsn1SpcSigInfoEncode
    vi.  1.3.6.1.4.1.311.2.1.4 (SPC_INDIRECT_DATA_OBJID)
    函数: wintrust!WVTAsn1SpcIndirectDataContentEncode
    vii.  1.3.6.1.4.1.311.2.1.10 (SPC_SP_AGENCY_INFO_OBJID)
    函数: wintrust!WVTAsn1SpcSpAgencyInfoEncode
    viii.  1.3.6.1.4.1.311.2.1.26 (SPC_MINIMAL_CRITERIA_OBJID)
    函数: wintrust!WVTAsn1SpcMinimalCriteriaInfoEncode
    ix.  1.3.6.1.4.1.311.2.1.27 (SPC_FINANCIAL_CRITERIA_OBJID)
    函数: wintrust!WVTAsn1SpcFinancialCriteriaInfoEncode
    x.  1.3.6.1.4.1.311.2.1.11 (SPC_STATEMENT_TYPE_OBJID)
    函数: wintrust!WVTAsn1SpcStatementTypeEncode
    xi.  1.3.6.1.4.1.311.12.2.1 (CAT_NAMEVALUE_OBJID)
    函数: wintrust!WVTAsn1CatNameValueEncode
    xii.  1.3.6.1.4.1.311.12.2.2 (CAT_MEMBERINFO_OBJID)
    函数: wintrust!WVTAsn1CatMemberInfoEncode
    xiii.  1.3.6.1.4.1.311.12.2.3 (CAT_MEMBERINFO2_OBJID)
    函数: wintrust!WVTAsn1CatMemberInfo2Encode
    xiv.  1.3.6.1.4.1.311.2.1.12 (SPC_SP_OPUS_INFO_OBJID)
    函数: wintrust!WVTAsn1SpcSpOpusInfoEncode
    xv.  1.3.6.1.4.1.311.2.4.2 (szOID_INTENT_TO_SEAL)
    函数: wintrust!WVTAsn1IntentToSealAttributeEncode
    xvi.  1.3.6.1.4.1.311.2.4.3 (szOID_SEALING_SIGNATURE)
    函数: wintrust!WVTAsn1SealingSignatureAttributeEncode
    xvii.  1.3.6.1.4.1.311.2.4.4 (szOID_SEALING_TIMESTAMP)
    函数: wintrust!WVTAsn1SealingTimestampAttributeEncode

  • BOOL WINAPI EncoderDecoderFunction(DWORD
    dwCertEncodingType, LPCSTR lpszStructType,
    PSPC_PE_IMAGE_DATA pInfo, BYTE pbEncoded, DWORDpcbEncoded);

  • HKLM\SOFTWARE\[WOW6432Node\]Microsoft\Cryptography\OID\EncodingType1\[CryptDllDecodeObject|CryptDllEncodeObject]

  • 接下来, SoftpubDllRegisterServer 调用 WintrustAddActionID 来注册下列信任提供者:
    a. WINTRUST_ACTION_GENERIC_VERIFY_V2
    b. WIN_SPUB_ACTION_PUBLISHED_SOFTWARE
    c. WIN_SPUB_ACTION_PUBLISHED_SOFTWARE_NOBADUI
    d. WINTRUST_ACTION_GENERIC_CERT_VERIFY
    e. WINTRUST_ACTION_TRUSTPROVIDER_TEST
    f. HTTPSPROV_ACTION. 下面的相关默认 "用法".aspx) 也注册 (全部存储在注册表HKLM\SOFTWARE\[WOW6432Node\]Microsoft\Cryptography\Providers\Trust\Usages中):
         i. 1.3.6.1.4.1.311.10.3.3 (szOID_SERVER_GATED_CRYPTO)
            Alloc/dealloc 函数: wintrust!SoftpubLoadDefUsageCallData
        ii. 1.3.6.1.5.5.7.3.1 (szOID_PKIX_KP_SERVER_AUTH)
            Alloc/dealloc 函数: wintrust!SoftpubLoadDefUsageCallData
        iii. 1.3.6.1.5.5.7.3.2 (szOID_PKIX_KP_CLIENT_AUTH)
            Alloc/dealloc 函数: wintrust!SoftpubLoadDefUsageCallData
        iv. 2.16.840.1.113730.4.1 (szOID_SGC_NETSCAPE)
            Alloc/dealloc 函数: wintrust!SoftpubLoadDefUsageCallData
    g. DRIVER_ACTION_VERIFY
    h. WINTRUST_ACTION_GENERIC_CHAIN_VERIFY

  • 最后, mssip32DllRegisterServer 被调用来注册SIP。具体来说, 调用 CryptSIPAddProvider 来注册以下SIP:
    a. DE351A42-8E59-11D0-8C47-00C04FC295EE
       CRYPT_SUBJTYPE_FLAT_IMAGE
    b. C689AABA-8E78-11d0-8C47-00C04FC295EE
       CRYPT_SUBJTYPE_CABINET_IMAGE
    c. C689AAB8-8E78-11D0-8C47-00C04FC295EE
       CRYPT_SUBJTYPE_PE_IMAGE
    d. DE351A43-8E59-11D0-8C47-00C04FC295EE
       CRYPT_SUBJTYPE_CATALOG_IMAGE
    e. 9BA61D3F-E73A-11D0-8CD2-00C04FC295EE
       CRYPT_SUBJTYPE_CTL_IMAGE

  • mssip32DllRegisterServer 还显式注销了以下 sip (实际上, Java SIP 组件保留在windows默认的注册表中):

    a. C689AAB9-8E78-11D0-8C47-00C04FC295EE

    CRYPT_SUBJTYPE_JAVACLASS_IMAGE

    b. 941C2937-1292-11D1-85BE-00C04FC295EE

    CRYPT_SUBJTYPE_SS_IMAGE.aspx)

  • 虽然不建议这样做, 但所有 wintrust 的信任提供程序和 SIP 注册都可以使用以下命令 (从提升权限的命令提示符中) 正式注销:
    regsvr32.exe /u C:\Windows\System32\wintrust.dll
    运行上述命令将剥离 Windows 在用户模式下的 执行大多数数字签名检索和信任验证的用户模式的能力。

    信任提供者和 SIP 交互





    背景



    虽然在前面的 "消息" 信任提供者步骤中提到了 SIP 和信任提供者之间的交互, 不过按顺序说明所有步骤的图表应该更有用吧。

    WinVerifyTrust、信任提供者和SIP之间的交互说明


    希望到目前为止, 对信任提供者和SIP的角色有一个基本的了解, 以及它们的体系架构在很大程度上是通过注册注册表来实现模块化的。在下一节中, 将讨论对 Windows 信任体系结构的模块化的攻击。





    Windows 信任体系架构攻击



    通过对 Windows 用户模式信任体系结构的基本了解以及较高的权限级别, 攻击者拥有了他需要破坏信任体系的武器。那么攻击者通过颠覆信任可以来实现什么呢?

    1. 让操作系统相信攻击者提供的代码是以 "受信任的" 代码签名证书 (例如, 用于签名 Microsoft 代码的) 签名和验证的。这种攻击背后的动机是:
      a. 使安全产品将攻击者提供的代码分类为良性。
      b. 从执行签名验证的安全/诊断工具中隐藏。
      c. 一般情况下,在实时检测工具之下,安全人员可能更容易忽略 "使用合法证书签名" 的代码。
      d. 在执行用户模式信任验证的任何进程的上下文中加载恶意代码。

    2. 颠覆应用程序强制基于可信签名权限策略的白名单发布规则。发布者强校验是最常见的名单规则方案之一,因为它甚至允许受信任发布者签名的代码可以绕过不允许软件更新的哈希规则而更新、执行,这种情况下更难维护和审核。

      SIP 劫持 #1: CryptSIPDllGetSignedDataMsg

      如前所述,SIP的CryptSIPDllGetSignedDataMsg组件是允许从已签名的文件中检索编码的数字证书的。再次提醒下,SIP的CryptSIPDllGetSignedDataMsg组件的已实现导出功能存在于以下注册表项中:

    3. HKLM\SOFTWARE\[WOW6432Node\]Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllGetSignedDataMsg\{SIP Guid}

    • Dll -实现数字签名检索函数的 DLL 的路径

    • FuncName -实现数字签名检索功能的导出函数的名称

    此外, 如前所述, CryptSIPDllGetSignedDataMsg 函数具有以下原型:

    BOOL WINAPI CryptSIPGetSignedDataMsg(
        IN SIP_SUBJECTINFO *pSubjectInfo,
        OUT DWORD *pdwEncodingType,
        IN DWORD dwIndex,
        IN OUT DWORD *pcbSignedDataMsg,
        OUT BYTE *pbSignedDataMsg);


    任何熟悉 c/c++ 的攻击者都能够轻松地实现此类功能, 并将现有的 SIP 条目替换为其恶意功能。首先, 了解每个参数的含义是很重要的:


    pSubjectInfo:从调用信任提供者传入的结构体指针, 包含有关提取签名的文件的所有相关信息。这里是一个例子:传递给 pwrshsip!PsGetSignature (PowerShell SIP的 CryptSIPDllGetSignedDataMsg组件)结构体的转储(dump):

    1. pdwEncodingType:在从pSubjectInfo中指定的文件检索数字签名时,此参数指示调用函数(信任提供者"消息"组件)如何正确解码返回数字签名。最常见是的PKCS_7_ASN_ENCODING 和 X509_ASN_ENCODING 一起进行二进制或运算 。

    2. dwIndex: 此参数应为零, 但理论上 SIP 可以包含多个嵌入的签名, dwIndex 表示从指定文件中提取哪一个数字签名。

    3. pcbSignedDataMsg: 通过 pbSignedDataMsg 返回的数字签名的长度 (以字节为单位)。

    4. pbSignedDataMsg: 返回到调用信任提供程者的已编码的数字签名。


    因此,如果攻击者要实现此功能并使用它作为示例,来覆盖可执行文件的SIP(C689AAB8-8E78-11D0-8C47-00C04FC295EE)的CryptSIPDllGetSignedDataMsg组件,则任何 PE 文件都可能返回攻击者选择的任意数字签名。

     

    想象一下下面虚构的攻击场景:


    1. 攻击者在注册表中实现了可执行文件SIP的CryptSIPDllGetSignedDataMsg组件。

    2. 简单地说, 无论是否有嵌入的验证码签名, 为任何可执行文件返回相同的 Microsoft 证书。

    3. 为了确保返回适当格式的数字签名,最好在对其进行劫持之前在调试器中的合法CryptSIPDllGetSignedDataMsg上设置断点。这样做可以确保PKCS#7认证签名数据始终可以正确地返回。

      a. 在 PowerShell 脚本中, 这涉及 base64 解码 "SIG # 开始签名块"(SIG # Begin signature block)。
      b. 在带有嵌入验证码签名的PE文件中, PKCS #7 校验签名的数据存在于PE校验码规范(PE Authenticode specification)中所记录的嵌入式WIN_CERTIFICATE.aspx)结构体的bCertificate 字段中。
      c. 编录文件本身就是 PKCS #7 校验码签名的数据 (实际上可以在嵌入的 PE 校验码签名中使用)。

    4. 现在, 攻击者的实现只需要返回正确的编码签名数据长度和签名数据。


    在这种攻击场景中,被劫持的CryptSIPDllGetSignedDataMsg可以返回用于签署许多系统组件(如notepad.exe)的目录文件的字节。为了方便地确定与已签名文件关联的编录文件,可以使用 sigcheck.exe:

    sigcheck -i C:\Windows\System32\notepad.exe


    在当前的例子中,返回下面的编录文件路径:

    C:\WINDOWS\system32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\Microsoft-Windows-Client-Features-Package-AutoMerged-shell~31bf3856ad364e35~amd64~~10.0.15063.0.cat


    现在,攻击者实现只需要从该编录文件返回字节,使任何PE文件看起来都使用了与notepad.exe相同的证书进行签名。模块化设计方法是将所需的签名内容嵌入到攻击者提供的SIP DLL中的资源中。

    下面的示例说明了 PowerShell SIP CryptSIPDllGetSignedDataMsg 组件是如何使用自定义的恶意 SIP来劫持的, 它将始终返回与 PowerShell 文件相同的合法 Microsoft 证书:

    PowerShell CryptSIPDllGetSignedDataMsg 劫持的演示

     

    可以看出, 在劫持之前, 不出所料,test.ps1 显示为未签名。然而, 在劫持发生后,test.ps1 似乎是用 Microsoft 证书签名的:

    一个未经签名的 PowerShell 脚本, 似乎突然间就被微软给签名了

    虽然未签名的 PowerShell 脚本看起来由 Microsoft 签名了, 但它的哈希验证并没有通过。

     

    虽然看起来劫持是成功的, 但有一个缺陷-签名无法验证, 因为计算的哈希与数字签名中的已签名哈希不匹配。此劫持的另一个不良影响是,任何PowerShell代码都将使用相同的数字签名, 这将在大多数情况下导致哈希不匹配。

     

    为了防止信任验证因哈希不匹配而失败, 还需要劫持CryptSIPDllVerifyIndirectData 。


    SIP 劫持 #2: CryptSIPDllVerifyIndirectData


    正如前面的劫持场景中所解释的,劫持已注册SIP的CryptSIPDllGetSignedDataMsg组件允许未经签名的代码看起来像是被签名了。但是,考虑到哈希值不匹配,数字签名将无法在攻击者提供的代码上进行验证。然而, 再劫持 CryptSIPDllVerifyIndirectData 下函数就不存在这个问题了。


    再次提醒下, CryptSIPDllVerifyIndirectData 实现存储在以下注册表值中:

    • HKLM\SOFTWARE\[WOW6432Node\]Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllVerifyIndirectData\{SIP Guid}

      • Dll

      • FuncName


    函数原型:

    BOOL WINAPI CryptSIPVerifyIndirectData(
        IN SIP_SUBJECTINFO *pSubjectInfo,
        IN SIP_INDIRECT_DATA *pIndirectData);


    调试CryptSIPVerifyIndirectData的合法实现,可以确认当计算出的验证码哈希与签名的哈希值匹配时,CryptSIPVerifyIndirectData返回TRUE。因此,所有恶意SIP需要做的就是为被劫持的相应SIP匹配生成,返回TRUE,从而使之看起来可以通过哈希验证。继续执行PowerShell劫持示例,恶意SIP仅为哈希验证例程返回true,将解决攻击者提供的代码无法正确验证的问题。


    BOOL WINAPI AutoApproveHash(SIP_SUBJECTINFO *pSubjectInfo,SIP_INDIRECT_DATA *pIndirectData) {
        UNREFERENCED_PARAMETER(pSubjectInfo);
        UNREFERENCED_PARAMETER(pIndirectData);
        return TRUE;
    }


    接下来, 劫持哈希验证处理程序 (连同以前的劫持签名检索功能) 将通过所有的检查, 将未经签名的 PowerShell 代码伪装为已签署Microsoft的代码:


    劫持 PowerShell SIP 的 CryptSIPVerifyIndirectData 组件



    现在, 未签名的 PowerShell 文件出现签名并经过正确验证。


    "数字签名" UI 选项卡显示一个未签名的 PowerShell 文件, 它显示签名并经过正确验证。


    Sysinternals sigcheck 显示一个未签名的 PowerShell 文件, 它显示签名并经过正确验证。

     

    一个更理想的劫持场景是甚至懒得劫持 CryptSIPDllGetSignedDataMsg 的目标 SIP。 相反, 只需应用合法的验证码签名 (例如 从 C:\Windows\System32\WindowsPowerShell\v1.0\Modules\ISE\ise.psm1) 到攻击者提供的代码, 并只劫持 CryptSIPVerifyIndirectData。这样做为攻击者提供了以下好处:

    1. 有更少的劫持和清理工作;

    2. 良性、合法签名的代码将正确应用其各自的签名;

    3. 攻击者提供的带有 "合法" 的嵌入式校验码证书的代码很可能会受到安全产品的严格审查。


    test.ps1 具有和ise.psm1相同的嵌入式校验码签名, 并且证书指纹值相匹配

     

    虽然目前为止示例集中在 PowerShell SIP 上, 但这些劫持原则适用于所有SIP。下面是一个被劫持的可执行文件的SIP (C689AAB8-8E78-11D0-8C47-00C04FC295EE) 的示例, 它将合法的 Microsoft 数字签名应用于攻击者提供的二进制文件上:


    notepad_backdoored.exe拥有应用于本属于notepad.exe (目录签名) 的数字签名

     

    "数字签名" UI 选项卡还确认攻击者-suppled notepad_backdoored. exe 验证为已签名的 Microsoft 文件。

     

    此劫持将骗过任何执行用户模式信任/签名验证的程序, 包括 Sysinternals 的Process Explorer:

    notepad_backdoored.exe 在 Sysinternals 的Process Explorer中显示为“已验证签名"。


    未完待续!




    本文由看雪论坛 龙幽 翻译

    转载请注明来自看雪社区



    热门阅读


    点击阅读原文/read,

    更多干货等着你~



    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存