Tuesday, April 26, 2016

How to determine whether a process's privilege exists and its enabled/disabled?

Leave a Comment

SCENARIO


I would like to determine whether the specified process has enabled a specific privilege.

To make things easier for this question, the example target process will be the current process, and I'll check for the right to shutdown the local system (previouslly enabled with AdjustTokenPrivileges function).

Then, I found the PrivilegeCheck function that seems can determine whether a specified set of privileges are enabled in an access token of a target process.

UPDATE

I think that I focused on the wrong direction, because seems that the PrivilegeCheck function needs impersonation, so now I'm facing another neverending trial-and-error phase trying the GetTokenInformation function, which seems the proper function to realize this task.

PROBLEM


The problem I have is that when I try to use the PrivilegeCheck function, it always returns False (error), and the by-reference array of privileges does not have the expected values (because the function failed).

UPDATE

The GetTokenInformation function fails too with a False value, returning this win32 error code: 122 (HRESULT: -2147467259) with message:

Data Area Passed to a System Call Is Too Small

QUESTION


What I should do to fix the errors I'm getting on my code to be able check whether a process's privilege exists, and then, whether the privilege is enabled or disabled?.

Using PrivilegeCheck or GetTokenInformation functions, or just any other damn function that could determine the privilege state.

SOURCE-CODE


This is a full copyable example (together with the p/invokes below) on where I'll demonstrate how I'm testing both the PrivilegeCheck and the GetTokenInformation methodologies, both fails.

Dim pHandle As IntPtr = Process.GetCurrentProcess().Handle Dim privilegeName As String = "SeShutdownPrivilege" Dim tokenAccess As TokenAccess = (TokenAccess.AdjustPrivileges Or TokenAccess.Query Or TokenAccess.Duplicate) Dim hToken As IntPtr Dim hTokenDup As IntPtr  Try     ' ****************************************************************************     ' 1st Step: Enable the "SeShutdownPrivilege" privilege in the current process.     ' ****************************************************************************      Dim win32Err As Integer      ' Get the process token.     NativeMethods.OpenProcessToken(pHandle, tokenAccess, hToken)      ' Set up a LuidAndAttributes structure containing the privilege to enable,     ' getting the LUID that corresponds to the privilege.     Dim luAttr As New LuidAndAttributes     luAttr.Attributes = TokenPrivilegeAttributes.PrivilegeEnabled     NativeMethods.LookupPrivilegeValue(Nothing, privilegeName, luAttr.Luid)      ' Set up a TokenPrivileges structure containing only the source privilege.     Dim newState As New TokenPrivileges     newState.PrivilegeCount = 1     newState.Privileges = New LuidAndAttributes() {luAttr}      ' Set up a TokenPrivileges structure for the previous (modified) privileges.     Dim prevState As New TokenPrivileges     prevState = New TokenPrivileges     ReDim prevState.Privileges(CInt(newState.PrivilegeCount))      ' Apply the TokenPrivileges structure to the source process token.     Dim bufferLength As Integer = Marshal.SizeOf(prevState)     Dim returnLength As IntPtr     If Not NativeMethods.AdjustTokenPrivileges(hToken, False, newState, bufferLength, prevState, returnLength) Then         win32Err = Marshal.GetLastWin32Error         MessageBox.Show("AdjustTokenPrivileges failed.")         Throw New Win32Exception(win32Err)     End If      ' *********************************************************************     ' Everything OK at this point,      ' as AdjustTokenPrivileges dididn't failed, I assume the privilege Is enabled in the process.     '     ' 2n Step: Check whether the privilege is enabled or not...     ' *********************************************************************      ' Set up a new one LuidAndAttributes structure containing the privilege to check,     ' getting the LUID that corresponds to the privilege.     luAttr = New LuidAndAttributes     NativeMethods.LookupPrivilegeValue(Nothing, privilegeName, luAttr.Luid)      ' *********************************************************************     ' Trying PrivilegeCheck and Duplicatetoken methodology...     ' *********************************************************************      NativeMethods.DuplicateToken(hToken, SecurityImpersonationLevel.SecurityImpersonation, hTokenDup)     win32Err = Marshal.GetLastWin32Error      If (hTokenDup <> IntPtr.Zero) Then         Dim result As Boolean         Dim pSet As New PrivilegeSet         pSet.Control = 0         pSet.PrivilegeCount = 1         pSet.Privileges = New LuidAndAttributes() {luAttr}          If Not NativeMethods.PrivilegeCheck(hToken, pSet, result) Then             win32Err = Marshal.GetLastWin32Error             MessageBox.Show("PrivilegeCheck using original access-token failed.")             ' Ignore exception, to continue with the GetTokenInformation methodology.             ' Throw New Win32Exception(win32Err)          Else             MessageBox.Show(String.Format("{0} (original token) state is: {1}",                                               privilegeName, pSet.Privileges(0).Attributes.ToString()))              If Not NativeMethods.PrivilegeCheck(hTokenDup, pSet, result) Then                 win32Err = Marshal.GetLastWin32Error                 MessageBox.Show("PrivilegeCheck using impersonated access-token failed.")                 ' Ignore exception, to continue with the GetTokenInformation methodology.                 ' Throw New Win32Exception(win32Err)             Else                 MessageBox.Show(String.Format("{0} (impersonated token) state is: {1}",                                               privilegeName, pSet.Privileges(0).Attributes.ToString()))              End If          End If      Else         MessageBox.Show("DuplicateToken failed.")         ' Ignore exception, to continue with the GetTokenInformation methodology.         ' Throw New Win32Exception(win32Err)      End If      ' *********************************************************************     ' Trying GetTokenInformation methodology...     ' *********************************************************************      Dim tkp As New TokenPrivileges     Dim tkpHandle As IntPtr     Dim tkInfoLength As Integer = 0      tkpHandle = Marshal.AllocHGlobal(Marshal.SizeOf(tkpHandle))     Marshal.StructureToPtr(tkp, tkpHandle, False)      NativeMethods.GetTokenInformation(hToken, TokenInformationClass.TokenPrivileges, IntPtr.Zero, tkInfoLength, tkInfoLength)     win32Err = Marshal.GetLastWin32Error     ' If I understood, It is supposed to return 122,     ' so I should ignore that error code?:     If (win32Err <> 122) Then          MessageBox.Show("GetTokenInformation failed in the attempt to get the TokenPrivileges's size.")         Throw New Win32Exception(win32Err)      Else         If Not NativeMethods.GetTokenInformation(hToken, TokenInformationClass.TokenPrivileges, tkpHandle, tkInfoLength, tkInfoLength) Then             win32Err = Marshal.GetLastWin32Error             MessageBox.Show("GetTokenInformation failed in the attempt to get the TokenPrivileges.")             Throw New Win32Exception(win32Err)          Else             Dim privilegeAttr As TokenPrivilegeAttributes = tkp.Privileges(0).Attributes             MessageBox.Show(String.Format("{0} state is: {1}", privilegeName, privilegeAttr.ToString()))          End If      End If  Catch ex As Win32Exception     MessageBox.Show(ex.NativeErrorCode & " " & ex.Message)  Catch ex As Exception     MessageBox.Show(ex.Message)  Finally     If (hTokenDup <> IntPtr.Zero) Then         NativeMethods.CloseHandle(hTokenDup)     End If      If (hToken <> IntPtr.Zero) Then         NativeMethods.CloseHandle(hToken)     End If  End Try 

And these are the related winapi definitions (notice the commented MSDN urls for interest):

' http://msdn.microsoft.com/en-us/library/windows/desktop/aa379295%28v=vs.85%29.aspx <DllImport("advapi32.dll", SetLastError:=True)> Public Shared Function OpenProcessToken(ByVal processHandle As IntPtr,                                         ByVal desiredAccess As TokenAccess,                                         ByRef tokenHandle As IntPtr ) As <MarshalAs(UnmanagedType.Bool)> Boolean End Function  ' http://msdn.microsoft.com/en-us/library/windows/desktop/aa379180%28v=vs.85%29.aspx <DllImport("Advapi32.dll", SetLastError:=True, CharSet:=CharSet.Auto, BestFitMapping:=False, ThrowOnUnmappableChar:=True)> Public Shared Function LookupPrivilegeValue(ByVal lpSystemName As String,                                             ByVal lpName As String,                                             ByRef lpLuid As Luid ) As <MarshalAs(UnmanagedType.Bool)> Boolean End Function  ' http://msdn.microsoft.com/es-es/library/windows/desktop/aa375202%28v=vs.85%29.aspx <DllImport("Advapi32.dll", SetLastError:=True)> Public Shared Function AdjustTokenPrivileges(ByVal tokenHandle As IntPtr,                                              ByVal disableAllPrivileges As Boolean,                                              ByRef newState As TokenPrivileges,                                              ByVal bufferLength As Integer,                                              ByRef refPreviousState As TokenPrivileges,                                              ByRef refReturnLength As IntPtr ) As <MarshalAs(UnmanagedType.Bool)> Boolean End Function  ' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379304%28v=vs.85%29.aspx <DllImport("Advapi32.dll", SetLastError:=True)> Public Shared Function PrivilegeCheck(ByVal token As IntPtr,                           <[In], Out> ByRef privileges As PrivilegeSet,                                       ByRef refResult As Boolean ) As <MarshalAs(UnmanagedType.Bool)> Boolean End Function  ' https://msdn.microsoft.com/en-us/library/windows/desktop/aa446616%28v=vs.85%29.aspx <DllImport("advapi32.dll", SetLastError:=True)> Public Shared Function DuplicateToken(ByVal tokenHandle As IntPtr,                                       ByVal impersonationLevel As SecurityImpersonationLevel,                                       ByRef duplicateTokenHandle As IntPtr ) As <MarshalAs(UnmanagedType.Bool)> Boolean End Function  ' https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671%28v=vs.85%29.aspx <DllImport("Advapi32.dll", SetLastError:=True)> Public Shared Function GetTokenInformation(ByVal tokenHandle As IntPtr,                                            ByVal tokenInformationClass As TokenInformationClass,                                            ByVal tokenInformation As IntPtr,                                            ByVal tokenInformationLength As Integer,                                            ByRef refReturnLength As Integer ) As <MarshalAs(UnmanagedType.Bool)> Boolean End Function  ' http://msdn.microsoft.com/en-us/library/windows/desktop/aa374905%28v=vs.85%29.aspx <Flags> Public Enum TokenAccess As UInteger     ' THIS ENUMERATION IS PARTIALLY DEFINED.     ' **************************************     TokenAdjustPrivileges = &H20UI     TokenQuery = &H8UI End Enum  ' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379630%28v=vs.85%29.aspx <Flags> Public Enum TokenPrivilegeAttributes As UInteger     PrivilegeDisabled = &H0UI     PrivilegeEnabledByDefault = &H1UI     PrivilegeEnabled = &H2UI     PrivilegeRemoved = &H4UI     PrivilegeUsedForAccess = &H80000000UI End Enum  ' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379572(v=vs.85).aspx Public Enum SecurityImpersonationLevel As Integer     SecurityAnonymous = 0     SecurityIdentification = 1     SecurityImpersonation = 2     SecurityDelegation = 3 End Enum  ' http://msdn.microsoft.com/en-us/library/windows/desktop/aa379261%28v=vs.85%29.aspx <StructLayout(LayoutKind.Sequential)> Public Structure Luid     Public LowPart As UInteger     Public HighPart As Integer End Structure  ' http://msdn.microsoft.com/en-us/library/windows/desktop/aa379263%28v=vs.85%29.aspx <StructLayout(LayoutKind.Sequential)> Public Structure LuidAndAttributes     Public Luid As Luid     Public Attributes As TokenPrivilegeAttributes End Structure  ' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379630%28v=vs.85%29.aspx <StructLayout(LayoutKind.Sequential)> Public Structure TokenPrivileges     Public PrivilegeCount As UInteger      <MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)>     Public Privileges As LuidAndAttributes() End Structure  ' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379307%28v=vs.85%29.aspx <StructLayout(LayoutKind.Sequential)> Public Structure PrivilegeSet     Public PrivilegeCount As UInteger     Public Control As UInteger      <MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)>     Public Privileges As LuidAndAttributes() End Structure 

1 Answers

Answers 1

You are incorrectly using the unmanaged memory block tkpHandle and the structure tkp.

If I change your code like this, it works for me:

' ********************************************************************* ' Trying GetTokenInformation methodology... ' *********************************************************************  Dim tkp As New TokenPrivileges Dim tkpHandle As IntPtr = IntPtr.Zero ' <<< will be set later Dim tkInfoLength As Integer = 0  ' Here we call GetTokenInformation the first time to receive the length of the data it would like to store. NativeMethods.GetTokenInformation(hToken, TokenInformationClass.TokenPrivileges, IntPtr.Zero, tkInfoLength, tkInfoLength) win32Err = Marshal.GetLastWin32Error  ' Since the "current" length we pass is 0, we'll always get "error" 122, which is fine. We also get the required length returned. If (win32Err <> 122) Then     MessageBox.Show("GetTokenInformation failed in the attempt to get the TokenPrivileges's size.")     Throw New Win32Exception(win32Err) Else     Try         ' Here we allocate memory for receiving the actual data. By now, tkInfoLength contains the size of the memory block we need to allocate.         tkpHandle = Marshal.AllocHGlobal(tkInfoLength)          ' This time, we shouldn't get an error 122, because this time we already have set the correct buffer size. GetTokenInformation should now write the data into the memory block we just allocated.         If Not NativeMethods.GetTokenInformation(hToken, TokenInformationClass.TokenPrivileges, tkpHandle, tkInfoLength, tkInfoLength) Then             win32Err = Marshal.GetLastWin32Error             MessageBox.Show("GetTokenInformation failed in the attempt to get the TokenPrivileges.")             Throw New Win32Exception(win32Err)          Else             ' We will now ask PtrToStructure to read the raw data out of the memory block and convert it to a managed structure of type TokenPrivileges which we can use in our code. That's it!             tkp = Marshal.PtrToStructure(tkpHandle, GetType(TokenPrivileges))              Dim privilegeAttr As TokenPrivilegeAttributes = tkp.Privileges(0).Attributes             MessageBox.Show(String.Format("{0} state is: {1}", privilegeName, privilegeAttr.ToString()))          End If     Finally         ' Make sure the memory block is freed again (even when an error occured)         If tkpHandle Then Marshal.FreeHGlobal(tkpHandle)     End Try End If 

See my comments in the code. Basically, the flow is:

  • Call GetTokenInformation with a null pointer and a zero size in order to receive the actually required buffer size (so error 122 is normal here).
  • Allocate memory according to the buffer size we received.
  • Call GetTokenInformation again, with the real pointer to the memory block and the real size in order to receive the actual data.
  • Convert the raw data into a .NET structure using Marshal.PtrToStructure (not Marshal.StructureToPtr).
  • Free the memory block. (I added a Try/Finally block to make sure the memory is never leaked.)

Now the code successfully runs for me and shows a message box:

SeShutdownPrivilege state is: PrivilegeEnabled

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment