下一代密码术 ( CNG ) 内核模式驱动程序 (cng.sys) 无法正确验证和强制执行模拟级别时,它存在安全功能绕过漏洞。攻击者可以通过诱使用户运行特制的应用程序来利用此漏洞,该应用程序旨在导致 CNG 不正确地验证模拟级别,从而可能使攻击者获得对超出本地用户访问级别的信息的访问权限。该安全更新通过更正内核模式驱动程序验证和实施模拟级别的方式来解决该漏洞。
此漏洞已公开披露。它已被分配通用漏洞和暴露编号CVE-2015-2010。
函数 CryptProtectMemory 允许应用程序为以下三种情况之一加密内存:进程、登录会话和计算机。当使用登录会话选项(CRYPTPROTECTMEMORY_SAME_LOGON 标志)时,加密密钥是根据登录会话标识符生成的,这是为了在同一登录中运行的进程之间共享内存。由于这也可用于将数据从一个进程发送到另一个进程,因此它支持从模拟令牌中提取登录会话 ID。
问题是 CNG.sys 中的实现在捕获登录会话 id(使用 SeQueryAuthenticationIdToken)时不检查令牌的模拟级别,因此普通用户可以在标识级别模拟并解密或加密该登录会话的数据。
CryptProtectMemory 是一个 API,您应该使用它来保护临时数据,例如内存中的密码或加密密钥。内存的实际密钥存储在内核中,因此用户模式程序不应直接访问它。保护分为三个级别:
此问题仅影响 2,如果您使用与另一个登录会话(例如本地系统的会话)绑定的令牌进行模拟,您可以加密和解密内存。内核驱动程序缺少模拟级别检查,因此普通系统用户可以在标识级别模拟并解密在本地系统登录会话中加密的数据。此模拟级别不为普通用户提供任何额外的权限。
问题描述没有提供可访问加密数据的具体示例,但它可能会在许多地方泄漏,例如共享内存部分或临时文件。因此,问题的最终严重性取决于哪些数据可以被解密或加密,以及您可以用它做什么(想想数据是否是另一个用户的密码)。
我们这里以windows 7的x86的补丁分析,补丁解开之后的目录列表如下:
因为MS15-010涉及到多个漏洞,我们这里的CVE-2015-0010仅仅涉及到lsa,查看lsa的目录的文件列表如下:
重点查看补丁文件为:
主要包括一个更新函数:
更新前
int __stdcall CngEncryptMemory(void *a1, unsigned int a2, int a3, int a4)
{
......
struct _SECURITY_SUBJECT_CONTEXT SubjectContext;
......
SeCaptureSubjectContext(&SubjectContext);
SeLockSubjectContext(&SubjectContext);
v5 = SubjectContext.ClientToken;
if ( !SubjectContext.ClientToken )
v5 = SubjectContext.PrimaryToken;
v6 = SeQueryAuthenticationIdToken(v5, &AuthenticationId);
SeUnlockSubjectContext(&SubjectContext);
SeReleaseSubjectContext(&SubjectContext);
......
}
更新后
int __stdcall CngEncryptMemory(void *a1, unsigned int a2, int a3, int a4)
{
......
struct _SECURITY_SUBJECT_CONTEXT SubjectContext;
......
SeCaptureSubjectContext(&SubjectContext);
SeLockSubjectContext(&SubjectContext);
v5 = SubjectContext.ClientToken;
if ( !SubjectContext.ClientToken )
{
v5 = SubjectContext.PrimaryToken;
goto LABEL_14;
}
if ( SubjectContext.ImpersonationLevel > SecurityIdentification )
{
LABEL_14:
v6 = SeQueryAuthenticationIdToken(v5, &AuthenticationId);
goto LABEL_15;
}
v6 = -1073741659;
LABEL_15:
SeUnlockSubjectContext(&SubjectContext);
SeReleaseSubjectContext(&SubjectContext);
if ( v6 < 0 )
return v6;
......
}
SeQueryAuthenticationIdToken这个函数是这个漏洞的关键,反编译ntoskrnl.exe之后看一下这个函数的代码:
NTSTATUS __stdcall SeQueryAuthenticationIdToken(PACCESS_TOKEN Token, PLUID AuthenticationId)
{
*AuthenticationId = (struct _LUID)*((_QWORD *)Token + 3);
return 0;
}
这个函数的执行非常简单,也符合作者的说法,它没有做任何模拟级别的判断,只是从Token所在的地址的偏移地址上找到AuthenticationId字段。
这是一个漏洞攻击面,下一个漏洞分析,还会介绍一个类似的漏洞。
我们在理解Windows的安全体系的时候,要仔细分析,它有多个层面多个维度的安全防护。工程师在写代码的时候,他不一定能过理解所有层面所有维度的安全防护方案。
https://bugs.chromium.org/p/project-zero/issues/detail?id=128
https://learn.microsoft.com/en-us/security-updates/SecurityBulletins/2015/ms15-010
Security Update for Windows 7 (KB3023562)
https://www.microsoft.com/en-us/download/details.aspx?id=45600