【问题标题】:starting a UAC elevated process from a non-interactive service (win32/.net/powershell)从非交互式服务 (win32/.net/powershell) 启动 UAC 提升进程
【发布时间】:2011-01-26 11:07:17
【问题描述】:

我正在使用第三方 Windows 服务,该服务通过使用 CreateProcessAsUser() 运行脚本和可执行文件来处理一些自动化任务。由于 UAC 以及通过 API 处理 LUA 提升的方式,我在 Windows Server 2008 上遇到了问题。

该服务作为 LocalSystem 运行,并且没有启用“与桌面交互”。这些进程作为管理员组中的用户运行,但不是管理员帐户(不受许多 UAC 限制)。所有 UAC 默认设置均已到位。

我可以将任意命令或 powershell 代码传递给服务,但我似乎无法“突破”由服务启动的非提升、非交互式进程。

问题的症结似乎在于启动提升进程的唯一(公共)API 选项是带有“runas”动词的 ShellExecute(),但据我所知,它不能从非交互式服务,否则您会收到诸如“此操作需要交互式窗口站”之类的错误。

这里提到了我发现的唯一解决方法: http://www.eggheadcafe.com/software/aspnet/29620442/how-to-proper-use-sendinp.aspx

在Vista中,官方记录的方式 提升进程仅使用 外壳 API ShellExecute(Ex)(不是 CreateProcess 或 CreateProcessAsUser)。 所以你的应用程序必须调用 ShellExecute(Ex) 启动助手 提升以调用 SendInput。 此外,由于 Session 0 隔离,一个服务只能使用 CreateProcessAsUser 或 CreateProcessWithLogonW(不能使用 ShellExecute(Ex)) 来指定 交互式桌面。

..我认为没有直接的方法 从 a 产生一个提升的进程 窗口服务。我们只能先使用 CreateProcessAsUser 或 CreateProcessWithLogonW 生成一个 非提升进程进入用户 会话(交互式桌面)。然后在 非提升过程,它可以使用 ShellExecute(Ex) 产生一个提升的 实际任务的过程。

要从 .net/powershell 代码中执行此操作,看起来我必须做一些复杂的 P/Invoke 操作才能调用 CreateProcessAsUser 或 CreateProcessWithLogonW,因为 .Net System.Diagnostics.ProcessStartInfo 没有等效于 lpDesktop我可以设置为“winsta0\default”。而且我不清楚 LocalSystem 是否甚至有权调用 CreateProcessAsUser 或 CreateProcessWithLogonW。

我也看过 http://blogs.msdn.com/alejacma/archive/2007/12/20/how-to-call-createprocesswithlogonw-createprocessasuser-in-net.aspxProcess.Start with different credentials with UAC on

基于所有这些,我得出的结论是,没有直接的方法可以做到这一点。我错过了什么吗?这似乎真的不应该那么难。感觉就像 UAC 从未设计用于处理非交互式用例。

如果有任何 Microsoft 人员最终阅读了这篇文章,我注意到 ShellExecute 在内部处理提升的方式是调用应用程序信息服务 (AIS)。为什么不能通过某些 Win32 或 .NET API 对 AIS 进行相同的调用? http://msdn.microsoft.com/en-us/library/bb756945.aspx

抱歉,运行时间有点长。感谢您的任何想法。

【问题讨论】:

标签: winapi uac shellexecute createprocessasuser


【解决方案1】:

打破会话零隔离的“官方”方法是结合使用终端服务 API 和CreateProcessAsUser() 在用户会话中启动进程。在我以前的工作中,我们就是这样做的,因为我们需要在安装下载的更新之前从服务向用户显示一个对话框所以,我知道它至少在 WinXP、Win2K3、Vista 和 Win7 上有效,但我不要指望 Win 2K8 会有太大的不同。基本上,过程如下:

  1. 调用WTSGetActiveConsoleSessionId() 以获取活动控制台会话ID(非常重要,因为交互式会话始终是会话1,即使在客户端系统上也是如此)。如果没有活动用户登录到交互式会话(即本地登录到物理机,而不是使用 RDP),此 API 还将返回 -1
  2. 将上一次 API 调用中的会话 ID 传递给 WTSQueryUserToken(),以获取代表用户登录控制台的开放令牌。
  3. 调用DuplicateTokenEx() 将模拟令牌(来自WTSQueryUserToken)转换为主令牌。
  4. 调用CreateEnvironmentBlock() 为进程创建一个新环境(可选,但如果不这样做,进程将不会有一个)。
  5. 将第 3 步中的主令牌与可执行文件的命令行一起传递到对CreateProccessAsUser() 的调用中。如果您从第 4 步创建了环境块,则还必须传递 CREATE_UNICODE_ENVIRONMENT 标志(始终)。这可能看起来很愚蠢,但如果你不这样做,API 会严重失败(使用ERROR_INVALID_PARAMTER)。
  6. 如果你创建了环境块,那么你需要调用DestroyEnvironmentBlock,否则会产生内存泄漏。该进程在启动时会获得环境块的单独副本,因此您只会破坏本地数据。

瞧! Windows 做了一些内部魔术,您会看到应用程序启动。但是,尽管这将从服务启动和交互过程,但我不确定它是否会绕过 UAC(但不要引用我的话)。换句话说,除非注册表或内部清单要求这样做,否则它可能不会作为提升的进程启动,即使那样,您仍可能会收到 UAC 提示。如果您从第 3 步获得的令牌是受限令牌,您可以使用 AdjustTokenPrivileges() 恢复提升的(完整)令牌,但也不要引用我的话。但是,如 MSDN 文档中所述,请注意,无法在尚未拥有权限的令牌上“添加”权限(例如,您无法使用 AdjustTokenPrivileges 将受限用户令牌转换为管理员;底层用户必须是管理员才能开始)。

从技术上讲,从 Win2K 开始,这一切都是可行的。然而,这实际上只有从 WinXP 开始才可行,因为 Win2K 缺少 WTSGetActiveConsoleSessionId()WTSQueryUserToken() API(以及用于 Win2K Pro 的 WTSEnumerateProcesses())。您可以将 0 硬编码为会话 ID(因为在 Win2K 中总是如此),我想您可能能够通过枚举正在运行的进程并复制其中一个令牌来获取用户令牌(它应该是具有交互式 SID 存在)。无论如何,CreateProcessAsUser() 在传递交互式用户令牌时的行为方式相同,即使您没有从服务设置中选择“与桌面交互”。无论如何,它也比直接从服务启动更安全,因为该进程不会继承神圣的LocalSystem 访问令牌。

现在,我不知道您的第三方应用程序在运行脚本/进程时是否会执行任何这些操作,但如果您想从服务中执行此操作,就是这样(对于 Vista 或 Win7,它是克服会话 0 隔离的唯一方法。

【讨论】:

  • 无需致电DuplicateTokenExCreateProcessAsUser 将使用从WTSQueryUserToken 获得的令牌运行该进程。您的回答非常详细,但没有回答“如何从服务启动提升的进程?(启用 UAC)”
  • 确实没有。这将为您留下一个受限令牌。
  • 有几点需要注意:1) 正如@Ajay 提到的WTSQueryUserToken 已经返回了一个主令牌,所以你可以跳过第 3 步。 2) 启用 UAC 你实际上需要得到-在第 2 步之后调用链接令牌,您可以通过使用 TokenLinkedToken 信息类调用 GetTokenInformation 来执行此操作,但前提是查询 TokenElevationType 表示海拔是有限的。 3)如果您不创建环境块,则新进程将具有创建进程的环境,即您的服务
【解决方案2】:

根据您的用例,您可以按照我的方式进行操作。我寻找活动会话的 winlogon 进程并窃取它的令牌。如果没有活动会话(API 返回 -1),如果 WINVER >= 6 则使用 1,否则为 0。这会导致活动会话上的 SYSTEM。

【讨论】:

    猜你喜欢
    • 2017-01-26
    • 2017-06-17
    • 2014-07-08
    • 2011-04-25
    • 2014-08-20
    • 1970-01-01
    • 1970-01-01
    • 2012-04-11
    • 1970-01-01
    相关资源
    最近更新 更多