首页
论坛
课程
招聘
[翻译]颠覆windows的信任体系——实现任意代码签名劫持
2017-10-21 22:38 24930

[翻译]颠覆windows的信任体系——实现任意代码签名劫持

2017-10-21 22:38
24930

背景

在计算机安全领域,什么是信任呢?现代安全解决方案——遇到恶意代码或者恶意操作弹个窗提示下——提供的隐含的安全感?还是企业里对某个工作必需的软件经过认真评估后的信任?实际上,并没有唯一的正确答案。信任本质上是主观的。重要的是,每个组织都要认真考虑在技术层面信任的意义。即便具有成熟信任定义的组织,也应该质疑下由安全解决方案和操作系统验证过的信任是否可信。

 

既然你的脑子里有了关于信任对你意味着什么,不包括涉及人工干预的代码审查,什么是信任验证的技术手段?这显然是一个难以回答的问题,当然你也可能没有问过自己。本白皮书的目的是展示微软的Windows是怎样决策信任的。通过展示如何在Windows中颠覆信任,您将有机会有更多的机会多问问自己,信任对您而言到底意味着什么——一些在安全方面非常重要和不清楚的概念。

 

除了验证签名代码的来源和完整性之外,代码签名和信任验证也是许多安全产品(例如防病毒和EDR解决方案)的重要恶意软件分类组件。适当的信任验证也是大多数应用程序的执行组件白名单解决方案(AppLocker,Device Guard等)。 在许多情况下颠覆Windows的信任架构也有可能颠覆安全产品的功效。

Windows用户模式信任架构

使用Authenticode.aspx)数字签名,可以验证来自于特定供应商的可执行代码的合法性。在用户模式下,验证签名代码的信任的API是WinVerifyTrust.aspx)和WinVerifyTrustEx.aspx)(它只是WinVerifyTrust的包装器,具有更明确定义的函数原型)。

 

随着Windows的更新迭代,同时也有必要扩展签名和信任架构以便支持额外的文件格式和二进制Blob格式,签名可能就需要以不同的格式存储,信任也跟着这种技术以特有的方式进行验证。例如,数字签名以特定的PE文件格式存储二进制格式。PowerShell脚本,从另一方面来看的话也是可以签名的文本文件,因此可以理解其签名需要不同的存储格式。另外,当签名代码时,需要计算要签名的代码的哈希(通常称为签名证书哈希),并且根据文件/ blob格式执行此操作的方式是不同的。可以理解,关于数字签名的认证,设备驱动程序的信任的验证方法与HTTPS证书的信任方式是不同的。

 

考虑到需要支持独特格式的数字签名,并以独特的方式执行信任验证,微软设计了可扩展架构来支持这一点。主题接口包(subject interface package.aspx),SIP)架构旨在支持数字签名的创建,检索和哈希计算、验证。使用信任提供者.aspx)来执行签名代码的信任验证。通过使用WinVerifyTrust和wintrust.dll、crypt32.dll中的各种导出函数, 信任提供者和SIP架构帮助软件开发人员从执行代码签名和信任验证的具体步骤中完全抽象出来。在撰写本文时,没有证据表明该架构的文档已被扩展到可支持第三方软件开发人员希望支持的其特定文件格式的签名。这可能是因为无论格式如何变化任何文件都可以在技术上通过使用目录签名(catalog signing,一种包含可以被认证码签名的文件哈希列表的文件格式)进行“签名”。需要注意的是编录文件的验证只有在“CryptSvc”服务运行的情况下才能生效。

 

除了各种Windows SDK头文件以及零星的关于wintrust.dll和crypt32.dll导出函数的MSDN文档.aspx)外,信任提供者和SIP并没有文档化。由于第三方实施的复杂性,微软可能故意选择对这些架构不进行文档化。该白皮书用于记录信任提供者和SIP架构,同时还解释攻击者如何将其滥用的方式作为破坏信任的手段,并且在执行信任验证的进程的上下文中获取代码执行的权限。

 

本白皮书中主要介绍的是CryptoAPI.aspx#_security_cryptoapi_gly)的可扩展性,主要包括加密编码、解码、证书管理等。微软不可能预见未来的加密要求,所以他们设计了一个完全可扩展的架构(大概可追溯到90年代初),以适应当前和未来的需求。不幸的是,这一非常广泛的可扩展性,有可能允许攻击者(具有提升的权限)来劫持现有的功能。

什么文件可以被签名呢


 

怎么知道一个可执行文件是否被签名呢?一个最简单直接的方法就是右键查看文件属性,然后切换到数字签名选项卡。

 

“数字签名”选项卡显示是否存在嵌入的认证签名
“数字签名”选项卡显示是否存在嵌入的认证签名

 

虽然这种方法可以用来确认某些文件类型是否被签名,如上图所示的ise.psm1(PowerShell脚本模块.aspx)文件)的情况,但这远远不是枚举可签名文件类型的系统方法。对文件类型的签名支持只是SIP(负责数字签名的创建、检索和哈希计算、验证的体系结构)的部分实现。

 

以下是ise.psm1中嵌入签名的一部分:

# SIG # Begin signature block
# MIIXXAYJKoZIhvcNAQcCoIIXTTCCF0kCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUv0M9fHFPOaghmrZBoun/tqPG
# zE6gghIxMIIEYDCCA0ygAwIBAgIKLqsR3FD/XJ3LwDAJBgUrDgMCHQUAMHAxKzAp
# BgNVBAsTIkNvcHlyaWdodCAoYykgMTk5NyBNaWNyb3NvZnQgQ29ycC4xHjAcBgNV
# BAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFJv
# b3QgQXV0aG9yaXR5MB4XDTA3MDgyMjIyMzEwMloXDTEyMDgyNTA3MDAwMFoweTEL
# MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
# bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEjMCEGA1UEAxMaTWlj
# cm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
# ggEKAoIBAQC3eX3WXbNFOag0rDHa+SU1SXfA+x+ex0Vx79FG6NSMw2tMUmL0mQLD
# TdhJbC8kPmW/ziO3C0i3f3XdRb2qjw5QxSUr8qDnDSMf0UEk+mKZzxlFpZNKH5nN
# sy8iw0otfG/ZFR47jDkQOd29KfRmOy0BMv/+J0imtWwBh5z7urJjf4L5XKCBhIWO
# sPK4lKPPOKZQhRcnh07dMPYAPfTG+T2BvobtbDmnLjT2tC6vCn1ikXhmnJhzDYav
...
# HNHPPQanI9HpDNBxWrVzcH6zIV1vBHSeB/tFtZpOI+beHjx7X3d1cyCg5lfERzyQ
# 3jJyjSbMMbz8Pj/1meM0rlWQ/ZnYYiQAtJYqUN3ctT21Uu3ZVVnw46A8voTnSRMd
# 5mVFLFMeFyJkWgsyqLroBTm4U/G+gZ2BB0ImzSbSfIo=
# SIG # End signature block

这就是PowerShell代码中的签名如何存储的(MOF文件是一个例外)。 为了使问题复杂化,可以签名的每种文件类型都以独特的方式存储其签名。 例如,PE签名认证规范解释了如何在PE文件(如EXE、DLL、SYS等)中存储和验证签名。

 

位于crypt32.dll(常通过WinVerifyTrust.aspx)间接调用)中的一个函数CryptSIPRetrieveSubjectGuid.aspx),用于发现与特定文件类型相关联的SIP的功能。 给定文件名和可选句柄,CryptSIPRetrieveSubjectGuid返回一个GUID,表示可以处理检索嵌入认证签名的SIP。 功能大致如下:

  1. 根据文件魔数,尝试确定该文件是PE,编录文件,CTL还是cabinet文件。如果是任何这些文件类型,它将返回以下相应的SIP GUID:
    • C689AAB8-8E78-11D0-8C47-00C04FC295EE -PE
    • DE351A43-8E59-11D0-8C47-00C04FC295EE -Catalog
    • 9BA61D3F-E73A-11D0-8CD2-00C04FC295EE -CTL
    • C689AABA-8E78-11D0-8C47-00C04FC295EE -Cabinet
  2. 如果文件不匹配任何以前的文件类型,它将调用CryptEnumOIDFunction.aspx)函数,传递它的功能名称为“CryptSIPDllIsMyFileType”和“CryptSIPDllIsMyFileType2”。 这些功能分别对应于以下注册表项的查找:
    • HKLM\SOFTWARE\[WOW6432Node]\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllIsMyFileType\<All sub-GUIDs>
    • HKLM\SOFTWARE\[WOW6432Node]\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllIsMyFileType2\<All sub-GUIDs>
      随着CryptEnumOIDFunction枚举每个SIP GUID注册表子项,它将从“FuncName”和“Dll”列出的注册表键值中调用DLL导出函数。

“CryptSIPDllIsMyFileType”的功能原型文档戳我.aspx),“CryptSIPDllIsMyFileType2”的功能原型文档戳我.aspx)。 如果有实现,“CryptSIPDllIsMyFileType”
函数首先被调用,如果其中一个函数返回“TRUE”,则返回处理签名的SIP GUID。 在实践中(至少在Windows 10上),没有SIP实现“CryptSIPDllIsMyFileType”,所以随后调用“CryptSIPDllIsMyFileType2”函数来尝试解决处理SIP。 例如,PowerShell(SIP GUID:603BCC1F-4B59-4E08-B724-D2C6297EF351)将CryptSIPDllIsMyFileType2实现为pwrshsip!PsIsMyFileType。 经过反汇编、反编译及整理输出,这里是PsIsMyFileType函数的C语言版的原型:

#define CRYPT_SUBJTYPE_POWERSHELL_IMAGE \
{ 0x603BCC1F, \
0x4B59, \
0x4E08, \
{ 0xB7, 0x24, 0xD2, 0xC6, 0x29, 0x7E, 0xF3, 0x51 } \
}
BOOL WINAPI PsIsMyFileType(IN WCHAR *pwszFileName, OUT GUID *pgSubject) {
    BOOL bResult;
    WCHAR *SupportedExtensions[7];
    WCHAR *Extension;
    GUID PowerShellSIPGUID = CRYPT_SUBJTYPE_POWERSHELL_IMAGE;
    SupportedExtensions[0] = L"ps1";
    SupportedExtensions[1] = L"ps1xml";
    SupportedExtensions[2] = L"psc1";
    SupportedExtensions[3] = L"psd1";
    SupportedExtensions[4] = L"psm1";
    SupportedExtensions[5] = L"cdxml";
    SupportedExtensions[6] = L"mof";
    bResult = FALSE;
    if (pwszFileName && pgSubject) {
        Extension = wcsrchr(pwszFileName, '.');
        if (Extension) {
            Extension++;
            for (int i = 0; i < 7; i++) {
                if (!_wcsicmp(Extension, SupportedExtensions[i])) {
                    bResult = TRUE;
                    memcpy(pgSubject, &PowerShellSIPGUID, sizeof(GUID));
                    break;
                }
            }
        }
    }
    else
    {
        SetLastError(
            ERROR_INVALID_PARAMETER
        );
    }
    return
        bResult;
}

从C代码中可以看出,如果任何文件有任何上述扩展名,那么PowerShell SIP将是用作代码签名的SIP。 “CryptSIPDllIsMyFileType2”不一定要检查文件扩展名,SIP还可以选择打开文件句柄并检查文件中的魔数值,以确定正确的文件/blob SIP处理顺序。

 

其他支持的SIP文件类型处理函数如下(非详尽列表):

  1. 000C10F1-0000-0000-C000-000000000046
    C:\Windows\System32\MSISIP.DLL
    MsiSIPIsMyTypeOfFile
  2. 06C9E010-38CE-11D4-A2A3-00104BD35090
    C:\Windows\System32\wshext.dll
    IsFileSupportedName
  3. 0AC5DF4B-CE07-4DE2-B76E-23C839A09FD1
    C:\Windows\System32\AppxSip.dll
    AppxSipIsFileSupportedName
  4. 0F5F58B3-AADE-4B9A-A434-95742D92ECEB
    C:\Windows\System32\AppxSip.dll
    AppxBundleSipIsFileSupportedName
  5. 1629F04E-2799-4DB5-8FE5-ACE10F17EBAB
    C:\Windows\System32\wshext.dll
    IsFileSupportedName
  6. 1A610570-38CE-11D4-A2A3-00104BD35090
    C:\Windows\System32\wshext.dll
    IsFileSupportedName
  7. 5598CFF1-68DB-4340-B57F-1CACF88C9A51
    C:\Windows\System32\AppxSip.dll
    P7xSipIsFileSupportedName
  8. 603BCC1F-4B59-4E08-B724-D2C6297EF351
    C:\Windows\System32\WindowsPowerShell\v1.0\pwrshsip.dll
    PsIsMyFileType
  9. 9F3053C5-439D-4BF7-8A77-04F0450A1D9F
    C:\Windows\System32\EsdSip.dll
    EsdSipIsMyFileType
  10. CF78C6DE-64A2-4799-B506-89ADFF5D16D6
    C:\Windows\System32\AppxSip.dll
    EappxSipIsFileSupportedName
  11. D1D04F0C-9ABA-430D-B0E4-D7E96ACCE66C
    C:\Windows\System32\AppxSip.dll
    EappxBundleSipIsFileSupportedName

对于读者来说, 逆向上面的某些函数来查看Windows所支持代码签名的文件或二进制blob的类型,这将是一个很有价值的练习。

 

一旦需要检索签名的软件获得该 SIP 的 GUID, 那它就可以继续提取该证书。

文件签名检索和哈希验证

一旦负责处理特定文件/二进制Blob格式的签名的SIP通过其各自的GUID标识符被识别,WinVerifyTrust 就会知道如何从该文件中获取数字签名并验证其计算出的哈希对嵌入在数字中的签名哈希签名。为实现这一点, WinVerifyTrust 在注册表中调用以下函数:

 

SIP 签名检索功能位置:

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

SIP 哈希验证函数:

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

CryptSIPDllGetSignedDataMsg.aspx)和CryptSIPDllVerifyIndirectData.aspx)的函数原型在MSDN有文档,同样也存在于Windows SDK中的mssip.h头文件中。

 

SIP签名检索功能原型:

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

SIP 哈希验证函数原型:

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

SIP_SUBJECTINFO.aspx) SIP_INDIRECT_DATA.aspx)
1387/5000
提供给这些函数的参数由调用信任提供者负责填充(有关信任提供者架构的更多细节,请参见以下部分)。当CryptSIPGetSignedDataMsg被调用时,SIP将提取编码的数字签名(最常用的是CERT_SIGNED_CONTENT_INFO.aspx)结构体,ASN.1 PKCS_7_ASN_ENCODING和X509_ASN_ENCODING编码),并通过“pbSignedDataMsg”参数返回。CERT_SIGNED_CONTENT_INFO内容由签名证书(包括其发行链)、用于对文件进行哈希和签名的算法以及文件的签名散列组成。调用信任提供者然后对数字签名进行解码,提取散列算法和签名哈希值,然后将它们传递给CryptSIPVerifyIndi​​rectData。在校验认证码哈希计算并与已签名哈希进行比较后,如果匹配,则CryptSIPVerifyIndirectData返回TRUE,否则返回FALSE,然后WinVerifyTrust将返回一个错误,表明哈希不匹配。

 

CryptSIPVerifyIndirectData是最重要的数字签名验证功能之一,但这将会犯错:因为攻击者可以将现有的合法数字签名应用于其恶意软件——这是一种在野的攻击技术。
以下是一个适用于合法认证码签名的恶意软件示例的哈希失真的示例:
在使用微软认证码签名的未签名文件上显示哈希不匹配错误的示例(注意相同的SignerCertificate指纹值)
在使用微软认证码签名的未签名文件上显示哈希不匹配错误的示例(注意相同的SignerCertificate指纹值)

 

未签名的文件在应用于签名文件的认证码签名时无法验证。微软就是这么设计的。
未签名的文件在应用于签名文件的认证码签名时无法验证。微软就是这么设计的。

信任提供者架构

到目前为止,已经讨论了SIP的基本架构。如现在应该理解的,SIP仅负责数字签名应用、检索和散列计算、验证。应用于文件的数字签名的存在是无意义的,除非某些标准被实际验证。这就是信任提供者发挥作用的地方——除了内置到所需的信任提供者中的标准之外,还可以根据调用者指定的参数组合对WinVerifyTrust进行验证。

 

像SIP一样,信任提供者也由GUID唯一标识。 截至到Windows 10,有以下信任提供者存在:

GUID 描述
A7F4C378-21BE-494e-BA0F-BB12C5D208C5 UNKNOWN .NET VERIFIER
7801EBD0-CF4B-11D0-851F-0060979387EA CERT_CERTIFICATE_ACTION_VERIFY
6078065B-8F22-4B13-BD9B-5B762776F386 CONFIG_CI_ACTION_VERIFY
D41E4F1F-A407-11D1-8BC9-00C04FA30A41 COR_POLICY_LOCKDOWN_CHECK
D41E4F1D-A407-11D1-8BC9-00C04FA30A41 COR_POLICY_PROVIDER_DOWNLOAD
31D1ADC1-D329-11D1-8ED8-0080C76516C6 COREE_POLICY_PROVIDER
F750E6C3-38EE-11D1-85E5-00C04FC295EE DRIVER_ACTION_VERIFY
573E31F8-AABA-11D0-8CCB-00C04FC295EE HTTPSPROV_ACTION
5555C2CD-17FB-11d1-85C4-00C04FC295EE OFFICESIGN_ACTION_VERIFY
64B9D180-8DA2-11CF-8736-00AA00A485EB WIN_SPUB_ACTION_PUBLISHED_SOFTWARE
C6B2E8D0-E005-11CF-A134-00C04FD7BF43 WIN_SPUB_ACTION_PUBLISHED_SOFTWARE_NOB
189A3842-3041-11D1-85E1-00C04FC295EE WINTRUST_ACTION_GENERIC_CERT_VERIFY
FC451C16-AC75-11D1-B4B8-00C04FB66EA0 WINTRUST_ACTION_GENERIC_CHAIN_VERIFY
00AAC56B-CD44-11D0-8CC2-00C04FC295EE WINTRUST_ACTION_GENERIC_VERIFY_V2
573E31F8-DDBA-11D0-8CCB-00C04FC295EE WINTRUST_ACTION_TRUSTPROVIDER_TEST
 

信任提供者的部分组件的声明在MSDN和windowsSDk的SoftPub.h的文档中能找到,但是它们的实现并没有文档化。对开发人员而言,这就需要从信任证书、签名、信任链、吊销和时间戳正确执行验证。开发人员调用WinVerifyTrust使用的更常见的信任提供程序之一是WINTRUST_ACTION_GENERIC_VERIFY_V2以用来确认通用校验码签名。如果需要在用户模式下验证驱动程序的可信性任, 则应使用 DRIVER_ACTION_VERIFY。

 

与 sip 一样, 信任提供程序也在注册表中注册了以下项:

- HKLM\\SOFTWARE\\[WOW6432Node\\]Microsoft\\Cryptography\\Providers\\Trust

在"信任"键中,是一个子项列表,对应于可能发生的每个信任提供程序验证步骤:初始化(Initialization)、消息(Message)、签名(Signature)、证书(Certificate)、认证检查(CertCheck)、最终策略(FinalPolicy)、诊断策略(DiagnosticPolicy)和清理(Cleanup)。其中的每个密钥都是实现每个步骤的信任提供程序 guid (不是所有的都是必需的. 例如, 证书检查、诊断策略和清理)。在每个各自的 GUID 子项中, 都是由注册表里的 dll 和导出函数来实现信任提供程序步骤的$DLL$Function
在注册表中注册的信任提供者的示例
在注册表中注册的信任提供者的示例

 

每个信任提供程序步骤的用途可以大致细分如下:

  1. 初始化:
    a. 初始化 CRYPT_PROVIDER_DATA.aspx) 结构体(该结构体基于WINTRUST_DATA.aspx) 结构体),然后传递给WinVerifyTrust。CRYPT_PROVIDER_DATA是在所有信任提供程序函数之间传递的结构体,用于在所有调用中维护状态,包括可能在执行过程中的每个步骤的错误代码 (请参见 wintrust 中的 TRUSTERROR_STEP 值)。
    b. 打开要验证的文件的只读句柄。
  2. 消息:
    a. 从主题接口包中获取签名者信息。这是验证过程中的唯一步骤,它调用各自的SIP以获取正确的签名。请注意,在尝试从嵌入的验证码签名获取签名之前,某些信任校验实用程序将首先检查签名的目录存储。
    b. "初始化" 和 "消息" 步骤都被称为 "对象提供程序"。
  3. 签名:
    a. 在此步骤中, 将生成数字签名, 并验证 counter-signers 和时间戳。
    b. 这一步骤被称为“签名提供者”。
  4. 证书:
    a. 这一步中,整个证书链将被生成。
    b. 这一步被称为“证书提供者”。
  5. 认证检查:
    a. 如果实现此可选步骤, 则为证书链中的每个索引调用此函数, 并用于向信任提供者指示证书链应继续构建。
  6. 最终策略:
    a. 这是大多数信任决策的作用。此时, 签名和证书链已被解码、解析并提供给这个实现函数。
    b. 验证签名、证书链和证书存储的哪些组件因信任提供程序而异。下面是使用 WINTRUST_ACTION_GENERIC_VERIFY_V2 信任提供程序时发生的一些检查的小列表 (实现 WINTRUST!SoftPubAuthenticode):
     i. 验证文件是否已使用指定的代码签名证书(由 "1.3. 6.1. 5.5. 7.3. 3" 的增强密钥用法 (EKU) 表示)进行签名。
     ii. 检查证书是否已过期,是否有时间戳。
     iii. 检查证书是否被吊销。
     iv. 验证文件是否使用“弱”哈希算法进行了签名。
     v. 如果文件是指定为 "Windows 系统证书签名的组件验证 "(EKU-1.3. 6.1. 4.1. 311.10.3. 6), 则验证签名证书链到一组固定的受信任的 Microsoft 根证书。
    
  7. 诊断策略:
    a. 此可选步骤旨在帮助信任提供程序开发人员进行调试。它的目的是让微软的开发者在返回到 WinVerifyTrust 之前可以把结构体内容dump出来。
    b. WINTRUST_ACTION_TRUSTPROVIDER_TEST是实现此步骤的唯一信任提供程序。WINTRUST_ACTION_TRUSTPROVIDER_TESTWINTRUST_ACTION_GENERIC_VERIFY_V2是相同的,不过它是实现wintrust!SoftpubDumpStructure的一个额外步骤。SoftpubDumpStructure将填充的 CRYPT_DATA_PROVIDER 结构转储到 C:\TRUSTPOL.txt。从命令提示符(需要有写入c盘的权限)使用 signtool.exe (在 Windows SDK 中) 可以轻松地测试此步骤。指定WINTRUST_ACTION_TRUSTPROVIDER_TEST (认证码测试) 信任提供程序的GUID:
    i. signtool verify /pg {573E31F8-DDBA-11D0-8CCB-00C04FC295EE} filename.exe
  8. 清理:
    a. 在此可选步骤中, 信任提供程序可以清除所有已填充的 CRYPT_PROVIDER_PRIVDATA结构体, 以便跨信任提供程序步骤传递特定策略的数据。

    信任提供者和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 和信任提供程序注册一样, 这些实现函数也存储在注册表中:

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

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

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

    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

  2. 接下来, 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

  3. 最后, 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

  4. 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之间的交互说明
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 条目替换为其恶意功能。首先, 了解每个参数的含义是很重要的:

  1. pSubjectInfo:从调用信任提供者传入的结构体指针, 包含有关提取签名的文件的所有相关信息。这里是一个例子:传递给 pwrshsip!PsGetSignature (PowerShell SIP的 CryptSIPDllGetSignedDataMsg组件)结构体的转储(dump):
    pSubjectInfo dump
  2. pdwEncodingType:在从pSubjectInfo中指定的文件检索数字签名时,此参数指示调用函数(信任提供者"消息"组件)如何正确解码返回数字签名。最常见是的PKCS_7_ASN_ENCODING 和 X509_ASN_ENCODING 一起进行二进制或运算 。
  3. dwIndex: 此参数应为零, 但理论上 SIP 可以包含多个嵌入的签名, dwIndex 表示从指定文件中提取哪一个数字签名。
  4. pcbSignedDataMsg: 通过 pbSignedDataMsg 返回的数字签名的长度 (以字节为单位)。
  5. 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 劫持的演示
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 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中显示为“已验证签名"。

绕过 UMCI 设备保护 执行

在应用程序白名单方案中, 使用未经签名的/未经批准的二进制文件来验证信任的机制,构成了一个 "鸡和蛋" 问题,即需要根据部署的恶意 SIP DLL 的信任来验证白名单政策。结果是, 至少使用设备保护,系统将无法加载恶意的 SIP DLL, 不过这将导致信任验证在许多情况下失败。这可以理解地有可能导致系统稳定性问题。理想情况下 (对于攻击者) 将有一个可以为 CryptSIPVerifyIndirectData 角色提供服务的签名 DLL。幸运的是,回想一下,CryptSIPVerifyIndirectData 函数接受以下函数签名:

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

此外, 为了通过验证检查, 函数必须返回 TRUE。因此, 我们面临以下要求, 以产生一个签名的CryptSIPVerifyIndirectData 函数:

  1. Dll文件必须有签名;
  2. 函数必须接受两个参数;
  3. 函数必须使用WINAPI/stdcall 调用规范;
  4. 函数必须返回TRUE(通常为非零数或者奇数);
  5. 函数不能改变传入的参数, 因为这可能导致内存损坏;
  6. 除了返回 "TRUE" 之外, 该函数最好没有其他意料之外的影响;
  7. 函数必须导出。

毫无疑问, 这样一个查找候选函数的过程可以通过将函数转换为中间语言来进行自动分析, 而不需要很长时间就能找到候选输出函数-ntdll!DbgUiContinue:

 

 

ntdll!DbgUiContinue的反汇编及注释

 

只需将目标 SIP 的 CryptSIPVerifyIndirectData 注册表项设置为 C:\Windows\System32\ntdll.dllDbgUiContinue,就足以通过对任何应用了合法的嵌入校验码签名的代码进行哈希校验检查。实际上,在对强制启用设备保护的系统上的可执行文件 SIP 进行测试时, 攻击者提供的代码被阻止执行。但是, 劫持 PowerShell SIP 启用了受约束的语言模式绕过, 从而实现了任意的、无签名的代码执行。不过,对于使用可执行文件与 PowerShell 代码进行的其他 (可能是内核支持的) 信任断言, 还不清楚。也有可能存在比DbgUiContinue更好用的劫持函数 , 但这足以证明攻击者提供的无签名的 SIP DLL足够可以用来劫持。

 

下面的示例演示了PowerShell在启用了设备保护的约束语言模式下,防止在发生劫持事件之前执行添加类型,并防止 CryptSIPVerifyIndirectData 在被劫持之后进行后续旁路操作:

 

在劫持之前,由于受限的语言模式,test.psm1中的代码将被阻止执行

 

在劫持之前,由于受限的语言模式,test.psm1中的代码将被阻止执行

 

 

在 "签名代码重用" 攻击发生之后,将绕过受约束的语言模式。

 

尽管这种形式的劫持并不代表完全接管强制设备保护用户模式完整性 (UMCI),但它确实从隐形角度提出了一种良好的劫持方法,因为它不需要攻击者将任何恶意代码丢弃到磁盘-即攻击者提供 SIP。

信任提供者“最终策略”(FinalPolicy )劫持

正如在 "信任提供者体系架构" 部分中所描述的那样, 最终的信任决策由信任提供程序的 最终策略组件进行。这是 FinalPolicy 的函数签名:

HRESULT WINAPI FinalPolicyFunction(_Inout_ struct _CRYPT_PROVIDER_DATA *pProvData);

FinalPolicy 为各自的信任提供者实现功能,其位于这里:

 

HKLM\SOFTWARE\[WOW6432Node\]Microsoft\Cryptography\Providers\Trust\FinalPolicy\{trust provider GUID}

 

虽然攻击者可以选择实现自己的信任提供程序 DLL 来颠覆 FinalPolicy,但这需要攻击者将恶意代码在硬盘落地。此外, 与 SIP 相比, 完全实现信任提供者的接口比较复杂。不过,如前所述,可以用已签名代码来劫持 FinalPolicy,以此来模拟传递其所有检查。备选的已签名的劫持函数需要满足以下要求:

  1. DLL 必须有签名;
  2. 函数必须只接受一个参数;
  3. 函数必须使用WINAPI/stdcall 调用规范;
  4. 函数的返回结果必须是 0 (S_OK) ,以表示HRESULT成功;
  5. 函数不能改变传入的参数, 因为这可能导致内存损坏;
  6. 除了返回 0之外, 该函数最好没有别的影响;
  7. 函数必须被导出。

未实现的导出函数 wintrust!SoftpubCleanup 满足了执行劫持的所有要求。

 

SoftpubCleanup函数的反汇编

 

SoftpubCleanup函数的反汇编

 

转化为C语言的话,等效于下面的内容:

HRESULT WINAPI SoftpubCleanup(CRYPT_PROVIDER_DATA *data)
{
    return S_OK;
}

例如, 设置 WINTRUST_ACTION_GENERIC_VERIFY_V2 (00AAC56B-CD44-11D0-8CC2-00C04FC295EE)
的 FinalPolicy 组件将导致许多签名验证工具 (如AuthenticodeSignature、sigcheck、signtool 等)认为未签名的代码或应用合法签名的代码作为受信任的。在实践中, 使用 SoftpubCleanup 执行此劫持会导致进程资源管理器 (procexp)必现崩溃。

躲避Autoruns的检测

将合法的 microsoft 校验码数字签名应用于攻击者提供的代码劫持目标 SIP 的 CryptSIPVerifyIndirectData 组件的额外影响是, 默认情况下, 它将从启动中隐藏, 而不显示 "microsoft" 或 "Windows"项。

 

随着可执行程序 SIP 劫持完成,默认情况下,一个持久的攻击者提供的 EXE 不会出现:

 

在Autoruns的默认视图中看不到notepad_backdoored.exe

 

在Autoruns的默认视图中看不到notepad_backdoored.exe

 

但是, 当 取消选择"隐藏 Microsoft 条目" 和 "隐藏 Windows 条目" 时, Run 键中的恶意条目将变为可见:

 

 

确认只有在取消选择 "隐藏 Windows 条目" 时才会出现 notepad_backdoored.exe

持久化和代码执行

了解了如何劫持 SIP和信任提供者,应该清楚的是,除了颠覆信任之外,这些劫持攻击还允许在执行代码签名或签名验证的任何应用程序的上下文中执行代码。通过实现 SIP 或信任提供程序,代码可能在下列列表的程序中执行:

  1. DllHost.exe——当在文件属性中显示“数字签名”标签时
  2. Process Explorer——当显示“签名校验”标签时
  3. Autoruns
  4. Sigcheck
  5. consent.exe——任何时候显示UAC弹窗时
  6. signtool.exe
  7. smartscreen.exe
  8. Get-AuthenticodeSignature
  9. Set-AuthenticodeSignature
  10. 基于对 WinVerifyTrust 的调用执行证书验证的安全供应商软件

可以通过在进程监视器(Process Monitor)中筛选下列注册表项路径来发现其他的代码执行和持久化:

  • HKLM\SOFTWARE\Microsoft\Cryptography\Providers
  • HKLM\SOFTWARE\WOW6432Node\Microsoft\Cryptography\Providers
  • HKLM\SOFTWARE\Microsoft\Cryptography\OID
  • HKLM\SOFTWARE\WOW6432Node\Microsoft\Cryptography\OID

在使用攻击者提供的代码劫持信任提供程序时,出于稳定性考虑,一种可能的是将恶意逻辑作为 "诊断策略(DiagnosticPolicy)" 组件的一部分来实现, 以不影响合法的信任功能。

 

当试图在 SIP 的上下文中获取代码执行时, 一种获取代码执行考虑可能是在 "CryptSIPDllIsMyFileType" 组件中实现恶意逻辑, 并返回 "FALSE",表示其他 "CryptSIPDllIsMyFileType" 和 "CryptSIPDllIsMyFileType2 "组件应该被调用,以确定哪个 SIP 代表了有问题的文件。然而, 要注意的是, 任何武器化方案都有它自己独特的一套指标或折中方案来确保恶意代码可以被签名。

 

最后一个考虑是, SIP 和信任提供者 dll 不需要在注册表中指定它们的完整路径。如果只指定了 SIP 或信任提供程序文件名, 则通过标准的 DLL 加载顺序来加载它。这使攻击者能够在不需要修改注册表的情况下劫持现有的 SIP/信任提供程序 dll。例如, 在 Windows 10 中, Microsoft Office SIP VBA 宏 sip (9FA65764-C36F-4319-9737-658A34585BB7) 只使用其文件名注册 (仅限 WoW64): mso.dll。此外, 仅在指定了 "mso.dll" 的文件名的情况下, 在执行用户模式信任验证的任何代码中都有可能出现泛型 dll 加载顺序劫持漏洞。

颠覆 CryptoAPI v2 (CAPI) 事件日志

虽然默认情况下未启用,但是启用 Microsoft-Windows-CAPI2/Operational 事件日志可能是获取失败的信任验证相关的上下文信息的宝贵来源。每当调用 WinVerifyTrust 时,都会生成 EID 81,如果签名或信任验证失败, 则事件将被填充为错误。例如, 以下是与 "notepad_backdoored" 的失败的信任验证相关的事件详细信息,它具有合法的 Microsoft 认证数字签名 (相关部分加粗):

 

 

上面的事件是一个 "错误" 事件。在本例中, 如果可执行程序 SIP 的 CryptSIPVerifyIndirectData 组件被劫持, 则 WinVerifyTrust 事件仍将被记录, 但作为 "信息" 事件表示信任验证成功:

 

因此, 虽然 Microsoft-Windows-CAPI2/Operational 事件日志可以提供有价值的攻击上下文 (主要是文件路径和验证过程的名称)信息,但它的预期行为可以被信任验证攻击来破坏掉。

攻击操作注意事项

以下建议旨在帮助在实现恶意 SIP 时缓解检被检测到的可能性:

  • 如果 SIP被用来劫持现有的 SIP功能,请实现与您劫持的函数相同的函数名,这样就没必要再更改 "FuncName" 注册表值了。
  • 虽然建议不要将合法的 SIP 二进制文件替换为你自己的 (例如 wintrust.dll),但最好让你的SIP dll 与被劫持的dll 同名。除了具有相对路径 (例如 WoW64 mso.dll) 的 SIP 注册之外,还需要更改 "dll" 注册表值。更改 "dll" 值的最不可疑的方法是更改从 "dll" 中剥离文件路径,并把自己的 SIP Dll 放在目标应用程序的当前目录中。 例如, 将 "C:\Windows\System32\WINTRUST.dll" 更改为 "WINTRUST.dll"。请注意, wintrust.dll 不存在于 KnownDlls 中。
  • 如果实现完整的 SIP (如具有适当的注册/注销功能), 请注意与 SIP 操作相关的函数相对容易生成Yara签名。考虑直接通过注册表执行 SIP 注册/劫持。例如, 以下导入将为一个良好的Yara 规则:
    • CryptSIPAddProvider
    • CryptSIPRemoveProvider
    • CryptSIPLoad
    • CryptSetOIDFunctionValue
    • CryptRegisterOIDFunction
  • 如果你的SIP DLL直接操作注册表的Microsoft\Cryptography\OID键值,则需要混淆下子键的路径。
  • 对于计划使用 SIP dll 劫持的合法 dll,请将其校验码签名应用于二进制文件。尽管存在哈希不匹配,理想情况下,仍可以劫持 CryptSIPVerifyIndirectData SIP 组件来缓解此问题。需要注意的是,许多系统二进制文件都是编录签名的。不过你也可以将编录签名应用于内嵌的校验码签名。 应用同一证书将产生相同的指纹计算,并绕过安全产品可能执行的一些简单检查。
  • 如果要注册一个新的 SIP GUID,请使用以前定义过的一个但是当前未注册的文件,并应用与所使用的SIP GUID 相同的文件名和导出函数名称。例如, Silverlight 具有以下 GUID 的 SIP: BA08A66F-113B-4D58-9329-A1B37AF30F0E

    • 文件名:xapauthenticodesip.dll
    • 导出函数:XAP_CryptSIPCreateIndirectData, XAP_CryptSIPGetSignedDataMsg, XAP_CryptSIPPutSign
      edDataMsg,XAP_CryptSIPRemoveSignedDataMsg,XAP_CryptSIPVerifyIndirectData,XAP_IsFileSupportedName

第五届安全开发者峰会(SDC 2021)10月23日上海召开!限时2.5折门票(含自助午餐1份)

收藏
点赞0
打赏
分享
最新回复 (21)
雪    币: 301
活跃值: 活跃值 (121)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
noNumber 活跃值 2 2017-10-22 08:59
2
0
太长了…………    不知道实战有没有效果, 
话说各种杀毒卫士验证数字签名的方式是否和系统用的同一套流程呢.?
雪    币: 342
活跃值: 活跃值 (32)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
龙幽 活跃值 1 2017-10-22 09:55
3
0
noNumber 太长了………… 不知道实战有没有效果, 话说各种杀毒卫士验证数字签名的方式是否和系统用的同一套流程呢.?
应用层全过,数字会拦截注册表操作,不知道其他的怎么样,老外的文档我应该精简下的
雪    币: 406
活跃值: 活跃值 (580)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
AperOdry 活跃值 2017-10-22 10:00
4
0
感谢分析。
雪    币: 10448
活跃值: 活跃值 (4160)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
hzqst 活跃值 3 2017-10-22 10:49
5
0
不知道CI.DLL是怎么跑的,我觉得用CI.DLL来校验在恶意程序不加载驱动的情况应该是最安全的
雪    币: 107
活跃值: 活跃值 (134)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yangya 活跃值 2017-10-22 15:46
6
0
龙幽 应用层全过,数字会拦截注册表操作,不知道其他的怎么样,老外的文档我应该精简下的
咋没有原文链接。
雪    币: 114
活跃值: 活跃值 (163)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
topofall 活跃值 2017-10-25 10:32
7
0
借鸡生蛋,很有借鉴意,放一下原文链接
雪    币: 388
活跃值: 活跃值 (398)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
wowocock 活跃值 1 2017-10-25 17:12
8
0
数字签名对于安全软件来说只是验证的一个步骤,而非全部。当然出于效率原因会有一个白签名列表,一些比较有信用的大公司什么的可能会在列表里,其他的即使是正规签名,包括个人签名什么的,都会有额外的验证流程,说实话,加不加签名对安全软件的影响不大。
雪    币: 6817
活跃值: 活跃值 (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
聖blue 活跃值 2017-10-25 20:42
9
0
不错!
雪    币: 87
活跃值: 活跃值 (119)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
liucq 活跃值 2017-10-25 22:44
10
0
很有借鉴意义
雪    币: 69
活跃值: 活跃值 (239)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Archar 活跃值 2017-10-26 00:23
11
0
雪    币: 688
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
maomaolk 活跃值 2017-11-15 20:16
12
0
mark
雪    币: 1065
活跃值: 活跃值 (49)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ielts 活跃值 2017-12-9 19:14
13
0
mark
雪    币: 519
活跃值: 活跃值 (14)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
lcz 活跃值 2 2018-4-23 17:25
14
0
雪    币: 360
活跃值: 活跃值 (95)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
babalove 活跃值 2018-5-9 13:36
15
0
CRYPT_PROVIDER_DATA.aspx    这里错了把    ,结构体怎么变成aspx了
雪    币: 342
活跃值: 活跃值 (32)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
龙幽 活跃值 1 2018-5-18 17:45
16
0
babalove CRYPT_PROVIDER_DATA.aspx 这里错了把 ,结构体怎么变成aspx了
看雪解析markdown有bug,把链接解析错了
https://kevin-richael.github.io/
雪    币: 1
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
一行代码 活跃值 2018-5-18 17:46
17
0
https://specterops.io/assets/resources/SpecterOps_Subverting_Trust_in_Windows.pdf
原文链接
雪    币: 153
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
黑白程式 活跃值 2018-6-12 14:02
18
0
长见识了。
雪    币: 1065
活跃值: 活跃值 (49)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ielts 活跃值 2018-9-24 09:04
19
0
长见识了
雪    币: 25
活跃值: 活跃值 (31)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
elfbin 活跃值 2019-9-11 11:20
20
0
感觉机器翻译的,读着好难受...但是有胜于无,支持一下!
雪    币: 342
活跃值: 活跃值 (32)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
龙幽 活跃值 1 2019-12-17 11:32
21
0
不是,是我英文太渣渣了,要不看原文吧,个人感觉也属于比较学术风的
最后于 2019-12-17 11:32 被龙幽编辑 ,原因:
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
git_95964zhangli1245 活跃值 2021-2-2 16:40
22
0
mark
游客
登录 | 注册 方可回帖
返回