如何在非交互式登录下通过模拟获得提升权限(UAC)?

时间:2011-02-23 22:37:49

标签: c# windows uac impersonation

我有一个类库,可以在注册表中保存系统范围的配置数据(HKLM \ Software \ XXX)。该库用于各种版本的Windows(XP,2003,7,2008 R2)上的各种应用程序(服务,Windows窗体,Web应用程序,控制台应用程序)。因此,应用程序的身份不一致,甚至可能不是计算机管理员组的成员。因此,我创建了一个AD域管理员用户并进行模拟,以获得对注册表的写入权限。这在XP / 2003中完美运行,但在启用UAC的系统(7 / 2008R2)中不起作用。我的理解是,只有交互式登录才会拆分令牌,这意味着非交互式登录(服务标识,应用程序池标识等)不会。我找不到任何可以确认的东西,但根据这个假设,我正在做的模仿应该有用。

我编写了一个包装类来使用本机LogonUser(网络登录类型,默认提供程序)和DuplicateTokenEx(模拟,主令牌)然后使用WindowsIdentity.Impersonate()进行模拟。我得到了我的根密钥的引用:

using (ECR.Impersonator imp = new ECR.Impersonator("XXX", "XXX", "XXX"))
{
    _root = Registry.LocalMachine.CreateSubKey("SOFTWARE\\XXX", RegistryKeyPermissionCheck.ReadWriteSubTree);
}

根据MSDN,通过使用ReadWriteSubTree,这应该是进行安全检查的唯一时间。我可以为该键写入值,创建子键(也使用ReadWriteSubTree)并将值写入这些子键,而无需进行其他安全检查。所以我认为我只需要进行一次代价高昂的模拟 - 获取对我的根密钥的引用。

我可以将值写入我的根密钥:

_root.SetValue("cachedDate", value.ToBinary(), RegistryValueKind.QWord); }

但是当我使用ReadWriteSubTree创建/打开一个子键时:

RegistryKey key = _root.CreateSubKey("XXX", RegistryKeyPermissionCheck.ReadWriteSubTree);

它以Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\XXX\XXX' is denied轰炸。

虽然我很好奇为什么在MSDN说它不应该进行安全检查时,我的问题是如何通过假冒来获取可能无法在交互式登录下运行的应用程序的提升权限?

2 个答案:

答案 0 :(得分:11)

LogonUser()只返回受限制的令牌。搜索确认,我得到的印象是LogonUser()仅为交互式会话返回受限制的令牌。我创建了几个测试来找出答案。

第一个是控制台应用程序:

using (ECR.Impersonator imp = new ECR.Impersonator("XXX", "XXX", "XXX"))
{
    WindowsIdentity ident = WindowsIdentity.GetCurrent();
    WindowsPrincipal princ = new WindowsPrincipal(ident);
    Console.WriteLine("{0}, {1}", ident.Name, princ.IsInRole(WindowsBuiltInRole.Administrator));
    RegistryKey root = Registry.LocalMachine.CreateSubKey("SOFTWARE\\Connection Strings", RegistryKeyPermissionCheck.ReadWriteSubTree);
    RegistryKey key = root.CreateSubKey("AbacBill", RegistryKeyPermissionCheck.ReadWriteSubTree);
}

在提升的控制台中运行时,IsInRole()返回true并且没有错误打开该子项。在非提升控制台中运行时,IsInRole()返回true并错误打开子项:

Unhandled Exception: System.IO.IOException: Unknown error "1346".
   at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
   at Microsoft.Win32.RegistryKey.CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistrySecurity registrySecurity)
   at test.Program.test14()
   at test.Program.Main(String[] args)

因此,在非提升的交互式会话中,LogonUser()确实会返回受限制的令牌。有趣的是,执行IsInRole()的正常测试意外返回true。

第二个测试是一个网站。我把相同的代码放入(用literal1.Text = string.Format替换Console.Write):IsInRole()返回true,没有错误打开子项,IIS7.5:匿名身份验证,app pool:经典管道,ApplicationPoolIdentity,2.0框架, web.config:authentication mode = none,no impersonation。

所以这似乎证实了我的印象,即LogonUser()仅为交互式会话返回受限制的令牌,但非交互式会话获得完整的令牌。

做这些测试帮助我回答了我自己的问题。我的类库主要用于Web应用程序,并且在应用配置更新时会持续炸弹(访问被拒绝打开子项)。所以我改变了我的测试,以更准确地反映我正在做的事情(模仿只是为了获得对我的根密钥的引用):

protected void Page_Load(object sender, EventArgs e)
{
    RegistryKey root = null;

    using (ECR.Impersonator imp = new ECR.Impersonator("XXX", "XXX", "XXX"))
    {
        WindowsIdentity ident = WindowsIdentity.GetCurrent();
        WindowsPrincipal princ = new WindowsPrincipal(ident);
        lit.Text = string.Format("{0}, {1}", ident.Name, princ.IsInRole(WindowsBuiltInRole.Administrator));
        root = Registry.LocalMachine.CreateSubKey("SOFTWARE\\XXX", RegistryKeyPermissionCheck.ReadWriteSubTree);
    }

    root.SetValue("test", "test");
    RegistryKey key = root.CreateSubKey("XXX", RegistryKeyPermissionCheck.ReadWriteSubTree);
}

并且错误:

[UnauthorizedAccessException: Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\XXX\XXX' is denied.]
   Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str) +3803431
   Microsoft.Win32.RegistryKey.CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistrySecurity registrySecurity) +743
   webtest.impTest.Page_Load(Object sender, EventArgs e) in D:\VS 2008 Projects\test\webtest\impTest.aspx.cs:28
   System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +25
   System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +42
   System.Web.UI.Control.OnLoad(EventArgs e) +132
   System.Web.UI.Control.LoadRecursive() +66
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2428

同样,没有问题将值写入我的根密钥,只需打开子密钥。所以看起来使用RegistryKeyPermissionCheck.ReadWriteSubTree在写入该密钥时确实没有进行任何进一步的安全检查,但是在打开子密钥时IS正在进行另一次安全检查,即使使用RegistryKeyPermissionCheck.ReadWriteSubTree(尽管文档说它没有)。 / p>

我的问题的答案是它在非交互式登录下通过模拟适当地提供了提升的权限。我的问题是我假设即使在模拟结束后,RegistryKeyPermissionCheck.ReadWriteSubTree也不会对该引用进行任何进一步的安全检查(如文档所述)。

我想我每次需要写入注册表时都要进行模拟。 :(

答案 1 :(得分:3)

我不明白微软如何声称不会执行任何安全检查。我快速查看了代码,这些方法并不比本机RegOpenKeyEx / RegCreateKeyEx函数的包装更多,因此显然会进行安全检查。 RegistryKeyPermissionCheck似乎控制RegistryKey类本身是否在P /调用本机函数之前进行自己的内部安全检查。因此,您可以将所需的任何标志传递给此RegistryKey类,但如果底层注册表项未授予您访问权限,那么您将获得访问被拒绝的异常。

对于LogonUser(),从我所看到的内容来看,似乎总会给你一个受限制的令牌。