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

标签 c# windows uac impersonation

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

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

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

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

我可以很好地将值写入我的根键:

_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 说不应该进行安全检查时进行安全检查,但我的问题是如何通过模拟可能未在交互式登录下运行的应用程序来提升权限?

最佳答案

有人提出 LogonUser() 只返回受限 token 。搜索确认,我得到的印象是 LogonUser() 只为交互式 session 返回受限 token 。我创建了几个测试来找出答案。

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

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)

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

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

因此,这似乎证实了我的印象,即 LogonUser() 仅为交互式 session 返回受限 token ,但非交互式 session 会获得完整 token 。

做这些测试帮助我回答了我自己的问题。我的类库主要用于 web 应用程序,并且在应用配置更新时它们不断地轰炸(拒绝访问打开子项)。所以我更改了我的测试以更准确地反射(reflect)我在做什么(模拟只是为了获得对我的根 key 的引用):

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

同样,将值写入我的根 key 没有问题,只需打开子 key 即可。因此,看起来使用 RegistryKeyPermissionCheck.ReadWriteSubTree 在写入该 key 时确实不会进行任何进一步的安全检查,但在打开子 key 时会进行另一次安全检查,即使使用 RegistryKeyPermissionCheck.ReadWriteSubTree(尽管文档说它没有)。

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

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

关于c# - 如何在非交互式登录下通过模拟获得提升的权限 (UAC)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5098121/

相关文章:

windows - Docker:Hyper-V在尝试访问计算机 'host-machine'上的对象时遇到错误

Windows - 在 CentOS 上运行的 LAMP

.net - 使用网站生成的电子邮件进行 UAC 测试

c# - 主要方法代码完全在 try/catch : Is it bad practice? 中

c# - 验证 POST 请求的输入?

c# - ASP.NET 核心 : Session Id Always Changes

VBScript 的 ShellExecute 返回代码

c# - Entity Framework 、导航属性、调用并将值存储在数据库表中

Python 多处理不断生成 pythonw.exe 进程而不做任何实际工作

c# - 在非管理员帐户上自动启动具有管理员权限的 WinForms 应用程序