CreateProcess 的权限低于调用者

发布于 2025-01-17 23:26:11 字数 479 浏览 1 评论 0 原文

我们有一个具有管理特权的应用程序,除了其他需要管理特权的操作之外)用户可以发送电子邮件。

我们的电子邮件系统是这样的工作:管理运行应用程序将电子邮件字段和启动(通过 createProcess )进行了访问,我们的电子邮件应用程序调用了实际的电子邮件发送。如果电子邮件完成并准备就绪,它将直接发送,否则将显示Outlook电子邮件表格,以使用户填写缺失字段并发送。

我们的电子邮件应用程序使用 tjclemail 处理电子邮件发送并显示Outlook电子邮件表单。我的问题是:如果Outlook未作为管理员运行,则电子邮件应用程序不会显示Outlook电子邮件表单,我猜是因为它是从管理运行应用程序中调用的,因此它继承了特权。由于Outlook几乎从未作为管理员运行,因此我想找到一种使用普通用户特权来调用 createProcess 的方法,该> ,该特权>从其呼叫者那里继承了管理员特权。

有办法这样做吗?

We have an application that is run with admin privileges, where (apart from other operations that actually require admin privileges) the user can send emails.

Our email system works like this: admin-run application precompiles the email fields and launches (via CreateProcess) our email application that calls the actual email send. If the email is complete and ready it will send it directly, otherwise it will show the Outlook email form to let the user fill the missing fields and send.

Our email application uses TJclEmail to handle email sending and showing Outlook email form. My problem is this: the email application won't show the Outlook email form if Outlook isn't run as administrator, I guess because it's called from the admin-run application so it inherits privileges. Since Outlook is hardly ever run as administrator I'd like to find a way to call CreateProcess with normal user privileges, insted of inheriting admin privileges from its caller.

Is there a way to do so?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

逆光飞翔i 2025-01-24 23:26:11

根据如何从提升的进程启动未提升的进程,反之亦然?

从非提升流程到提升流程很容易。您可以通过将 runas 动词传递给 ShellExecuteShellExecuteEx 来运行具有提升的进程。

走另一条路比较棘手。一方面,很难通过修改你的令牌来正确地消除海拔特性。另一方面,即使您可以做到这一点,但这也不是正确的做法,因为未提升的用户可能与提升的用户不同。

...

此处的解决方案是返回到 Explorer 并要求 Explorer 为您启动该程序。由于 Explorer 是作为原始未提升用户运行的,因此该程序(在本例中为 Web 浏览器)将作为鲍勃运行。如果您要打开的文件的处理程序作为进程内扩展而不是作为单独的进程运行,这一点也很重要,因为在这种情况下,尝试取消提升将是毫无意义的,因为在第一名。 (如果文件的处理程序尝试与自身的现有未提升副本进行通信,则可能会因 UIPI 的原因而失败。)

然后本文继续显示一个获取桌面的 IShellFolderViewDual 接口的示例,并从中创建一个 IShellDispatch2 接口,然后调用 IShellDispatch2::ShellExecute() 以登录用户身份执行新进程(这基本上是MSDN 上提供的相同示例: 在资源管理器示例中执行):

#define STRICT
#include <windows.h>
#include <shldisp.h>
#include <shlobj.h>
#include <exdisp.h>
#include <atlbase.h>
#include <stdlib.h>

void FindDesktopFolderView(REFIID riid, void **ppv)
{
 CComPtr<IShellWindows> spShellWindows;
 spShellWindows.CoCreateInstance(CLSID_ShellWindows);

 CComVariant vtLoc(CSIDL_DESKTOP);
 CComVariant vtEmpty;
 long lhwnd;
 CComPtr<IDispatch> spdisp;
 spShellWindows->FindWindowSW(
     &vtLoc, &vtEmpty,
     SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);

 CComPtr<IShellBrowser> spBrowser;
 CComQIPtr<IServiceProvider>(spdisp)->
     QueryService(SID_STopLevelBrowser,
                  IID_PPV_ARGS(&spBrowser));

 CComPtr<IShellView> spView;
 spBrowser->QueryActiveShellView(&spView);

 spView->QueryInterface(riid, ppv);
}

void GetDesktopAutomationObject(REFIID riid, void **ppv)
{
 CComPtr<IShellView> spsv;
 FindDesktopFolderView(IID_PPV_ARGS(&spsv));
 CComPtr<IDispatch> spdispView;
 spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
 spdispView->QueryInterface(riid, ppv);
}

void ShellExecuteFromExplorer(
    PCWSTR pszFile,
    PCWSTR pszParameters = nullptr,
    PCWSTR pszDirectory  = nullptr,
    PCWSTR pszOperation  = nullptr,
    int nShowCmd         = SW_SHOWNORMAL)
{
 CComPtr<IShellFolderViewDual> spFolderView;
 GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
 CComPtr<IDispatch> spdispShell;
 spFolderView->get_Application(&spdispShell);
 CComQIPtr<IShellDispatch2>(spdispShell)
    ->ShellExecute(CComBSTR(pszFile),
                   CComVariant(pszParameters ? pszParameters : L""),
                   CComVariant(pszDirectory ? pszDirectory : L""),
                   CComVariant(pszOperation ? pszOperation : L""),
                   CComVariant(nShowCmd));
}

int __cdecl wmain(int argc, wchar_t **argv)
{
 if (argc < 2) return 0;
 CCoInitialize init;
 ShellExecuteFromExplorer(
    argv[1],
    argc >= 3 ? argv[2] : L"",
    argc >= 4 ? argv[3] : L"",
    argc >= 5 ? argv[4] : L"",
    argc >= 6 ? _wtoi(argv[5]) : SW_SHOWNORMAL);
 return 0;
}

按照如何从提升的进程 redux 启动未提升的进程

还有另一种更直接的方法,但它假设您想要做的事情可以通过直接调用 CreateProcess 来完成。换句话说,如果您需要系统查找用户的文件关联或默认浏览器,那么此技术不适合您。

这个想法是利用PROCESS_CREATE_PROCESS访问权限和附带的PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 进程线程属性

...

基本上,这可以让您告诉 CreateProcess 函数,“嘿,嗯,假装那边的其他人正在创建该流程。”

这是该文章中的示例:

int main(int, char**)
{
  HWND hwnd = GetShellWindow();

  DWORD pid;
  GetWindowThreadProcessId(hwnd, &pid);

  HANDLE process =
    OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid);

  SIZE_T size;
  InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
  auto p = (PPROC_THREAD_ATTRIBUTE_LIST)new char[size];

  InitializeProcThreadAttributeList(p, 1, 0, &size);
  UpdateProcThreadAttribute(p, 0,
    PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
    &process, sizeof(process),
    nullptr, nullptr);

  wchar_t cmd[] = L"C:\\Windows\\System32\\cmd.exe";
  STARTUPINFOEX siex = {};
  siex.lpAttributeList = p;
  siex.StartupInfo.cb = sizeof(siex);
  PROCESS_INFORMATION pi;

  CreateProcessW(cmd, cmd, nullptr, nullptr, FALSE,
    CREATE_NEW_CONSOLE | EXTENDED_STARTUPINFO_PRESENT,
    nullptr, nullptr, &siex.StartupInfo, &pi);

  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);
  delete[] (char*)p;
  CloseHandle(process);
  return 0;
}

该程序使用 shell 进程(通常是 explorer.exe)作为其父进程来运行 cmd.exe 的副本,这意味着如果 shell 进程未提升,那么 cmd.exe 进程也会如此。当然,如果用户是管理员并且禁用了UAC,那么Explorer 仍将被提升,cmd.exe 也将被提升。但在这种情况下,用户希望一切都以提升的方式运行,因此您只需遵循用户的偏好即可。

Per How can I launch an unelevated process from my elevated process and vice versa?:

Going from an unelevated process to an elevated process is easy. You can run a process with elevation by passing the runas verb to Shell­Execute or Shell­Execute­Ex.

Going the other way is trickier. For one thing, it’s really hard to munge your token to remove the elevation nature properly. And for another thing, even if you could do it, it’s not the right thing to do, because the unelevated user may be different from the elevated user.

...

The solution here is to go back to Explorer and ask Explorer to launch the program for you. Since Explorer is running as the original unelevated user, the program (in this case, the Web browser) will run as Bob. This is also important in the case that the handler for the file you want to open runs as an in-process extension rather than as a separate process, for in that case, the attempt to unelevate would be pointless since no new process was created in the first place. (And if the handler for the file tries to communicate with an existing unelevated copy of itself, things may fail because of UIPI.)

And then the article goes on to show an example that gets the desktop's IShellFolderViewDual interface, and from that an IShellDispatch2 interface, and then calls IShellDispatch2::ShellExecute() to execute the new process as the logged-in user (which is basically the same example provided on MSDN: Execute In Explorer Sample):

#define STRICT
#include <windows.h>
#include <shldisp.h>
#include <shlobj.h>
#include <exdisp.h>
#include <atlbase.h>
#include <stdlib.h>

void FindDesktopFolderView(REFIID riid, void **ppv)
{
 CComPtr<IShellWindows> spShellWindows;
 spShellWindows.CoCreateInstance(CLSID_ShellWindows);

 CComVariant vtLoc(CSIDL_DESKTOP);
 CComVariant vtEmpty;
 long lhwnd;
 CComPtr<IDispatch> spdisp;
 spShellWindows->FindWindowSW(
     &vtLoc, &vtEmpty,
     SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);

 CComPtr<IShellBrowser> spBrowser;
 CComQIPtr<IServiceProvider>(spdisp)->
     QueryService(SID_STopLevelBrowser,
                  IID_PPV_ARGS(&spBrowser));

 CComPtr<IShellView> spView;
 spBrowser->QueryActiveShellView(&spView);

 spView->QueryInterface(riid, ppv);
}

void GetDesktopAutomationObject(REFIID riid, void **ppv)
{
 CComPtr<IShellView> spsv;
 FindDesktopFolderView(IID_PPV_ARGS(&spsv));
 CComPtr<IDispatch> spdispView;
 spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
 spdispView->QueryInterface(riid, ppv);
}

void ShellExecuteFromExplorer(
    PCWSTR pszFile,
    PCWSTR pszParameters = nullptr,
    PCWSTR pszDirectory  = nullptr,
    PCWSTR pszOperation  = nullptr,
    int nShowCmd         = SW_SHOWNORMAL)
{
 CComPtr<IShellFolderViewDual> spFolderView;
 GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
 CComPtr<IDispatch> spdispShell;
 spFolderView->get_Application(&spdispShell);
 CComQIPtr<IShellDispatch2>(spdispShell)
    ->ShellExecute(CComBSTR(pszFile),
                   CComVariant(pszParameters ? pszParameters : L""),
                   CComVariant(pszDirectory ? pszDirectory : L""),
                   CComVariant(pszOperation ? pszOperation : L""),
                   CComVariant(nShowCmd));
}

int __cdecl wmain(int argc, wchar_t **argv)
{
 if (argc < 2) return 0;
 CCoInitialize init;
 ShellExecuteFromExplorer(
    argv[1],
    argc >= 3 ? argv[2] : L"",
    argc >= 4 ? argv[3] : L"",
    argc >= 5 ? argv[4] : L"",
    argc >= 6 ? _wtoi(argv[5]) : SW_SHOWNORMAL);
 return 0;
}

And per How can I launch an unelevated process from my elevated process, redux:

There’s another way which is a bit more direct, but it assumes that the thing you want to do can be done with a direct Create­Process call. In other words, if you need the system to look up the user’s file associations or default browser, then this technique is not for you.

The idea is to take advantage of PROCESS_CREATE_PROCESS access and the accompanying PROC_THREAD_ATTRIBUTE_PARENT_PROCESS process thread attribute

...

Basically, this lets you tell the Create­Process function, "Hey, like, um, pretend that other guy over there is creating the process."

And here is the example from that article:

int main(int, char**)
{
  HWND hwnd = GetShellWindow();

  DWORD pid;
  GetWindowThreadProcessId(hwnd, &pid);

  HANDLE process =
    OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid);

  SIZE_T size;
  InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
  auto p = (PPROC_THREAD_ATTRIBUTE_LIST)new char[size];

  InitializeProcThreadAttributeList(p, 1, 0, &size);
  UpdateProcThreadAttribute(p, 0,
    PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
    &process, sizeof(process),
    nullptr, nullptr);

  wchar_t cmd[] = L"C:\\Windows\\System32\\cmd.exe";
  STARTUPINFOEX siex = {};
  siex.lpAttributeList = p;
  siex.StartupInfo.cb = sizeof(siex);
  PROCESS_INFORMATION pi;

  CreateProcessW(cmd, cmd, nullptr, nullptr, FALSE,
    CREATE_NEW_CONSOLE | EXTENDED_STARTUPINFO_PRESENT,
    nullptr, nullptr, &siex.StartupInfo, &pi);

  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);
  delete[] (char*)p;
  CloseHandle(process);
  return 0;
}

This program runs a copy of cmd.exe using the shell process (usually explorer.exe) as its parent, which means that if the shell process is unelevated, then so too will the cmd.exe process. Of course, if the user is an administrator and has disabled UAC, then Explorer will still be elevated, and so too will be the cmd.exe. But in that case, the user wants everything to run elevated, so you’re just following the user's preferences.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文