深入剖析 SharePointSharePoint 安全帐户

Pav Cherny

目录

应用程序池和安全帐户
在 SharePoint 服务器上运行自定义代码
关于 Jekyll 博士和 Hyde 先生
安全帐户和进程隔离
配置数据库中的安全帐户
安全帐户和凭据密钥
切勿违反定律

使用 SharePoint 安全帐户时,极有可能创建弱系统配置,此类配置可能会暴露整个 SharePoint 环境。为帮助您正确地部署和保护 SharePoint 服务器场,Microsoft 发布了大量信息以及详细的指导方针。例如,Office SharePoint Server 安全指南共有 300 多页,内容涉及规划和实施站点及内容层次结构、身份验证方法、安全角色、管理员和服务帐户以及许多其他安全问题。Windows SharePoint Services 安全帐户要求工作表还提供了有关安全帐户配置的基本信息。如果安全性对您而言至关重要,您肯定希望确保遵守该工作表。

即使拥有内容翔实的文档,配置安全帐户仍可能是一项艰巨的任务。实际上,单服务器安装的默认设置并不遵从工作表的建议,并且某些组件(如 Windows SharePoint Services (WSS) 3.0 中包括的电子邮件集成 Web 服务)在服务器上需要提升的权限,这一点不仅与 Microsoft 安全建议背道而驰,并且与安全性最佳实践和一般常识直接冲突。SharePoint 3.0 中心管理工具乐于应用关键安全帐户配置且没有警告,Microsoft Baseline Security Analyzer (MBSA) 未检测由此产生的不足,这就为保护 SharePoint 服务器场以及确保其安全留下了一个隐患。

在本专栏中,我将深入剖析 SharePoint 安全帐户,向您展示攻击者如何利用弱配置完全控制所有站点集合和站点。这是一个有点敏感的话题。一方面,我希望帮助您找出与 SharePoint 服务器配置相关的安全挑战。毕竟,如果想要有效地保护 SharePoint 环境,您必须了解它的优缺点。

另一方面,我不想助纣为孽。为此,我不会在本专栏中提供任何工作表或自定义工具,并且对源代码的讨论将仅局限于基本主题,所有专业 ASP.NET 开发人员都应非常熟悉这些主题。本专栏中的源代码段可帮助您检测漏洞,但不会帮助任何人利用它们。即使编程技能有限,如果拥有 Microsoft Office SharePoint Designer 2007,应该就可以使用这些代码段来创建自定义 ASP.NET 页面。

可在线获取 Microsoft Office SharePoint Designer 2007 的试用版。建议您根据自己的偏好配置一个测试实验室,尽您所能的保护它,然后运行代码段进行验证。让我们来看一下您的 SharePoint 站点有多安全!

应用程序池和安全帐户

安全帐户是 SharePoint 请求处理模型的核心。它们定义了运行 SharePoint Web 应用程序的 IIS 工作进程的安全上下文。在创建 SharePoint Web 应用程序时,必须指定具有相关安全帐户凭据的应用程序池以及具有相关身份验证方法的 SharePoint 数据库。如果使用 Windows 身份验证(推荐),SharePoint 将自动授予指定安全帐户内容数据库的 dbOwner 权限,这样运行 SharePoint Web 应用程序的 IIS 工作进程可获取该数据库中站点集合和站点的访问权限。否则,必须提供明确的 SQL Server 凭据。

在任何情况下,SharePoint 站点集合和站点都是虚拟结构。实际上,它们与数据库记录相对应。如果知道用于与内容数据库建立直接 SQL Server 连接的帐户名称和密码,就可获取对其所有站点集合和站点数据的完全访问权限(无论在 SharePoint 级别定义了哪些权限和访问控制)。SharePoint 无法阻止您,因为您与数据库服务器建立了直接连接(如图 1 所示)。因此,安全帐户是攻击的主要目标。

fig01.gif

图 1 绕开 SharePoint 站点集合和站点访问数据

如果站点集合包含通过身份验证的内容和匿名内容,为降低安全风险,Microsoft 建议分别配置应用程序池(和安全帐户),并隔离存储密码的应用程序或者用户可在其中自由地创建和管理站点以及共享内容的应用程序。如果能遵循这一配置建议,即使攻击者获取了某个应用程序池的控制权限,也并不意味着他对 SharePoint 场中的所有数据具有全局访问权限。即使对相关的 Web 应用程序分别使用安全帐户,仍然无法触及其他数据库中的 SharePoint 站点集合和站点。

Microsoft 在 IIS 6.0 中首先引入了根据应用程序池隔离工作进程这一概念,并且指出 IIS 从此再也没有出现过一次严重的安全漏洞。这是切实可靠的事实,因此务必在 SharePoint 场中利用应用程序池。但是,请记住,IIS 网站并不等价于 SharePoint Web 应用程序。尽管可隔离 IIS 网站,但无法将 SharePoint Web 应用程序彼此隔离开来。

仅当网站没有共享资源时才存在真正的隔离,而 SharePoint Web 应用程序始终都有公共资源(如场的配置数据库)。如图 2 所示,如果获取了 SharePoint 安全帐户的控制权限,即意味着可以访问 SharePoint 配置数据库。如果在部署 SharePoint 场时没有恰当地考虑安全帐户的保护,尤其是要将来自不同的内部或外部客户的站点集合和站点放在一个共享环境中时,该访问能力会让您感到不安。

图 2 SharePoint Web 应用程序、配置数据库和内容数据库之间的关系

在 SharePoint 服务器上运行自定义代码

默认情况下,SharePoint 并不公开安全帐户信息;它利用恶意代码来找出细节。众所周知,攻击者可以利用安全漏洞上载恶意代码,但有时使用入侵更容易达到目的。

最简单的情形是攻击者可在本地或通过终端服务器登录 SharePoint 服务器,将恶意代码复制到 %COMMONPROGRAMFILES%\Microsoft Shared\Web Server Extensions\12\TEMPLATE\Layouts 文件夹下。请注意,SharePoint 在每个 SharePoint 站点中都包括作为虚拟子文件夹的这一文件夹。

另一种情况是,SharePoint 管理员可能没有经过适当的测试和代码确认,就部署了来自某个可疑来源的自定义 SharePoint 解决方案,从而不知不觉地引入了恶意代码。主页面和内容页面中嵌入的内联 ASP.NET 代码也令人担忧。默认情况下,SharePoint 不会处理服务器端的脚本,但如果某人拥有 SharePoint Web 应用程序 web.config 文件的写入权限,他就可以通过更改规则处理服务器端脚本。仅需向 web.config 文件的 <PageParserPaths> 中添加一个 PageParserPath 条目即可。那 PageParserPath 条目的工作原理究竟是什么呢?

假设有一名使用 SharePoint 的开发人员打电话给您,抱怨他在开发一个自定义 ASP.NET 页面时出现了一条错误消息“此文件中不允许使用代码块”。您在 Internet 中进行搜索并在新闻组或博客站点中找到了解决方案:

<PageParserPath VirtualPath="/*" CompilationMode="Always" AllowServerSideScript="true" IncludeSubFolders="true" />

也许您忽略了安全警告或者甚至并未提及安全隐患。无论如何,您是将以上代码行添加到了 web.config 文件中,现在所有人都非常高兴,因为您把问题解决了。

但是,您也无意间为以完全信任方式运行 ASP.NET 页面中的任意自定义代码开启了方便之门。如果攻击者此时上载一个恶意 ASP.NET 页面,SharePoint 环境将面临危险。如图 3 所示,攻击者在站点集合层次结构的哪个位置拥有上载页面的权限无关紧要—它可以是一个看似无恶意的小型团队站点。攻击始终影响着 Web 应用程序甚至整个服务器场,因为内容数据库和配置数据库均触手可及。

Figure 3

图 3 启用内联 ASP.NET 代码可能危及 SharePoint Web 应用程序的安全

关于 Jekyll 博士和 Hyde 先生

那么,攻击者是如何在并不明确了解安全帐户凭据的情况下获取内容和配置数据库的访问权限的呢?过程其实比较简单。运行 SharePoint Web 应用程序的 IIS 工作进程模拟 SharePoint 用户并使用生成的线程令牌来进行访问检查。例如,Jekyll 博士可以访问其安全令牌有权访问的所有此类 SharePoint 资源。但是,SharePoint Web 应用程序还拥有 IIS 工作进程的进程令牌,而此令牌是 SharePoint 安全帐户的安全令牌。

当您通过调用静态 WindowsIdentity.Impersonate 方法并传入一个 zero 指针(如图 44a 所示)还原模拟时,出现的是 Hyde 先生。Jekyll 博士并无数据库的直接访问权限,但 Hyde 先生却有。这样就为 SQL Server 连接和 T-SQL 查询扫清了障碍。

fig4a_screen.gif

图 4 SharePoint Web 应用程序有两个安全上下文(单击图像可查看大图)

图 4a 用于检索两个 SharePoint Web 应用程序安全上下文的代码

private string GetMrHyde()
{
    string retVal = string.Empty;
    retVal = "Dr Jekyll is: " + WindowsIdentity.GetCurrent().Name + "<br>";

    WindowsImpersonationContext impCtx = WindowsIdentity.Impersonate(IntPtr.Zero);

    retVal += "Mr Hyde is: " + WindowsIdentity.GetCurrent().Name + "<br>";

    impCtx.Undo();
    return retVal;
}

安全帐户和进程隔离

如 Web 应用程序配置为运行未验证代码,应用程序池和安全帐户将无法帮助您保护其中的站点集合和站点。它们的作用在于通过进程隔离来缓解攻击者将代码注入服务器的某个站点以攻击其他站点所带来的影响。进程隔离可帮助实现此目的,但它要求您将其他站点放在单独的 Web 应用程序中,它们要在使用不同安全帐户的应用程序池中运行。

必须充分地保护帐户凭据,否则配置努力将毫无意义。如果向应用程序池帐户授予 IIS 元数据库的访问权限(若要运行电子邮件集成 Web 服务中的目录管理服务,必须拥有此权限),也容易让别有用心的人直接得到这些敏感的安全凭据。如果应用程序池帐户拥有元数据库访问权限,攻击者可还原模拟,然后检索所有明文形式的帐户和密码(如图 55a 所示)。整个服务器场都已失守,因为攻击者此时可通过以任意此类安全帐户身份运行恶意代码并与所有内容数据库建立 SQL Server 连接来绕开进程隔离。

fig5a_screen.gif

图 5 从 IIS 元数据库检索安全帐户信息

图 5a 用于从 IIS 元数据库检索安全帐户信息的代码

private string GetMetabaseAppPoolIDs()
{
        WindowsImpersonationContext impCtx = WindowsIdentity.Impersonate(IntPtr.Zero);
        string retVal = string.Empty;
        try
        {
            string metabasePath = "IIS://localhost/w3svc/AppPools";
            DirectoryEntry appPools = new DirectoryEntry(metabasePath);
            foreach (DirectoryEntry appPool in appPools.Children)
            {
            switch (int.Parse(appPool.Properties["AppPoolIdentityType"].Value.ToString()))
            {
                case 0: // Local System
                    retVal += "<br>" + appPool.Name
                        + " (Local System)";
                    break;
                case 1: // Local Service
                    retVal += "<br>" + appPool.Name
                        + " (Local Service)";
                    break;
                case 2: // Network Service
                     retVal += "<br>" + appPool.Name
                        + " (Network Service)";
                     break;
                case 3: // Custom 
                    retVal += "<br>" + appPool.Name
                        + " (" + appPool.Properties["WAMUserName"].Value
                        + " [Pwd: " + appPool.Properties["WAMUserPass"].Value
                        + "])";
                    break;
               }
            }
         }
        catch (Exception ee)
         {
        retVal = "Metabase " + ee.Message;
         }

        impCtx.Undo();
}

如果对不需要元数据库访问权限的目录管理服务解决方案感兴趣,可阅读一下我在 2008 年 9 月撰写的专栏文章“SharePoint 目录集成”。

配置数据库中的安全帐户

如果遵守规则,不向应用程序池帐户授予 SharePoint 服务器上 IIS 元数据库的管理权限甚至仅读取访问权限,则图 5 中的代码将仅得到“访问被拒绝”消息。但是,配置数据库中也有安全帐户信息,而如之前所述,Hyde 先生拥有该数据库的访问权限。

您无法拒绝 SharePoint 安全帐户访问配置数据库,也无法拒绝对存储相应 SQL Server 连接字符串的注册表项的访问。如果使用 SQL Server 2005 Express,连接字符串可能不会立即起作用,但可从当前的 SharePoint 站点集合派生正确的数据源信息 (SPContext.Current.Site.ContentDatabase.DatabaseConnectionString),并且配置数据库的名称对应于当地场的名称 (SPFarm.Local.Name)。

遗憾的是,这些小障碍无法阻止任何攻击。无论使用的是 SQL Server 还是 SQL Server Express,Hyde 先生均可检索图 66a 中所示的信息。但请注意,密码已加密,因此攻击尚未完全成功。

fig6a_screen.gif

图 6 从配置元数据库获取安全帐户信息

图 6a 用于从配置元数据库检索安全帐户信息的代码

private string EnumAppPoolAccounts()
{
    string retVal = string.Empty;
    try
    {
       WindowsImpersonationContext impCtx = WindowsIdentity.Impersonate(IntPtr.Zero);

       string regConfigDB = @"SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0\Secure\ConfigDB";
       RegistryKey keyConfigDB = Registry.LocalMachine.OpenSubKey(regConfigDB);

       string ConfigDB = (string)keyConfigDB.GetValue("dsn");

       SqlConnection sqlConn = new SqlConnection(ConfigDB);
       sqlConn.Open();

       SqlCommand sqlCmd = new SqlCommand("SELECT Name, Properties FROM Objects"
          + " WHERE ClassId = 'B8369089-08AD-4978-B1CB-C597B5E90F64'", sqlConn);
       sqlCmd.CommandType = System.Data.CommandType.Text;
       SqlDataReader sqlReader = sqlCmd.ExecuteReader();

          while (sqlReader.Read())
          {
             retVal += "<br>" + sqlReader.GetString(0);
             string appPoolXML = sqlReader.GetString(1);
             if (!string.IsNullOrEmpty(appPoolXML))
             {

                 XmlDocument xmlDoc = new XmlDocument();
                 xmlDoc.LoadXml(appPoolXML);

                 XmlElement root = xmlDoc.DocumentElement;
                 XmlNode ndType = root.SelectSingleNode("/object/fld[@name=                   'm_IdentityType']");
                 if (ndType != null && ndType.InnerText.ToLower() != "specificuser")
                 {
                     retVal += " (" + ndType.InnerText + ")";
                 }
                 else
                 {
                     retVal += " (" 
                         + root.SelectSingleNode("/object/sFld[@name='m_Username']").InnerText
                         + " [Pwd: " 
                         + root.SelectSingleNode("/object/fld[@name='m_Password']").InnerText
                         + "])";
                 }
             }
          }
          sqlReader.Close();
          sqlConn.Close();
          impCtx.Undo();
    }
    catch (Exception ee)
    {
          retVal = ee.Message;
    }
    return retVal;
}

虽然没有解密密码,但已可识别出使用相同安全帐户的应用程序池。例如,在图 6 中,应用程序池 SharePoint Central Administration v3 和 SharePoint—80 使用 Network Service 帐户;如果 SharePoint—80 恰好是存在安全隐患的 Web 应用程序,则 SharePoint Central Administration v3 的安全也会受到威胁。相应的安全帐户是管理中心帐户,它在 SharePoint 服务器上拥有提升的权限。

它不应用于标准的 Web 应用程序池,但“SharePoint 产品和技术配置向导”在单服务器安装中默认应用该配置。因此,检查并在必要时更改 SharePoint 环境中的安全帐户配置非常重要。Microsoft 知识库文章“如何在 SharePoint Server 2007 和 Windows SharePoint Services 3.0 中更改服务帐户和服务帐户密码”详细阐述了有关此主题的更多信息。

安全帐户和凭据密钥

那么,管理中心帐户的重要作用是什么?最重要的是,管理中心帐户可以访问解密安全帐户密码的凭据密钥注册表存储位置,这是与标准应用程序池帐户的区别。

图 7 显示了该参数及其默认安全设置。正如您所看到的,本地管理员 WSS_RESTRICTED_WPG 组(它包含管理中心帐户)和 SYSTEM 帐户拥有该密钥的访问权限,它意味着 SharePoint Web 应用程序不应使用拥有本地管理员权限的帐户(管理中心帐户)或 SYSTEM 帐户。SharePoint Web 应用程序应当不能访问凭据密钥。

fig07a.gif

图 7 用于访问 FarmAdmin 注册表项的权限分配

但遗憾的是,熟练攻击者能确定 CredentialKey 或安全帐户密码,如通过 SYSTEM 令牌攻击、密码破解,或者就是将恶意代码放在主页面或内容页面中,把凭据密钥导出到一个未受保护的位置,然后等待拥有本地管理员权限的用户访问该站点,这种行为很难防范。因此,切记不要允许服务器上有未经验证的代码。

SharePoint 资源

bluebullet.gif SharePoint 产品和技术网站

bluebullet.gif Windows SharePoint Services 技术中心

bluebullet.gif Windows SharePoint Services 开发人员中心

bluebullet.gif Microsoft SharePoint 产品和技术团队博客

在这里有必要再详细解释一下 SYSTEM 令牌攻击,因为如果避免使用 SharePoint Web 应用程序的内置系统帐户(如 Network Service)就可阻止此类攻击。实际上,Argeniss 的创始人兼 CEO Cesar Cerrudo 发现了这一漏洞,并在阿拉伯联合酋长国的迪拜举办的 HITBSecConf2008 安全深层知识会议中演示了此类攻击。Cesar 演示了以 Network Service 帐户运行的 ASP.NET Web 应用程序如何将一个 DLL 注入远程过程调用 (RPC) 服务,然后窃取 RPC 服务中在 SYSTEM 权限级别运行的某个线程的安全令牌。

随后,攻击者只需将窃得的 SYSTEM 安全令牌传到 WindowsIdentity.Impersonate 方法,就能访问 CredentialKey 注册表参数和其他受保护的资源。Microsoft 已确认此漏洞,因此您应当避免使用 SharePoint Web 应用程序的 Network Service 帐户。

切勿违反定律

很早之前 Microsoft 安全响应中心就发布了安全性的十个永恒定律,它们至今仍然适用。Jesper M. Johansson 最近撰写了由三部分内容组成的系列文章“再探安全性的十个永恒定律”。在设计 SharePoint 服务器场时应牢记这些定律,并且还应遵循 SharePoint 安全指导原则和工作表来应用可靠的安全帐户配置。

简而言之:使用强密码、切勿授予安全帐户 SharePoint 服务器的提升权限、经常更改密码(包括场凭据),并且牢记使用公共资源的 SharePoint Web 应用程序之间不存在绝对的隔离,就像不存在绝对的计算机安全一样。并且,切勿更改服务器代码处理规则,禁止将未验证程序集放在服务器上,并且在安全帐户配置中使用 Windows SharePoint Services 安全帐户要求工作表,这样的 SharePoint 环境才能视为比较安全。

但是,如果组织严格要求必须分隔站点内容,建议将对应的站点集合放在单独的服务器场中(可以是单独的 Active Directory 林和 SQL Server 环境)。

Pav Cherny 是一位 IT 专家兼撰稿人,专门研究 Microsoft 协作与统一通信技术。他的出版物包括白皮书、产品手册和书籍,其内容主要介绍 IT 运营和系统管理。Pav 是 Biblioso Corporation 的总裁,该公司主要经营托管文档和本地化服务。