【问题标题】:CreateMutex fails after impersonation模拟后 CreateMutex 失败
【发布时间】:2017-05-13 03:51:31
【问题描述】:

这是我试图模拟用户然后创建互斥锁的代码。未创建互斥锁。我收到 ERROR_ACCESS_DENIED 错误。

void Impersonate()
{
    DWORD logonType = LOGON32_LOGON_INTERACTIVE;
    DWORD logonProvider = LOGON32_PROVIDER_DEFAULT;
    HANDLE userToken;
    HANDLE hMutex;
    DWORD err;

    LPSTR user = "zoom"; // the user I created myself on my machine. 
    // It has Administrator privileges, and my account, 
    // from which I start the app, is Admin too
    LPSTR password = "zoom";
    LPSTR domain = ".";
    hMutex = NULL;

    LogonUserA(user, domain, password, logonType, logonProvider,&userToken);

    // just to make sure that mutexes are created fine before impersonation
    hMutex = CreateMutexA( NULL, FALSE, "mutex_good" );  

    ImpersonateLoggedOnUser(userToken);

    hMutex = CreateMutexA( NULL, FALSE, "mutex_797" ); // I can set any 
                                                       // random name, no difference
    if( hMutex == NULL )
    {
        err = GetLastError();
        // here err is ERROR_ACCESS_DENIED 
    }

    CloseHandle(userToken);
}

我发现了几个类似的主题,但他们都在讨论从两个不同的用户上下文创建同名互斥锁,即在模拟之前已经创建了一个互斥锁“MUTEX_1”,并尝试使用相同的名称调用 CreateMutex 但由于缺乏权限,来自模拟用户的请求失败。

这里不是这种情况,因为我很确定在此代码之前没有创建同名的互斥锁(或任何互斥锁)。

我想我应该将一些非空值传递给 CreateMutex,但究竟是什么?

我在 Windows 安全方面不是很好。我理解将 NULL 作为 CreateMutex 的第一个参数传递意味着将使用“默认”安全属性。在这种情况下,它将是与线程相关联的安全参数,即与模拟用户。

我的假设是否正确?

【问题讨论】:

  • RbMm 的回答解释了这不起作用的技术原因,并提供了一种解决方法(尽管不受支持)。由于两个帐户都是管理员,并且假设您以提升的权限运行程序,更好的解决方法是指定 LOGON32_LOGON_BATCH(而不是交互式),以便模拟令牌具有管理员权限。
  • 但是,最好还是退后一步,问问你为什么一开始就在模拟时尝试创建互斥锁。这是一件相当奇怪的事情。你想达到什么目标?
  • @HarryJohnston - “尽管不受支持” - 实际上我们不需要使用 Restricted 子文件夹。我们可以简单地使用带有“Global\\”前缀的CreateMutexA(0, 0, "Global\\mutex_797");,仅记录在案 - msdn.microsoft.com/en-us/library/windows/desktop/… 。我最初将\BaseNamedObjects\GLOBAL?? 混淆了,忘记了Everyone \BaseNamedObjects 有写访问权。对不起
  • @RbMm:嗯。我认为您需要管理员权限才能在全局命名空间中创建任何对象;我不知道这只适用于文件映射对象。谢谢!我同意创建全局互斥锁至少与使用管理员权限一样好,但我仍然怀疑 OP 在模拟时创建互斥锁是否有意义。另见blogs.msdn.microsoft.com/oldnewthing/20110928-00/?p=9533
  • 啊。对我来说,这听起来像是一个不合理的客户要求,但无论如何,迎合它当然是你的特权。 :-) RbMm 关于创建全局互斥锁的建议绝对是您最好的选择,但请确保您已经考虑过如果不止一个人登录并运行您的代码的后果。您可能还想查看我上面提到的博客文章,我相信它提到了您可能需要注意的许多其他潜在的假冒问题。

标签: c++ windows mutex impersonation


【解决方案1】:

首先您需要了解NT Namespaces 并使用WinObj 工具。

这就像内存中的小文件系统,其中包含文件夹和不同的“文件”(此处“文件”下的“文件”表示不同的对象类型 - EventMutant(mutex)、SectionDevice、 ...)。每次创建命名对象时 - 它都放在NT Namespaces 中的某个文件夹中。此处的文件夹(如 NTFS 中的文件夹)具有安全描述符。结果不是每个人都可以在任何文件夹下创建对象。

解释一下您在文件系统语言上所做的事情(也许会更清楚):

我(John)尝试在%USERPROFILE%\Documents 下创建文件“mutex_good”,这没关系。因为这是我的个人文件夹,我有写权限。

然后我以zoom 登录(模拟)并尝试在%USERPROFILE%\Documents 下再次创建文件“mutex_797”(%USERPROFILE% 在两种情况下都扩展为相同的路径,比如c:\Users\John 模拟不影响此)

zoom 无法创建文件。为什么 ?只是他没有权利这样做。只有JohnAdministartorsSYSTEMc:\Users\John 有写访问权,但对zoom 没有写访问权。

现在让我们返回NT Namespaces。当我们调用CreateMutexA( NULL, FALSE, "mutex_797" ); 时,"mutex_797" 会放在哪里?

如果您不是 appcontainer 并且未在系统 session 0 中运行 - 您在某些用户 session <N> 中运行,您的命名对象将被放置在 \Sessions\<N>\BaseNamedObjects 目录中,其中 N=1,2..

所以打电话给CreateMutexA( NULL, FALSE, "mutex_797" );

尝试在\Sessions\<N>\BaseNamedObjects\mutex_797创建互斥锁

但是在\Sessions\<N>\BaseNamedObjects 中存在下一个符号链接(这就像在 NTFS 文件系统中一样):

Global -> \BaseNamedObjects
Local  -> \Sessions\<N>\BaseNamedObjects
Session -> \Sessions\BNOLINKS

如果你打电话给CreateMutexA( NULL, FALSE, "Global\\mutex_797" );就说吧

对象管理器尝试将您的互斥锁放在\BaseNamedObjects\mutex_797

更多信息请阅读Kernel object namespaces

当然我们必须了解How AccessCheck Works

为目录对象定义了下一个访问权限:

//
// Object Manager Directory Specific Access Rights.
//

#define DIRECTORY_QUERY                 (0x0001)
#define DIRECTORY_TRAVERSE              (0x0002)
#define DIRECTORY_CREATE_OBJECT         (0x0004)
#define DIRECTORY_CREATE_SUBDIRECTORY   (0x0008)

也可以在DirectoryObject DesiredAccess Flags了解更多信息

我们需要DIRECTORY_CREATE_OBJECT 访问权限(对目录对象的名称创建访问权限)以便在目录中创建互斥锁(或事件或任何对象)

现在了解为什么您可以在 \Sessions\&lt;N&gt;\BaseNamedObjects 创建互斥锁但 zoom 不能 - 需要为此文件夹查找 Security Descriptor。我转储它:

T FL AcessMsK Sid
0 00 000F000F S-1-5-90-0-1 DWM-1
0 00 000F000F S-1-5-18 SYSTEM
0 0B 10000000 S-1-5-18 SYSTEM
0 0B 10000000 S-1-3-0 CREATOR OWNER
0 00 000F000F S-1-5-21-4026734978-3280735129-2412320105-1001 John
0 0B 10000000 S-1-5-5-0-294807 LogonSessionId_0_294807
0 00 0002000F S-1-5-5-0-294807 LogonSessionId_0_294807
0 00 0002000F S-1-5-32-544 Administrators
0 02 00000003 S-1-1-0 Everyone
0 00 00000002 S-1-5-12 RESTRICTED
17 00 00000001 S-1-16-4096 Low Mandatory Level

那么这里有DIRECTORY_CREATE_OBJECT (4) 吗? DWM-1SYSTEMAdministrators、当前登录会话用户 (LogonSessionId_0_294807)、当前用户 (John) - 等等。 zoom 没有此权限。

例如 Everyone 有 (3) - DIRECTORY_QUERY|DIRECTORY_TRAVERSE - Name lookupQuery 但没有 Name creation

你可以问,在这种情况下,我可以在模拟后在哪里创建互斥锁?需要使用 \BaseNamedObjects ( global namespace )OR \BaseNamedObjects\Restricted 目录 - 我测试它的安全描述符和结果:

\BaseNamedObjects

T FL AcessMsK Sid
0 00 0002000F S-1-1-0 Everyone
0 00 00000002 S-1-5-12 RESTRICTED
0 00 000F000F S-1-5-90-0-0 
0 00 000F000F S-1-5-18 SYSTEM
17 00 00000001 S-1-16-4096 Low Mandatory Level

\BaseNamedObjects\Restricted

T FL AcessMsK Sid
0 00 0002000F S-1-1-0 Everyone
0 00 0002000F S-1-5-12 RESTRICTED
0 00 000F000F S-1-5-90-0-0 
0 00 000F000F S-1-5-18 SYSTEM
17 00 00000001 S-1-16-4096 Low Mandatory Level

所以你如何查看Everyone 这里有 2000F - 所有需要的访问权限。希望zoomEveryone 的成员?下一个代码我肯定会工作

CreateMutexA(0, 0, "Global\\mutex_797");

对于\BaseNamedObjects全局命名空间)存在一个例外:

在全局命名空间中创建文件映射对象,由 使用 CreateFileMapping,来自会话零以外的会话是 特权操作。因此,运行在 任意远程桌面会话主机(RD Session Host)服务器会话 必须启用 SeCreateGlobalPrivilege 才能创建 全局命名空间中的文件映射对象成功。这 权限检查仅限于创建文件映射对象, 并且不适用于打开现有的。例如,如果一个服务 或者系统创建一个文件映射对象,任何运行在 任何会话都可以访问该文件映射对象,前提是用户 具有必要的访问权限。

但对于 Mutex 或说事件 - 不需要启用 SeCreateGlobalPrivilege


您也可以说,但zoom 是管理员帐户,而Administrator 可以访问\Sessions\&lt;N&gt;\BaseNamedObjects - 那为什么这不起作用?因为LOGON32_LOGON_INTERACTIVEUAC 系统分配给zoom 过滤令牌。 Administrator 组 (S-1-5-32-544) 存在于令牌中 仅具有 SE_GROUP_USE_FOR_DENY_ONLY 属性 - 因此它忽略了 SID 的允许访问 ACE。 和zoom 有另一个LogonSessionId_0_XXX SID - 结果和ERROR_ACCESS_DENIED

如@Harry Johnston 所述-如果我们将使用LOGON32_LOGON_BATCH 而不是LOGON32_LOGON_INTERACTIVE-我们得到了提升的令牌-这里Administrator 组将具有SE_GROUP_ENABLED 属性-启用了对允许访问的ACE 的访问检查

或我提供的方式 - 在名称前使用 Global\ 前缀 - 将对象放置到 \BaseNamedObjects 其中 Everyone 具有完全访问权限

我理解将 NULL 作为CreateMutex 的第一个参数传递意味着 将使用“默认”安全属性。在这种情况下,它将 是与线程关联的安全参数,即与 冒充用户。

第一个参数 - 指向SECURITY_ATTRIBUTES 的指针让您覆盖新对象的默认安全描述符。这是控制谁可以访问它。但这不会让您或多或少地访问您尝试放置对象的目录 - 您必须授予 DIRECTORY_CREATE_OBJECT 访问权限,我们不能通过 SECURITY_ATTRIBUTES 影响这一点 - 这会影响 新对象 但不会在现有目录上

最后是NT Namespace 的一些可视化

【讨论】:

  • @engf-010 - 抱歉英语不是我的母语,我不擅长它。我尝试描述并非所有令牌都可以访问默认对象目录 - 结果zoom 在此处创建互斥锁失败。但他可以在"Global\Restricted\mutex_797 创建它 - 这里不太严格的 SD
  • :好多了。作为一个非英语母语的读者,这个我能理解。
  • 没关系,我也不是母语人士,你的回答很清楚,很有帮助。
  • 所以,我的错误是假设命名对象 ar 在独立的命名空间中创建,并且每个用户都有自己的命名空间,例如 \Users\John\BaseNamedObjects\mutex_good 和 \Users\zoom\BaseNamedObjects\互斥体_797。现在我发现情况并非如此,并且对象是在“会话”命名空间下创建的,我不能通过模拟(或以任何其他方式更改会话,因为整个进程属于某个会话,并且该会话由启动进程的用户)
  • @VladimirNavrotsky - 我进行更新 - 添加更多信息 - 我们真的可以使用 CreateMutexA(0, 0, "Global\\mutex_797"); - 没有 Restricted 子文件夹。我最初将此与\GLOBAL?? 混淆
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-27
  • 2010-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-18
相关资源
最近更新 更多