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

..我认为没有直接的方法可以从 Windows 服务中生成提升的进程。我们只能首先使用 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 与 UAC 不同的凭据

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

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

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

4

2 回答 2

19

打破会话零隔离的“官方”方法是使用终端服务 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. CreateProccessAsUser()将第 3 步中的主令牌与可执行文件的命令行一起传递到对 的调用中。如果您从第 4 步创建了环境块,则还必须传递CREATE_UNICODE_ENVIRONMENT标志(始终)。这可能看起来很愚蠢,但如果你不这样做,API 会严重失败(使用ERROR_INVALID_PARAMTER)。
  6. 如果你创建了一个环境块,那么你需要调用DestroyEnvironmentBlock,否则你会产生内存泄漏。该进程在启动时会获得环境块的单独副本,因此您只是在破坏本地数据。

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

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

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

于 2010-09-04T05:09:49.083 回答
1

根据你的用例,你可以做我做的事。我寻找活动会话的 winlogon 进程并窃取它的令牌。如果没有活动会话(API 返回 -1),如果 WINVER >= 6 则使用 1,否则使用 0。这将导致活动会话上的 SYSTEM。

于 2013-07-03T19:05:44.770 回答