当我不再需要它时,如何删除提升的权限

时间:2017-06-19 14:44:35

标签: c++ windows winapi

我有一个应用程序需要在管理员运行时注册控件,我希望应用程序在不再需要时删除提升的权限。我已经读过可以使用AdjustTokenPrivileges(Dropping privileges in C++ on Windows)完成此操作,但我没有找到任何允许我从SECURITY_MANDATORY_HIGH_RID转到SECURITY_MANDATORY_MEDIUM_RID的示例代码。我的代码是使用Visual Studio编写的C ++。

1 个答案:

答案 0 :(得分:0)

如果你想要

  

示例代码,允许我从 SECURITY_MANDATORY_HIGH_RID 开始   到 SECURITY_MANDATORY_MEDIUM_RID

您需要使用TOKEN_ADJUST_DEFAULT打开自己的进程令牌(对于更改完整性级别 - 这是必需的)和WRITE_OWNER(用于更改令牌安全描述符中的强制标签 - 否则您将无法更加开放具有写访问权限的令牌 - 但这是可选的)

使用SetTokenInformation致电TokenIntegrityLevel让降级当前的完整性级别。在此之后它已经无法提出。

当我们将完整性级别设置为SECURITY_MANDATORY_HIGH_RID以下时,

内部系统也会禁用令牌中的某些权限。我怀疑这是记录在案的,但是从我的测试中,下一个特权被禁用,并且无法再启用:

SeTakeOwnershipPrivilege
SeLoadDriverPrivilege
SeBackupPrivilege
SeRestorePrivilege
SeDebugPrivilege
SeImpersonatePrivilege
SeDelegateSessionUserImpersonatePrivilege

但您仍然是管理员组的成员( S-1-5-32-544管理员),AdjustTokenGroups无法禁用此组,因为函数无法禁用组SE_GROUP_MANDATORY属性 - 但 S-1-5-32-544 具有此属性。并且也不可能更改主进程令牌(只有在创建进程后(处于挂起状态)并且在它开始执行(恢复)之前,如果我们有SeAssignPrimaryTokenPrivilege

因此,在将完整性级别降级为中等之后,您的应用程序将处于中间状态 - 从一方丢失最重要的权限,但访问对象(文件,注册表项)主要不基于权限但是团体成员资格和强制性标签 - 完整性等级。因为管理员组仍将在您的令牌中启用,并且大多数对象没有明确的强制标签(默认为中等) - 您的应用仍然可以打开/创建限制应用程序的文件/密钥(管理员在uac下) )不能。但是,如果您将完整性级别降级为SECURITY_MANDATORY_LOW_RID - 您的应用程序确实有限,但大多数遗留代码在LowLevel完整性下工作不正确

降级完整性级别的最小代码:

ULONG SetMediumLevel()
{
    HANDLE hToken;

    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT, &hToken))
    {
        ULONG cbSid = GetSidLengthRequired(1);

        TOKEN_MANDATORY_LABEL tml = { { alloca(cbSid)} };

        ULONG dwError = NOERROR;

        if (!CreateWellKnownSid(WinMediumLabelSid, 0, tml.Label.Sid, &cbSid) ||
            !SetTokenInformation(hToken, TokenIntegrityLevel, &tml, sizeof(tml)))
        {
            dwError = GetLastError();
        }

        CloseHandle(hToken);

        return dwError;
    }

    return GetLastError();
}

但这里存在瘦点 - 令牌本身具有带显式标签的安全描述符。和高完整性流程具有High Mandatory LabelSYSTEM_MANDATORY_LABEL_NO_WRITE_UP访问策略。这意味着我们不能再使用写访问权限(TOKEN_ADJUST_*)打开我们的进程令牌(具有读访问权限)。如果某个地方的应用程序尝试使用此访问权限打开自我进程令牌,则会产生问题(一些错误的设计代码可能需要查询自己的进程令牌属性而不是TOKEN_QUERY访问问TOKEN_ALL_ACCESS并在此时失败)。为了防止这个潜在的问题,我们可以在降级它的完整性之前更改我们的令牌安全描述符的强制标签:

ULONG SetMediumLevel()
{
    HANDLE hToken;

    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT|WRITE_OWNER, &hToken))
    {
        ULONG cbSid = GetSidLengthRequired(1);

        TOKEN_MANDATORY_LABEL tml = { { alloca(cbSid)} };

        ULONG dwError = NOERROR;
        if (CreateWellKnownSid(WinMediumLabelSid, 0, tml.Label.Sid, &cbSid))
        {
            SECURITY_DESCRIPTOR sd;
            ULONG cbAcl = sizeof(ACL) + FIELD_OFFSET(SYSTEM_MANDATORY_LABEL_ACE, SidStart) + cbSid;
            PACL Sacl = (PACL)alloca(cbAcl);

            if (!InitializeAcl(Sacl, cbAcl, ACL_REVISION) ||
                !AddMandatoryAce(Sacl, ACL_REVISION, 0, SYSTEM_MANDATORY_LABEL_NO_WRITE_UP, tml.Label.Sid) ||
                !InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) ||
                !SetSecurityDescriptorSacl(&sd, TRUE, Sacl, FALSE) ||
                !SetKernelObjectSecurity(hToken, LABEL_SECURITY_INFORMATION, &sd) ||
                !SetTokenInformation(hToken, TokenIntegrityLevel, &tml, sizeof(tml)))
            {
                dwError = GetLastError();
            }
        }

        CloseHandle(hToken);

        return dwError;
    }

    return GetLastError();
}