解决两个难懂的安全性问题
经常讨论一些您发现的少量错误并让人们知道它们是很有益的。在本月的专栏中,我想讨论两个主题:
交互式服务
调用 _alloca()
安全性、服务和交互式桌面
与 Unix 守护程序类似,服务是 Microsoft Windows NT 的中枢,可以向操作系统和用户提供重要功能而无需用户的参与。创建服务时,有一些问题需要注意。
Microsoft Windows 中的服务通常是控制台应用程序,它们的运行无需用户参与,也没有用户界面。但在某些实例中,服务可能需要与用户进行交互。运行在较高安全环境中的服务(如 SYSTEM)不应作为交互式服务运行。在 Windows 用户界面中,桌面是安全边界,在交互式桌面上运行的任何应用程序可以与交互式桌面上的任何窗口交互,即使窗口并不可见。无论创建窗口的应用程序的安全环境和应用程序的安全环境如何,都是这样。
由于这些设计特点,任何在交互式桌面上打开窗口的服务都会向登录用户所执行的应用程序公开。如果服务试图使用窗口消息控制其功能,则登录用户可以通过使用恶意消息来干扰该功能。
将服务作为 SYSTEM 运行的做法(即,服务通过调用 OpenWindowStation 和 GetThreadDesktop 来支持交互式桌面)十分不可取。请注意,Windows 将来的版本可能会完全取消对交互式服务的支持。
我们建议服务编写人员使用客户端/服务器技术(例如 RPC、套接字、命名管道或 COM)实现与来自某个服务的登录用户的交互,使用带 MB_SERVICE_NOTIFICATION 的 MessageBox 显示简单的状态。如果您的服务代码具有以下任何属性,请提高警惕:
作为 LocalSystem 运行,并且服务在安全配置管理器中进行了标记(“登录为”->“允许服务与桌面交互”),或注册表项 -> HKLMCCSServicesMyServiceType & 0x0100 == 0x0100)
CreateService,并且 dwServiceType & SERVICE_INTERACTIVE_PROCESS == SERVICE_INTERACTIVE_PROCESS
调用 MessageBox(),其中 uType and (MB_DEFAULT_DESKTOP_ONLY | MB_SERVICE_NOTIFICATION | MB_SERVICE_NOTIFICATION_NT3X) != 0
调用 OpenDesktop("winsta0",...) 并在该桌面上创建用户界面
在 OpenDesktop 上调用 LoadLibrary/GetProcAddress
小心 _alloca
_alloca 函数可以在堆栈中分配动态内存。分配的空间将在调用函数退出时自动释放,而不只是在分配超出范围时释放。下面是使用 _alloca 的示例代码:
void function(char *szData) {
PVOID p = _alloca(lstrlen(szData));
// 使用 p
}
如果攻击者提供一个比堆栈大小还要长的 szData,_alloca 会引发一个异常并导致应用程序停止。如果该代码位于服务器中,则情况会更糟。处理这种错误情况的正确方法是将对 _alloca 的调用打包在异常处理程序中,并在出现错误时重置堆栈。
void function(char *szData) {
__try {
PVOID p = _alloca(lstrlen(szData));
// 使用 p
} __except ((EXCEPTION_STACK_OVERFLOW == GetExceptionCode()) ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH) {
_resetstkoflw();
}
}