|
![]() | 作者: TomyChen [quest]
![]() |
登录 |
模拟问题 Michael Howard Secure Windows Initiative 摘要:Michael Howard 介绍了与模拟相关的安全性问题,并以新发布的 Microsoft Windows Server 2003 为例,说明如何保护系统免受此类攻击。 本文假定读者了解模拟的概念,它是指在 Microsoft Windows NT® 及更高版本上运行的应用程序中的线程取得调用方标识的功能。如果您需要了解此功能,请转到 http://msdn.microsoft.com/library/en-us/com/htm/security_6d9q.asp(英文)。模拟是 Windows NT 及更高版本的一个主要功能,基于 Windows 95 代码库的各种 Microsoft Windows® 版本未提供此功能。 对于各种服务而言,使用模拟是很常见的。例如,某一进程以服务帐户(如 Local System [也称为 SYSTEM]、Network Service 或 Local Service)的身份运行,它接受来自客户端的连接并模拟客户端,以模拟帐户的身份访问资源,然后在资源交互完成时恢复进程标识。当服务访问到资源时,将以模拟帐户的身份执行访问检查。 这里存在一个问题,如果模拟功能失败,将会出现什么问题?线程在哪个帐户下运行?假设某个服务以特权帐户的身份运行,例如最高级别的 SYSTEM 帐户。然后用户 Blake 使用命名管道连接到此服务,此服务模拟 Blake 并尝试向具有以下 ACL 的文件写入数据: Administrators(完全控制) Users(读取) 执行此操作的代码与下面的代码(摘自上一篇文章的“发现安全漏洞”的代码片断)有些类似。 bool WritePipeDataToFile(HANDLE hPipe) { bool fDataWritten = false; ImpersonateNamedPipeClient(hPipe); HANDLE hFile = CreateFile(...); if (hFile != INVALID_HANDLE_VALUE) { BYTE buff[1024]; DWORD cbRead = 0; if (ReadFile(hPipe, buff, sizeof(buff), &cbRead, NULL)) { DWORD cbWritten = 0; if (WriteFile(hFile, buff, cbRead, &cbWritten, NULL)) { if (cbRead == cbWritten) fDataWritten = true; } } if (hFile) CloseHandle(hFile); } RevertToSelf(); return fDataWritten; } 很多读者都发现了这段代码中的错误。如果对 ImpersonateNamedPipeClient 的调用失败,将发生什么情况?答案是线程仍然以进程 SYSTEM(Administrators 组的一个成员)的身份运行。也就是说,此服务可以对文件执行写入操作,即使 ACL 只允许管理员执行写入操作。实在太冒险了!改正方法很简单。检查所有模拟函数(包括 SetThreadToken)的返回值,如果失败,则返回一个错误。只需编写如下所示的简单代码即可: if (ImpersonateNamedPipeClient(hPipe) == 0) return GetLastError(); 请注意,我们已经更新了(或者正在更新)许多联机文档和印刷的 Microsoft Visual Studio® 文档,以说明不检查模拟函数返回值会带来的安全隐患。 模拟函数在什么情况下会失败? 您可能会问:“模拟函数在什么情况下会失败?”对于 Microsoft Windows Server 2003,答案要复杂得多。首先,让我们了解一点背景知识。假设您在一台打开命名管道的计算机上安装有缺陷的应用程序。然后说服管理员连接到该管道。当用户连接到此管道时,您模拟该用户,在这种情况下,您的有漏洞的代码潜在地作为该计算机的管理员运行!当然,这种行为违反了首要的安全性永恒法则:“如果一个居心叵测的家伙说服您在您的计算机上运行他的程序,那么这台计算机将不再属于您。”有关这些法则的详细信息,请访问 http://www.microsoft.com/technet/treeview/default.asp?url=/technet/columns/security/essays/10imlaws.asp(英文)。但是,我们决定在 Windows Server 2003 中对体系结构进行更改,方法是添加一个新权限,即在 Candidate 2 及更高版本中进行模拟的功能(http://www.microsoft.com/windows.netserver/preview/default.mspx [英文])。请将这种做法看作是深层的防御方法,也就是说,我们添加它“只是以防万一”。 总而言之,除非您拥有此权限,否则就无法模拟其他帐户。在 Windows Server 2003 中,此类攻击已不再是问题,因为模拟功能只授予具有 Service SID 的帐户(Network Service、Local Service 和 Local System)和 Administrator。 如果帐户未被授予此权限,则以此帐户运行的代码将尝试调用一个模拟函数,代码将不会失败,而是返回一个标识标记。 要使模拟生效,必须符合以下一个或多个条件: 所需的模拟级别小于模拟(即匿名或标识级别,应该始终成功)。 进程标记具有 SeImpersonatePrivilege 权限。 某一进程(或同一登录会话中的另一个进程)通过调用具有显式凭据的 LogonUser 创建标记。其实,如果您知道帐户密码,也可以模拟它们。 标记用于所期望的用户。即,代码正在尝试模拟此进程标识。 COM 结构启动的组件对象模型 (COM) 服务器和配置为在特定帐户下运行的 COM 服务器也会将 Service 组添加到其访问标记中。因此,这些服务将在启动时获得此用户。如果 COM 服务器被标记为“作为激活器激活”,则不适用。 目的是将此权限向后传递到先前的 Windows 版本中,如果客户认为这样做很重要的话。 如果不满足上述所有条件,当代码尝试模拟某一帐户时,应用程序将失败。此问题可以通过向相应的帐户授予模拟权限来解决。但请记住,向所有人授予权限意味着权限不能保护系统免受前面提到的攻击方案的攻击。 如果应用程序执行模拟,则应在 Windows Server 2003 RC2 或更高版本上对其进行测试,以确保所有模拟函数返回值都经过失败检查。 发现安全漏洞 许多人已知道了上个月的安全问题。问题出在下面的一行代码上: char *buff = new char(250); 此代码行创建一个单字节的缓冲区,并将其设置为默认值 250。代码应为: char *buff = new char[250]; 这样更改之后,将按预期要求分配 250 个字节。 这个月的问题: int ConcatString(char *buf1, char *buf2, size_t len1, size_t len2){ char buf[256]; if((len1 + len2) > 256) return -1; memcpy(buf, buf1, len1); memcpy(buf + len1, buf2, len2); // 使用 buf 进行填充 return 0; } -------------------------------------------------------------------------------- Michael Howard 是 Microsoft Secure Windows Initiative 组的高级安全程序经理,是 Writing Secure Code(英文)的作者之一,该书的第二版现已发行。他还是 Designing Secure Web-based Applications for Windows 2000(英文)的主要作者。他的主要工作就是确保人们设计、构建、测试和记录无缺陷的安全系统。他的座右铭是“尺有所短,寸有所长。” |
地主 发表时间: 08/11 15:11 |
|
20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon
粤ICP备05087286号