SetSecurityInfo 返回拒绝访问

Posted

技术标签:

【中文标题】SetSecurityInfo 返回拒绝访问【英文标题】:SetSecurityInfo returns access denied 【发布时间】:2017-05-09 17:41:20 【问题描述】:

使用 C,我试图在进程和它的子进程之间建立管道连接,而子进程的强制(完整性)级别较低(低,而父进程高)。

我编写了以下程序(如果它是简化版本),但它失败了:ERROR_ACCESS_DENIED (0x5)

INT wmain(IN SIZE_T nArgc, IN PWSTR *pArgv)

    SECURITY_ATTRIBUTES securityArrtibutes =  0 ;
    HANDLE hPipeRead = NULL;
    HANDLE hPipeWrite = NULL;

    tSecurityArrtibutes.nLength = sizeof(tSecurityArrtibutes);
    tSecurityArrtibutes.bInheritHandle = TRUE;

    SetSeSecurityNamePrivilege();
    CreatePipe(&hPipeRead, &hPipeWrite, &securityArrtibutes, 0);
    ChangeMandatoryLabelHandle(hPipeRead);


VOID ChangeMandatoryLabelHandle(HANDLE hObject)

    BOOL bRetval = FALSE;
    DWORD dwError = 0;
    PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
    PACL ptSacl = NULL;
    BOOL bSaclPresent = FALSE;
    BOOL bSaclDefaulted = FALSE;
    PWSTR pSDDL = NULL;

    SDDL = L"S:(ML;;LW;;;NW)";

    bRetval = ConvertStringSecurityDescriptorToSecurityDescriptorW(pSDDL, SDDL_REVISION_1, &pSecurityDescriptor, NULL);
    if (FALSE == bRetval)
        ... // Handle failure

    bRetval = GetSecurityDescriptorSacl(pSecurityDescriptor, &bSaclPresent, &ptSacl, &bSaclDefaulted);
    if (FALSE == bRetval)
        ... // Handle failure

    // getting ERROR_ACCESS_DENIED (0x5)
    dwErr = SetSecurityInfo(hObject, SE_KERNEL_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, ptSacl);
    if (ERROR_SUCCESS != dwErr)
        ... // Handle failure

    ... // Cleanup

我关注了https://msdn.microsoft.com/en-us/library/windows/desktop/aa379588(v=vs.85).aspx 和那句话 To set the SACL of an object, the caller must have the SE_SECURITY_NAME privilege enabled.

BOOL SetSeSecurityNamePrivilege()

    HANDLE hToken;
    TOKEN_PRIVILEGES tp;
    LUID luid;

    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_IMPERSONATE, &hToken)
        return FALSE

    if (!LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &luid))
        return FALSE;

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if (bEnablePrivilege)
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
        return FALSE;

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
        return FALSE;

    return TRUE;

注意:当我尝试使用文件执行它时,我得到了相同的结果,使用 CreateFile 而不是 CreatePipe。 此外,如果我尝试对文件执行此操作,并将SetSecurityInfo 替换为SetNamedSecurityInfoW,并为其提供文件的完整路径,效果很好。

有人知道如何让它工作吗?谢谢!

【问题讨论】:

正如 RbMm 在他的回答中解释的那样,更改安全描述符不太可能对您在此处实际尝试解决的任何问题产生任何影响。一旦你确信这一点,我建议你发布另一个关注你实际问题的问题。 【参考方案1】:

在解决直接问题的原因之前的一些注意事项。

首先,您根本不需要更改安全描述符,这样做不太可能帮助您实现最终目标。仅当您尝试打开对象的句柄时才检查安全描述符;如果您已经有句柄,则安全描述符无效。由于您正在创建一个未命名的管道,因此您必须将句柄而不是管道名称传递给子节点,因此您根本不需要 ChangeMandatoryLabelHandle 函数。

其次,设置LABEL_SECURITY_INFORMATION时不需要SE_SECURITY_NAME权限。强制标签在逻辑上与 SACL 的其余部分不同,并被视为特殊情况。

第三,您的"S:(ML;;LW;;;NW)" 无效。

我尝试在ConvertStringSecurityDescriptorToSecurityDescriptorW中使用它并得到错误1336,访问控制列表(ACL)结构无效。相反,使用"D:NO_ACCESS_CONTROLS:(ML;;;;;LW)"或更好仍然使用以下代码用于创建具有低标签且无 DACL 的安全描述符:

ULONG cb = MAX_SID_SIZE;
PSID LowLabelSid = (PSID)alloca(MAX_SID_SIZE);

ULONG dwError = NOERROR;

if (CreateWellKnownSid(WinLowLabelSid, 0, LowLabelSid, &cb))

    PACL Sacl = (PACL)alloca(cb += sizeof(ACL) + sizeof(ACE_HEADER) + sizeof(ACCESS_MASK));

    if (InitializeAcl(Sacl, cb, ACL_REVISION) && 
        AddMandatoryAce(Sacl, ACL_REVISION, 0, 0, LowLabelSid))
    
        SECURITY_DESCRIPTOR sd;
        InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
        SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
        SetSecurityDescriptorSacl(&sd, TRUE, Sacl, FALSE);

        SECURITY_ATTRIBUTES sa =  sizeof(sa), &sd, TRUE ;

        // todo something here
    
    else
    
        dwError = GetLastError();
    

else

    dwError = GetLastError();

但是,您需要再次了解,为未命名对象创建安全描述符(几乎)没有任何意义。仅在打开对象时检查安全描述符,并且(在用户模式下)您无法打开没有名称的对象。

(在内核模式下,我们可以使用ObOpenObjectByPointer通过指针打开一个对象。)

(在旧版本的 Windows 中,CreatePipe 实际上创建了一个随机名称的管道,但从 Windows 7 开始,该管道实际上是未命名的,因此无法使用 CreateFile 打开em> 或任何类似的方法。)

无论如何,我认为在这种情况下使用 CreatePipe 是一个糟糕的选择。这个函数设计的不好,参数太少。没有创建双向管道或以异步模式打开管道的选项。我认为最好使用 CreateNamedPipeWCreateFileW

(或者,从 Windows 7 开始,您可以使用 ZwCreateNamedPipeFileZwOpenFile 创建和打开未命名的管道。)


发布代码的最接近的问题是SetSecurityInfoSetKernelObjectSecurity 在使用CreatePipe 返回的句柄调用时返回ERROR_ACCESS_DENIED。这是因为,正如 LABEL_SECURITY_INFORMATION 的文档中所述:

需要设置的权限:WRITE_OWNER

由于CreatePipe 没有让您选择打开句柄时使用的访问权限,因此您无法执行此操作。如果你改用CreateNamedPipe,你可以在dwOpenMode中设置WRITE_OWNER

但是,您应该注意,如果您希望创建具有特殊安全描述符的对象,最好在创建对象时提供该安全描述符。使用默认安全描述符创建对象然后更改它是没有意义的;为什么要在两个操作中完成您可以在一个操作中完成的操作?在这种情况下,您传递给 CreatePipeCreateNamedPipeSECURITY_ATTRIBUTES 结构可用于指定安全描述符,提供另一种解决您当前问题的方法,尽管如前所述提到这实际上没有用。

【讨论】:

以上是关于SetSecurityInfo 返回拒绝访问的主要内容,如果未能解决你的问题,请参考以下文章

电脑C盘中,我删除程序时,出现无法删除,访问被拒绝现象,问,如何解决?

Spring Security 总是返回 403 被禁止,访问被拒绝

MFC GetLastError 返回 5(拒绝访问)

FindFirstFile 返回拒绝访问

Spring Boot Jwt 返回访问被拒绝

服务返回代码 5 中的 CreateProcessWithLogonw:访问被拒绝