Impersonating the LocalSystem account

Introduction

If you just want to execute a command under the LocalSystem account, you can use psexec with -s parameter, but the motivation for writing this code was for decryption of Wireless passwords which are encrypted using CryptProtectData API under the LocalSystem account.

The following are some ways to execute code under context of LocalSystem user.

  1. Create a new service using the Service Control Manager (SCM) API.
  2. Inject code into a LocalSystem process using CreateRemoteThread API.
  3. Create new process with LocalSystem privileges using CreateProcessAsUser API.
  4. Impersonate LocalSystem using existing LocalSystem process token

The 4th option is what we’ll discuss here since it’s much easier to use than the 1st one and certainly safer and more reliable than the 2nd. The 3rd is fine if you want to create a new process as LocalSystem but if you only need to perform the task temporarily, impersonation is a better option.

Elevated Privilege

Before doing anything else, we should ensure our process is elevated.
If this fails, the rest of our code won’t work.

/**
 *
 *  Determines if process token is elevated
 *  Returns TRUE or FALSE
 *
 */
BOOL isElevated(VOID) {
  HANDLE          hToken;
  BOOL            bResult = FALSE;
  TOKEN_ELEVATION te;
  DWORD           dwSize;
    
  if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
    if (GetTokenInformation(hToken, TokenElevation, &te, 
        sizeof(TOKEN_ELEVATION), &dwSize)) {
      bResult = te.TokenIsElevated != 0;
    }
    CloseHandle(hToken);
  }
  return bResult;
}

Debug Privilege

To obtain a handle for a LocalSystem process, we need to enable the debug privilege of our process token which is disabled by default. Again, this requires Administrator privileges.

/**
 *
 *  Enables or disables a named privilege in token
 *  Returns TRUE or FALSE
 *
 */
BOOL SetPrivilege(wchar_t szPrivilege[], BOOL bEnable) {
  HANDLE           hToken;
  BOOL             bResult;
  LUID             luid;
  TOKEN_PRIVILEGES tp;
  
  bResult = OpenProcessToken(GetCurrentProcess(), 
    TOKEN_ADJUST_PRIVILEGES, &hToken);
  
  if (bResult) {    
    bResult = LookupPrivilegeValue(NULL, szPrivilege, &luid);
    if (bResult) {
      tp.PrivilegeCount           = 1;
      tp.Privileges[0].Luid       = luid;
      tp.Privileges[0].Attributes = (bEnable) ? SE_PRIVILEGE_ENABLED : 0;

      bResult = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
    }
    CloseHandle(hToken);
  }
  return bResult;
}

Obtaining process id of process name

If we’re an elevated process and can enable SeDebugPrivilege in our process token, we then obtain the process id of a known LocalSystem process. The Local Security Account Subsystem (LSASS) exists on all windows since the release of NT and is perfect.

/**
 *
 *  Obtain process id of process name
 *
 *  Returns process id or zero
 *
 */
DWORD GetProcessId(wchar_t szName[]) {
  DWORD          dwId = 0;
  HANDLE         hSnap;
  BOOL           bResult;
  PROCESSENTRY32 pe32;
  
  hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  
  if (hSnap != INVALID_HANDLE_VALUE) {
    pe32.dwSize = sizeof(PROCESSENTRY32);
    
    bResult = Process32First(hSnap, &pe32);
    while (bResult) {
      if (lstrcmpi(pe32.szExeFile, szName) == 0) {
        dwId = pe32.th32ProcessID;
        break;
      }
      bResult = Process32Next(hSnap, &pe32);
    }
    CloseHandle(hSnap);
  }
  return dwId;
}

Impersonation of token

All we need now is to open the process, open the process token for impersonation and call ImpersonateLoggedOnUser API.

BOOL ImpersonateSystem(VOID) {
  BOOL   bImpersonating = FALSE;
  HANDLE hToken, hProcess;
  // get id of a LocalSystem process
  DWORD  dwId = GetProcessId(L"lsass.exe");
  
  if (dwId != 0) {
    // enable debug privilege
    if (SetPrivilege(SE_DEBUG_NAME, TRUE)) {
      // attempt to open process
      hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwId);
      if (hProcess != NULL) {
        // attempt to open process token
        if (OpenProcessToken(hProcess, 
            TOKEN_IMPERSONATE | TOKEN_DUPLICATE | TOKEN_QUERY, &hToken)) {
          // attempt to impersonate LocalSystem
          bImpersonating = ImpersonateLoggedOnUser(hToken);
          if (!bImpersonating) {
            Error(L"ImpersonateLoggedOnUser failed : ", GetLastError());
          }
          CloseHandle(hToken);
        } else {
          Error(L"OpenProcessToken failed : ", GetLastError());
        }
        CloseHandle(hProcess);
      } else {
        Error(L"OpenProcess(\"lsass.exe\") failed : ", GetLastError());
      }
    } else {
      Error(L"SetPrivilege(SE_DEBUG_NAME, TRUE) failed : ", GetLastError());
    }
  } else {
    Error(L"GetProcessId(\"lsass.exe\") failed : ", GetLastError());
  }
  return bImpersonating;
}

Once we’ve executed CryptUnprotectData API under the context of LocalSystem privileges we can call RevertToSelf API or just exit the thread/application.

Advertisements
This entry was posted in programming, security, windows and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s