从非交互式服务启动UAC升级进程(win32 / .net / powershell)

时间:2010-03-11 16:11:41

标签: winapi uac shellexecute createprocessasuser

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

该服务作为LocalSystem运行,并且未启用“Interact With Desktop”。这些进程作为Administrators组中的用户运行,但不是管理员帐户(免除许多UAC限制)。所有UAC默认设置都已到位。

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

问题的症结似乎是启动提升进程的唯一(公共)API选项是带有'runas'动词的ShellExecute(),但据我所知,无法从a调用非交互式服务或您收到“此操作需要交互式窗口站”等错误。

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

  

在Vista中,官方记录的方式   提升进程只是使用了   shell API ShellExecute(Ex)(不是   CreateProcess或CreateProcessAsUser)。   所以你的申请必须打电话   ShellExecute(Ex)启动帮助程序   提升调用SendInput。   此外,由于会话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 with different credentials with UAC on

基于这一切,我得出结论,没有直接的方法来做到这一点。我错过了什么吗?这看起来真的不应该那么难。感觉UAC从未被设计用于处理非交互式用例。

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

抱歉,跑了一会儿。谢谢你的任何想法。

2 个答案:

答案 0 :(得分:17)

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

  1. 调用WTSGetActiveConsoleSessionId()以获取活动的控制台会话ID(非常重要,因为交互式会话始终是会话1,即使在客户端系统上也是如此)。如果没有活动用户登录交互式会话(即,本地登录到物理机,而不是使用RDP),此API还将返回 -1
  2. 将会话ID从之前的API调用传递给WTSQueryUserToken(),以获取一个代表登录到控制台的用户的开放令牌。
  3. 调用DuplicateTokenEx()将模拟令牌(从WTSQueryUserToken)转换为主令牌。
  4. 调用CreateEnvironmentBlock()为流程创建一个新环境(可选,但如果不这样做,则流程将没有)。
  5. 将步骤3中的主令牌传递给CreateProccessAsUser()的调用,以及可执行文件的命令行。如果您从步骤#4创建了一个环境块,则必须同时传递CREATE_UNICODE_ENVIRONMENT标志(始终)。这可能看起来很愚蠢,但如果你不这样做(ERROR_INVALID_PARAMTER),API就会失败。
  6. 如果您创建了一个环境块,则需要调用DestroyEnvironmentBlock,否则会产生内存泄漏。该过程在启动时会获得一个单独的环境块副本,因此您只会破坏本地数据。
  7. 瞧! Windows做了一些内部魔术,你看到应用程序启动。但是,虽然这将从服务启动和交互过程,但我不确定它是否会绕过UAC(但不要引用我的话)。换句话说,除非注册表或内部清单表明这样做,否则它可能无法作为提升的进程启动,即便如此,您仍可能会获得UAC提示。如果从步骤3获得的令牌是受限令牌,您可以使用AdjustTokenPrivileges()来恢复提升(完整)令牌,但也不要引用我。但是,如MSDN文档中所述,请注意,无法在尚未拥有它们的令牌上“添加”权限(例如,您无法使用{{1}将受限制的用户令牌转换为管理员基础用户必须是一个管理员才能开始)。

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

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

答案 1 :(得分:1)

根据您的使用情况,您可以完成我的工作。我搜索活动会话的winlogon进程并窃取其令牌。如果没有活动会话(API返回-1),如果WINVER> = 6则使用1,否则为0.这将导致活动会话中的SYSTEM。