Trying to understand process privilege attributes

2019-04-10 18:01发布

问题:

I'm writing a logging service that may collect privileges of a process and I'm trying to understand attributes for each process privilege. Let me explain with this code:

HANDLE hToken;
if(OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
    DWORD dwSize = 0;
    if(!GetTokenInformation(hToken, TokenPrivileges, NULL, dwSize, &dwSize) &&
        ::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        BYTE* pb = new (std::nothrow) BYTE[dwSize];
        if(pb)
        {
            TOKEN_PRIVILEGES* pTPs = (TOKEN_PRIVILEGES*)pb;
            DWORD dwSize2;
            if(GetTokenInformation(hToken, TokenPrivileges, pTPs, dwSize, &dwSize2) &&
                dwSize2 <= dwSize)
            {
                for(UINT i = 0; i < pTPs->PrivilegeCount; i++)
                {
                    //Analyze privilege attributes to understand if it's enabled or disabled?
                    DWORD dwPrivAttr = pTPs->Privileges[i].Attributes;

                    //...
                }
            }

            delete[] pb;
        }
    }

    CloseHandle(hToken);
}

So let's see the structure of TOKEN_PRIVILEGES and LUID_AND_ATTRIBUTES in particular:

#define SE_PRIVILEGE_ENABLED_BY_DEFAULT (0x00000001L)
#define SE_PRIVILEGE_ENABLED            (0x00000002L)
#define SE_PRIVILEGE_REMOVED            (0X00000004L)
#define SE_PRIVILEGE_USED_FOR_ACCESS    (0x80000000L)

It looks like it is defined as a bitmask, but that brings up the following questions of interpreting these flags:

  1. What is the difference between ENABLED and ENABLED_BY_DEFAULT?

  2. What is SE_PRIVILEGE_USED_FOR_ACCESS and how can it be used?

  3. What if both SE_PRIVILEGE_ENABLED and SE_PRIVILEGE_REMOVED are set? Or, reset?

  4. I just ran a simple test and for my process the SeShutdownPrivilege privilege had those attributes set as 0. So what is that supposed to mean?

I'm more confused over this structure, but I'll keep it at just these points for now.

Thank you!

回答1:

To take your questions in order:

  1. ENABLED_BY_DEFAULT means the privilege is one of those that is enabled when the process starts. If you have ENABLED but not ENABLED_BY_DEFAULT then the process has enabled the privilege explicitly. If you have ENABLED_BY_DEFAULT but not ENABLED then the process has disabled the privilege explicitly.

  2. According to the documentation, SE_PRIVILEGE_USED_FOR_ACCESS is set whenever the privilege is actually used. You might use this for troubleshooting, e.g., to detect that you are setting privileges that you are not actually using, or to determine experimentally which privileges a particular system call needs. (I've never checked whether this actually behaves as documented, though I have no reason to think otherwise.)

  3. If both SE_PRIVILEGE_ENABLED and SE_PRIVILEGE_REMOVED are set, you've found a bug in Windows. :-)

    If neither SE_PRIVILEGE_ENABLED nor SE_PRIVILEGE_REMOVED are set, then the privilege is present in the token, and has not been removed, but is not currently enabled. You can enable it (or remove it) with AdjustTokenPrivileges().

  4. If the attribute is zero, then the privilege is present in the token but is not currently enabled, has not been removed, was not enabled by default, and has never been used by the process.



回答2:

What some of us may need to be told explicitly is that privileges have THREE possible states, not just two. As I began researching this stuff, I thought that a process would either have, or not have a privilege. But it turns out that even if the process has a privilege, it can be in a disabled state. In other words, disabled != doesn't have it.

The rest follows logically. If a privilege is not present in the process' access token, the process does not have that privilege. And vice versa, if the process does not have a privilege, the privilege will not be present in the token.

If the process has the privilege, the process can enable or disable it at will, right? Why is this useful? All right, I can guess that this allows you to call library functions without fully knowing what they do, and have them fail if they do more than you thought... Odd though.