How to check if a TRUSTEE is me

1

I'm using the GetSecurityInfo funtion to get my own process's Discretional Access Control List (DACL):

PACL oldAcl;
Pointer se;
GetSecurityInfo(GetCurrentProcess, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, 
      null, null, ref oldAcl, nil, ref se);

And then i can use the GetExplicitEntriesFromAcl to crack open the ACL to get at it's list of Access Control Entries (ACE) inside:

UInt32 nCount;
EXPLICIT_ACCESS[] list;
GetExplicitEntriesFromAcl(oldAcl, ref nCount, ref list);

I can go through the list of three entries on my process:

  • STACKOVERFLOW\ian (S-1-5-21-6198258843-697258998-2146844275-1109) [SidTypeUser]

    • Grant Access
      • 0x00010000 DELETE
      • 0x00020000 READ_CONTROL
      • 0x00040000 WRITE_DAC
      • 0x00080000 WRITE_OWNER
      • 0x00100000 SYNCHRONIZE
      • ...
      • 0x00000008 PROCESS_VM_OPERATION
      • 0x00000010 PROCESS_VM_READ
      • 0x00000020 PROCESS_VM_WRITE
  • NT AUTHORITY\SYSTEM (S-1-5-18) [SidTypeWellKnownGroup]

    • Grant Access
      • 0x00010000 DELETE
      • 0x00020000 READ_CONTROL
      • 0x00040000 WRITE_DAC
      • 0x00080000 WRITE_OWNER
      • 0x00100000 SYNCHRONIZE
      • ...
      • 0x00000008 PROCESS_VM_OPERATION
      • 0x00000010 PROCESS_VM_READ
      • 0x00000020 PROCESS_VM_WRITE
  • NT AUTHORITY\LogonSessionId_0_20117843 (S-1-5-5-0-20117843) [SidType_11]
    • Grant Access
      • 0x00020000 READ_CONTROL
      • 0x00100000 SYNCHRONIZE
      • 0x00000010 PROCESS_VM_READ
      • 0x00000400 PROCESS_QUERY_INFORMATION
      • 0x00001000 PROCESS_QUERY_LIMITED_INFORMATION

I now want to go through and update the DACL for the process (which of course i'm allowed to do since i have WRITE_DACL - and because i'm the Owner, which means i implicitly have WRITE_DACL).

But i only want to re-write access control entries that apply to "me".

In this case there happen to be three Trustees:

  • S-1-5-21-6198258843-697258998-2146844275-1109 (user me)
  • S-1-5-18 (user LocalSystem - not me)
  • S-1-5-5-0-20117843 (group LoginSession - me)

Trustees are presented to us as a TRUSTEE object (note, not all of which have an SID). I know from experience that i am two of those trustees; but not the third.

Is there a function that i can use to compare "me" against a TRUSTEE?

Boolean DoIMatchThisTrustee(TRUSTEE trustee)
{

}

Why am i doing this?

No reason. I'm removing PROCESS_VM_READ, PROCESS_VM_WRITE, and PROCESS_VM_OPERATION from myself on my own process.

windows
security
winapi
asked on Stack Overflow Nov 20, 2017 by Ian Boyd • edited Nov 20, 2017 by Ian Boyd

3 Answers

1

you actually want check Discretional Access Control List (DACL) for Sids which is "me" - so enabled members of your process token.

To determine whether a SID is enabled in a token we can use CheckTokenMembership function.

use GetSecurityInfo not the best choice i think, much better use GetKernelObjectSecurity here. however you can check and trustees if want ("note, not all of which have an SID" - this is in general case, but in case DACL you will got only TRUSTEE_IS_SID trustees). code can be next:

void Test()
{
    HANDLE hToken, hImpToken;

    if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY|TOKEN_DUPLICATE, &hToken))
    {
        BOOL fOk = DuplicateToken(hToken, ::SecurityIdentification, &hImpToken);

        CloseHandle(hToken);

        if (fOk)
        {
            ULONG cb = 0, rcb = 256;

            static volatile UCHAR guz = 0;
            PVOID stack = alloca(guz);
            union {
                PVOID buf;
                PSECURITY_DESCRIPTOR pSD;
            };

            do 
            {
                if (cb < rcb)
                {
                    cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
                }

                if (GetKernelObjectSecurity(NtCurrentProcess(), DACL_SECURITY_INFORMATION, pSD, cb, &rcb))
                {
                    BOOL bPresent, bDefault;

                    union {
                        PACL Acl;
                        PBYTE pb;
                        PACE_HEADER pah;
                        PACCESS_ALLOWED_ACE paaa;
                    };

                    if (GetSecurityDescriptorDacl(pSD, &bPresent, &Acl, &bDefault) && bPresent && Acl)
                    {
                        CheckSidsInAcl(Acl);

                        if (USHORT AceCount = Acl->AceCount)
                        {
                            Acl++;

                            do 
                            {
                                if (pah->AceType == ACCESS_ALLOWED_ACE_TYPE)
                                {
                                    BOOL IsMember;
                                    if (CheckTokenMembership(hImpToken, &paaa->SidStart, &IsMember))
                                    {
                                        PWSTR sz;
                                        if (ConvertSidToStringSid(&paaa->SidStart, &sz))
                                        {
                                            DbgPrint("%x %S\n", IsMember, sz);
                                            LocalFree(sz);
                                        }
                                    }
                                    else
                                    {
                                        GetLastError();
                                    }
                                }

                            } while (pb += pah->AceSize, --AceCount);
                        }
                    }
                    break;
                }

            } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);

            CloseHandle(hImpToken);
        }
    }
}
answered on Stack Overflow Nov 20, 2017 by RbMm
0

I don't think there are any functions to compare nor normalize a TRUSTEE.

The ACEs in a ACL always store a SID (as far as I know) so it is probably safe to assume that the TRUSTEE will be a TRUSTEE_IS_*SID form when you get it from a ACL.

PSID GetSID(const TRUSTEE&t)
{
    if (TRUSTEE_IS_SID == t.TrusteeForm) return (PSID) t.ptstrName;
    if (TRUSTEE_IS_OBJECTS_AND_SID == t.TrusteeForm) return ((OBJECTS_AND_SID*)t.ptstrName)->pSid;
    return NULL;
}

bool DoIMatchThisTrustee(TRUSTEE&t)
{
  PSID tsid = GetSID(t);
  PSID mysid = GetMySid(); // From process/thread token or somewhere else
  return tsid && EqualSid(tsid, mysid);
}

If you don't want to assume then you can use LookupAccountName on the string forms to get a SID.

If for whatever reason you don't want to lookup any strings you can do things NT4 style and work with the ACL directly. Call GetAce to enumerate the ACL and use something like this:

PSID GetAllowedSID(const ACE_HEADER&ah)
{
    switch(ah.AceType)
    {
    case ACCESS_ALLOWED_ACE_TYPE: return (PSID) &((ACCESS_ALLOWED_ACE*)&ah)->SidStart;
    case ACCESS_ALLOWED_CALLBACK_ACE_TYPE: return (PSID) &((ACCESS_ALLOWED_CALLBACK_ACE*)&ah)->SidStart;
    case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE: return (PSID) &((ACCESS_ALLOWED_CALLBACK_OBJECT_ACE*)&ah)->SidStart;
    case ACCESS_ALLOWED_OBJECT_ACE_TYPE: return (PSID) &((ACCESS_ALLOWED_OBJECT_ACE*)&ah)->SidStart;
    default: return NULL;
    }
}

If you are doing this to increase security you might want to do things the other way around and only allow "NT AUTHORITY\SYSTEM" and "BUILTIN\Administrators" these rights.

Whether or not the logon session is "you" is debatable but you cannot compare the full SID to find out, just the SECURITY_NT_AUTHORITY+SECURITY_LOGON_IDS_RID parts.

answered on Stack Overflow Nov 20, 2017 by Anders • edited Nov 20, 2017 by Anders
0

RbMm's comment reminded me that i already knew the answer somewhere deep in my brain: CheckTokenMembeship:

//Get the ACL on the process
PACL oldAcl;
Pointer se;

GetSecurityInfo(GetCurrentProcess, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, 
      null, null, ref oldAcl, nil, ref se); //allocates memory into se, which we must LocalFree later
//oldAcl is a pointer to inside the se blob


//Crack open the ACL, to feast on the entries inside
UInt32 nCount;
EXPLICIT_ACCESS[] list;
GetExplicitEntriesFromAcl(oldAcl, ref nCount, ref list); //allocates memory into list, which we must localFree later


//the flags i want to remove me from having    
DWORD removeFlags = PROCESS_VM_READ || PROCESS_VM_WRITE || PROCESS_VM_OPERATION;

//Go through the list, looking for entries that are "me"
for (EXPLICIT_ACCESS ea in list)
{
   if (ea.Trustee.TrusteeForm != TRUSTEE_IS_SID) 
      continue;

   BOOL isMember;
   CheckTokenMembership(0, PSID(ea.Trustee.ptstrName), out isMember);

   if (!isMember)
      continue;

   //Remove the permissions
   ea.grfAccessPermissions = ea.grfAccessPermissions && (!removeFlags);
   aclUpdateNeeded = true;
}


//write the new DACL
SetEntriesInAcl(nCount, list, null, out newAcl); //allocates a new acl
SetSecurityInfo(GetCurrentProcess, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, 
      null, null, newAcl, null); //apply the new acl

LocalFree(list); //free the memory allocated by GetExplicitEntriesFromAcl
LocalFree(se); //free the memory allocated by GetSecurityInfo
answered on Stack Overflow Nov 21, 2017 by Ian Boyd

User contributions licensed under CC BY-SA 3.0