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

Jef*_*ler 10 c# windows impersonation uac

我有一个类库,可以在注册表(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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

_root.SetValue("cachedDate", value.ToBinary(), RegistryValueKind.QWord); }
Run Code Online (Sandbox Code Playgroud)

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

RegistryKey key = _root.CreateSubKey("XXX", RegistryKeyPermissionCheck.ReadWriteSubTree);
Run Code Online (Sandbox Code Playgroud)

它爆炸了Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\XXX\XXX' is denied.

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

Jef*_*ler 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);
}
Run Code Online (Sandbox Code Playgroud)

在提升的控制台中运行时,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)
Run Code Online (Sandbox Code Playgroud)

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

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

所以这似乎证实了我的印象,即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);
}
Run Code Online (Sandbox Code Playgroud)

它错误:

[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
Run Code Online (Sandbox Code Playgroud)

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

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

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