使用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
}

我接着说
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,并为它提供文件的完整路径,那么它工作得很好。
有人知道怎么做吗?谢谢!

最佳答案

在解决你眼前问题的原因之前要注意几点。
首先,您根本不需要更改安全描述符,这样做不太可能帮助您实现最终目标。只有当您试图打开对象的句柄时,才会检查安全描述符;如果您已经有一个句柄,则安全描述符不起作用。由于要创建未命名的管道,因此必须将句柄(而不是管道名称)传递给子管道,因此根本不需要ChangeMandatoryLabelHandle函数。
其次,设置SE_SECURITY_NAME时不需要LABEL_SECURITY_INFORMATION权限。强制标签在逻辑上与SACL的其他部分不同,并被视为一种特殊情况。
第三,您的"S:(ML;;LW;;;NW)"无效。
我试图在convertstringsecuritydescriptorosecuritydescriptorw中使用它,但得到错误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或任何类似的方法打开它。)
无论如何,我认为在这个上下文中使用CreatePipe是一个错误的选择。这个函数设计得不好,参数太少。没有创建双向管道或以异步模式打开管道的选项。我认为最好使用CreateNamedPipeW和CreateFileW。
(或者,从Windows 7开始,可以使用ZwCreateNamedPipeFile和ZwOpenFile创建和打开未命名的管道。)
发布的代码的最接近问题是SetSecurityInfoSetKernelObjectSecurity在用AA>返回的句柄调用时返回ErrRyAccess拒绝。这是因为,正如CreatePipe的文档中所述:
需要设置的权限:写入所有者
由于LABEL_SECURITY_INFORMATION没有给您选择句柄打开时的访问权限的选项,因此您无法这样做。如果使用CreatePipe则可以在dwOpenMode中设置WRITE_OWNER。
但是,您应该注意,如果希望创建具有特殊安全描述符的对象,最好在创建该对象时提供该安全描述符。使用默认的安全描述符创建对象,然后更改它是没有意义的;为什么要在两个操作中完成一个可以完成的操作?在这种情况下,可以使用传递给CreatePipe或CreateNamedPipe的SECURITY_ATTRIBUTES结构来指定安全描述符,从而提供解决当前问题的另一种方法,尽管如前所述,这实际上并不有用。

09-25 19:00