通过“以最高权限运行”的计划任务启动时,无法读取存储的凭据

本文关键字:任务启动 计划 存储 读取 高权限 运行 权限 通过 | 更新日期: 2023-09-27 18:05:43

我正在开发一个实用程序,该实用程序在后台运行(可能)很长一段时间,并在系统重新启动时自动恢复。该实用程序需要提升的特权才能运行(例如:"以管理员身份运行"),所以我不能依赖注册表(HKEY_LOCAL_MACHINE'SOFTWARE'Microsoft'Windows'CurrentVersion'Run)的"运行"部分,因为它只会启动具有默认权限的进程。

所以,我使用的是一个使用TaskScheduler API的"登录"触发器的计划任务,以及一个运行级别为"TASK_RUNLEVEL_HIGHEST"的运行级别,它以提升的特权启动应用程序(没有UAC,这是必要的)。到目前为止,一切顺利。

问题是此应用程序还将凭据存储在凭据管理器中,并在重新启动后重新读取它们(例如,当上述计划任务被触发时)。这是使用advapi32.dll中的CredRead()/CredWrite()函数完成的。由于某些原因,当任务以RUNLEVEL_HIGHEST启动时,CredRead()函数返回false,而GetLastError()返回1168 ("element not found")。

根据文档,由CredWrite()存储的凭证与当前用户令牌的登录会话相关联;我用"LOCAL_MACHINE"的持久类型存储了我的凭据,也就是说,它应该在当前会话之外持续存在——事实上,即使任务无法读取它,它也会显示在凭据管理器中。

因此,似乎以最高权限启动的任务导致它无法看到实用程序最初启动时创建的凭据——即使应用程序必须以更高的权限启动(或者在存储凭据之前由于不相关的原因失败)。

顺便说一句,我认为问题是由于从"以管理员身份运行"命令提示符运行的提升,以及由于作为"以最高权限运行"的任务启动的提升,在某种程度上导致了不同的"级别"提升。我觉得我只是错过了计划任务是如何创建的,或者与CredWrite/CredRead。任何帮助都非常感激!

更新:根据CodyGray在评论中的建议,我尝试在应用程序清单中设置requiredExecutionLevel。这并没有改变我最初问题的行为。

我也检查了,令牌,用户SID和各种"IsAuthenticated"等属性的当前WindowsIdentity (WindowsIdentity.GetCurrent())在工作和非工作情况下都是相同的。

我还添加了对CredEnumerate()的调用,它返回"false"并将错误代码设置为1168("未找到",与上面的CredRead()相同)。因此,这表明进程无法找到该用户的任何存储凭据,而不仅仅是我的应用程序的凭据。

最后,如果我在task Scheduler中手动运行任务,它会按预期工作并找到存储的凭据。这个问题似乎只有在登录时触发任务时才会发生。

更新# 2

我(短暂地)能够在开发机器上重现这个问题,这是我以前无法做到的。似乎只有当用户是两个组的成员时才会出现问题(例如:"用户"answers"管理员")。我的默认开发盒登录只是"Administrators"的成员。当我创建一个同时属于这两个组的新用户时,又出现了这个问题。然而,在用户在Users/Administrators/both之间来回切换了一段时间后,我再也不能重现该用户的问题了(即使他们又回到了两个组中)。

我找到了一个解决方法,那就是将凭据存储在其他地方(目前可以接受,长期不理想,但最终这些凭据无论如何都不是超敏感的)。不过,我还是想知道这里发生了什么。我会继续试验,因为我有时间,但感谢任何见解在此期间!

通过“以最高权限运行”的计划任务启动时,无法读取存储的凭据

我终于想通了:在Windows任务计划程序中右键单击任务->单击属性一个新窗口打开了。导航到触发器选项卡。点击编辑。在高级设置下,勾选"延迟任务"的复选框,并指定一个时间,比如10分钟。单击OK保存设置。现在关闭任务调度程序。您的计划任务应该能够从凭证管理器读取凭证。此设置也可以通过使用任务调度程序api的代码进行配置。