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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5098121/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 16:15:34  来源:igfitidea点击:

How can I get elevated permissions (UAC) via impersonation under a non-interactive login?

c#windowsuacimpersonation

提问by Jeff Shepler

I have a class library that keeps system-wide configuration data in the registry (HKLM\Software\XXX). This library is used in various applications (services, windows forms, web apps, console apps) on various versions of Windows (XP, 2003, 7, 2008 R2). Because of this, the identity of the app is not consistent and may not even be a member of the machine's Administrators group. So I've created an AD domain admin user and do impersonation to gain write access to the registry. This works perfectly in XP/2003, but not in UAC-enabled systems (7/2008R2). It is my understanding that only interactive logins split the tokens which would imply that non-interactive logins (service identities, app pool identities, etc.) do not. I can't find anything to confirm that, but working from that assumption, the impersonation I'm doing should work.

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

I wrote a wrapper class to do the impersonation using native LogonUser (network logontype, default provider) and DuplicateTokenEx (impersonation, primary token) then WindowsIdentity.Impersonate(). I get a reference to my root key:

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

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

According to MSDN, by using ReadWriteSubTree, this should be the ONLY time a security check is done. I can write values to that key, create sub-keys (also using ReadWriteSubTree) and writing values to those sub-keys without ever needing another security check. So I thought that I would only need to do the costly impersonation one time - getting the reference to my root key.

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

I can write values to my root key just fine:

我可以将值写入我的根键就好了:

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

but when I create/open a sub-key with ReadWriteSubTree:

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

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

it bombs with Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\XXX\XXX' is denied.

它用Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\XXX\XXX' is denied.

While I'm curious why a security check is done when MSDN says it shouldn't, my question is how can I get elevated permissions via impersonation for applications that may not be running under a interactive login?

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

回答by Jeff Shepler

It was brought up that LogonUser() only returns the restricted token. Searching for confirmation, the impression I got was that LogonUser() returned the restricted token only for interative sessions. I created a couple of tests to find out.

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

The first is a console application:

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

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);
}

When run in elevated console, IsInRole() returned true and no error opening the subkey. When run in non-elevated console, IsInRole() returned true and errored opening the subkey:

在提升的控制台中运行时, 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)

So it appears that in a non-elevated interactive session, LogonUser() does indeed return the restricted token. It's interesting that the normal test of doing IsInRole() unexpectedly returned true.

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

The second test is a website. I put the same code in (replaced Console.Write with literal1.Text = string.Format): IsInRole() returned true, no error opening subkey, IIS7.5: anonymous authentication, app pool: classic pipeline, ApplicationPoolIdentity, 2.0 framework, web.config: authentication mode = none, no impersonation.

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

So this seems to confirm my impression that LogonUser() returns the restricted token only for interactive sessions, but non-interactive sessions get the full token.

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

Doing these tests helped me answer my own question. My class library is mostly used in web applications, and they consistently bomb when applying config updates (access denied opening the subkey). So I changed my test to more accurately reflect what I'm doing (impersonating only to get the reference to my root key):

做这些测试帮助我回答了我自己的问题。我的类库主要用于 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);
}

and it errors:

它错误:

[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

Again, no problems writing values to my root key, just opening subkeys. So it appears that using RegistryKeyPermissionCheck.ReadWriteSubTree does indeed not do any further security checks when writing to that key, but IS doing another security check when opening a subkey, even with RegistryKeyPermissionCheck.ReadWriteSubTree (though the docs say it doesn't).

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

The answer to my question is that it does appropriately give elevated permissions via impersonation under a non-interactive login. My problem is I had assumed that RegistryKeyPermissionCheck.ReadWriteSubTree wouldn't do any further security checks (like the docs say) on that reference even after impersonation ends.

我的问题的答案是,它确实在非交互式登录下通过模拟适当地授予了更高的权限。我的问题是我假设 RegistryKeyPermissionCheck.ReadWriteSubTree 即使在模拟结束后也不会对该参考进行任何进一步的安全检查(如文档所说)。

I guess I'm going to have to do the impersonation every time I need to write to the registry. :(

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

回答by Luke

I don't understand how Microsoft could claim that no security checks would be performed. I took a quick look at the code and these methods are not too much more than wrappers around the native RegOpenKeyEx/RegCreateKeyEx functions, so obviously security checks are going to be made. RegistryKeyPermissionCheck seems to control whether or not the RegistryKey class itself does its own internal security checking prior to P/Invoking the native functions. So you can pass whatever flags you want to this RegistryKey class, but if the underlying registry key doesn't grant you access then you're going to get an access denied exception.

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

As for LogonUser(), from what I've read it seems that it is always going to give you the restricted token.

至于 LogonUser(),从我读过的内容来看,它似乎总是会给你受限令牌。