GUI-Based RunAsEx

来源:互联网 发布:php时间戳怎么读 编辑:程序博客网 时间:2024/05/06 13:26

Following table is a mapping Token-Elements and how to obtain them:

NameAPI to CallFunction Name in RunAsEx Source <Filename.cpp>User SIDLookupAccountNamePSID Name2SID(LPCTSTR pszUserName, LPCTSTR pszDomainName) <CoreCode.CPP>Group SIDsAllocateAndInitializeSid

NetUserGetLocalGroups

NetUserGetGroups

LookupAccountName

PTOKEN_GROUPS CreateTokenGroups(SID_AND_ATTRIBUTES* lpPSIDGroupsAttr)

PSID* QueryLocalGroupSIDs(LPCTSTR pszUserName)

PSID* QueryNetGroupSIDs(LPCTSTR pszUserName, LPCTSTR pszDomainName)

PSID GetEveryoneSID()

PSID GetAuthenticatedUsersSID()

PSID GetInteractiveSID()

...

<CoreCode.CPP>

Logon SID (inside Group SIDs)No Way To Create From Scratch

(but may be able to steal from elsewhere)

always in format S-1-5-5-0-XXXX

SYSTEM process (winlogon, lsass, etc...) does not have this

User processes and other system-level processes (such as svchost running under local_service and network_service) have this member.

A anonymous token token (with authenticationID = 998) does not have this member. Check http://www.develop.com/.../whatis_alogonsession.html

PrivilegesLookupPrivilegeValuePTOKEN_PRIVILEGES CreateTokenPriv(DWORD& dwPrivGranted, LPCTSTR* lpszPriv, BOOL bGrantEnableAll) <CoreCode.CPP>Token OwnerLookupAccountNamePSID Name2SID(LPCTSTR pszUserName, LPCTSTR pszDomainName) <CoreCode.CPP>Token Primary GroupAllocateAndInitializeSidSame as Group SIDsToken Default DACLAllocateACE, InitializeAcl, AddAce...Importable from caller; RunAsEx uses NULL DACL by defaultToken Source An 8-char string; RunAsEx uses "RunAsEx+"LUID TokenId,LARGE_INTEGER ExpirationTime,SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,LUID ModifiedIdAllocateLocallyUniqueIdPTOKEN_STATISTICS CreateTokenStatistics(LUID* lpTokenId, //Optional LUID* lpAuthenticationId, //Optional LARGE_INTEGER* lpExpirationTime, //OptionalTOKEN_TYPE* lpTokenType, //PrimarySECURITY_IMPERSONATION_LEVEL* lpImpersonationLevel, //Only When ImpersonteTokenDWORD* lpDynamicCharged, //OptionalDWORD* lpDynamicAvailable, //OptionalDWORD* lpGroupCount, //MandatoryDWORD* lpPrivilegeCount, //MandatoryLUID* lpModifiedId //Optional

<CoreCode.CPP>

Note: In the real world, all LUID members here set 0 as HighPart.

RunAsEx will assign random values to these members because they are not critically important.

TOKEN_TYPE TokenType N/A Only Meaningful When Impersonate TokenDWORD DynamicCharged Always 500DWORD DynamicAvailable Undecided; 420 by defaultDWORD GroupCount Checks count of Group SIDsDWORD PrivilegeCount Checks count of PrivilegesLUID AuthenticationIdThe first 1000 LUIDs are reserved (0x3E7 = 999).SYSTEM_LUID { 0x3E7, 0x0 }ANONYMOUS_LOGON_LUID { 0x3e6, 0x0 }LOCALSERVICE_LUID { 0x3e5, 0x0 }NETWORKSERVICE_LUID { 0x3e4, 0x0 }Caller pass. * Check RunAsEx desktop combo box and you will find Service-0x0-3e7$, Service-0x0-3e5$, and Service-0x0-3e4$. These are service logon sessions that get their names from this authenticate ID.

 

Note: This is different from Logon SID S-1-5-5-0-XXXX. I mean AuthenticationId.LowPart != XXXX.

It is the Logon Session LUID. Use the ZTokenMan check system, and you will see its authentication ID is 999. It is just the Logon Session associated with WinStat Service-0x0-3e7$. Use PrccessExplorer to check it!

All Tokens I met AuthenticationId.HighPart = 0.

RunAsEx uses 999 as the default when the caller does not pass into.

This member is closely related to the WinStat name.

Note: AuthenticationId is closely related to the WinStat Name when you pass the token to CreateProcessAsUser with an Empty Desktop Name "". Take this as an example: Configure a NT Service running as a user (instead of the default system); it should be put into a WinStation called "Service-0x0-XXX___FCKpd___0quot;. Go to ZTokenMan and have a look at its authenticationId; it should be the same value.
Note: The word Session is overused indeed. When I mention Logon Session SID, it is a SID embedded in Token. When I say Logon Session LUID, it is a LUID (64bits) indentifying a logon session and shared, usually, by some processes. When I Session ID, when in a WTS environment, it is a 0-based integer showing the current connection's order.

5. Implementation Explanation

Now, let's review what we need to do to RunAs and the corresponding API:

  1. Check If Caller is from Local Admin Group Member—SetTokenInformation, Enumerate Group SID to see if S-1-5-32-544 (alias S-1-5-32-544) there or not
  2. If need a user profile, load userenv.dll now—LoadLibrary, GetProcAddress
  3. Enable All Privileges needed—LookupPrivilegeValue, OpenProcessToken, AdjustTokenPrivileges
  4. Query Current Desktop Name—GetUserObjectInformation
  5. If the Target Desktop in Other WinStat, Yes—SetProcessWinStat (after closing All Windows, Hooks of Current Process)
  6. If the Desktop is non-existing—OpenWindowStation, CreateWindowStation, CreateDesktop
  7. If the user wants to use LogonUser, call it to get Token—LogonUser
  8. If the user has no password, Zw—ZwCreateToken
  9. If you want to launch in another session, change session ID—SetTokenInformation
  10. If the user wants to shoot in NT Service—CreateService, OpenSCManager...
  11. User Token Group SIDs to modify target desktop DACL, get rid of all deny ACE, add positive ACE—AllocateACE, InitializeAcl, AddAce...
  12. CreateProcessAsUser it
  13. If RunAs inside NT Service—DeleteService, ...

Although listing all the code (>3000 lines, compact with reasonable comments) seems daunting, I give the key code here so you can get something instantly without downloading, decompressing, and opening. Please note, you must handle exceptions if something wrong happened, especially in your NT service handler; otherwise, you will be stuck miserably (you can kill process, and modify the Registry manually if you like).

5.1. RunAsUser Function (showing handling of WinStat/Desktop affairs)

BOOL RunAsUser(  LPTSTR pszEXE, LPTSTR pszCmdLine, //in, Target Program and Command                                    //Line  LPTSTR pszDomainName, LPTSTR pszUserName, LPTSTR pszPassword, //in  LPTSTR pszDesktop, //in, Must Not be NULL, can be "NULL", "EMPTY",                     //"WinStat/Desktop"  BOOL bCreateTokenDirectly, //in, True: ZwCreateToken;                             //FALSE: LogonUser  DWORD dwSession,   //in, -1: No WTS; n (0-based): Target SessionID  BOOL bLoadProfile, //in, TRUE: Load User Profile  BOOL bCopyTokenPropFromCaller, //in, TRUE: All Token Information                                 //Use Caller Process's  BOOL bKeepPriv, //in, TRUE: Use Only Privileges User Holding;                  //FALSE: Set All Privileges  DWORD dwLogonType, //in, Same As LogonUser  DWORD dwLogonProvider //in, Reserved){  //LocalSystem no profile  if(pszUserName == NULL && bLoadProfile) return FALSE;  TCHAR szSrcWinStat[MAX_PATH];  TCHAR szSrcDesktop[MAX_PATH];  TCHAR szWinStat[MAX_PATH];  TCHAR szDesktop[MAX_PATH];  HWINSTA hSrcWinStat = ::GetProcessWindowStation();  HDESK   hSrcDesktop = ::GetThreadDesktop(::GetCurrentThreadId());  DWORD dwFakeLen;  //Get Target User Token --> his.her SID  HANDLE hToken   = NULL;  BOOL   fProcess = FALSE;  BOOL   fSuccess = FALSE;  PROCESS_INFORMATION pi = {NULL, NULL, 0, 0};  STARTUPINFO si;  PSECURITY_DESCRIPTOR pSD = NULL;  BOOL bRet = FALSE;  //save current win station and desktop to make return journey  //smoothly if ...  HWINSTA hwinstaOld = NULL;  HDESK hdeskOld     = NULL;  HWINSTA hwinstaNew = NULL;  HDESK hdeskNew     = NULL;  BOOL  bWinStatCreated = FALSE;  BOOL  bDeskCreated    = FALSE;  BOOL bSameWinStat = FALSE;  BOOL bSameDesktop = FALSE;  HANDLE hTokenSelf = NULL;  if(!OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY,     &hTokenSelf))    err;  PROFILEINFO profInfo = { sizeof(profInfo), 0, pszUserName };  void* pEnvBlock = NULL;  CreateEnvironmentBlock  _CreateEnvironmentBlock;  DestroyEnvironmentBlock _DestroyEnvironmentBlock;  LoadUserProfileW        _LoadUserProfileW;  UnloadUserProfile       _UnloadUserProfile;  HMODULE hEvnModule = NULL;  if(bLoadProfile && (hEvnModule = LoadLibrary(_T("userenv.dll")))     == NULL)    err;  if(hEvnModule)  {      //this unlikely fails      _CreateEnvironmentBlock =          reinterpret_cast<CreateEnvironmentBlock>         (GetProcAddress(hEvnModule, "CreateEnvironmentBlock"));      _DestroyEnvironmentBlock =         reinterpret_cast<DestroyEnvironmentBlock>         (GetProcAddress(hEvnModule, "DestroyEnvironmentBlock"));      _LoadUserProfileW = reinterpret_cast<LoadUserProfileW>         (GetProcAddress(hEvnModule, "LoadUserProfileW"));      _UnloadUserProfile = reinterpret_cast<UnloadUserProfile>         (GetProcAddress(hEvnModule, "UnloadUserProfile"));      if(!_CreateEnvironmentBlock || !_DestroyEnvironmentBlock ||         !_LoadUserProfileW || !_UnloadUserProfile)         err;  }  BOOL bNullDesktop  = FALSE;  BOOL bEmptyDesktop = FALSE;  //a long try_finally block  __try  {     if(!IsAdministrorMember()) err;     if(!::EnablePrivilege(L"SeTakeOwnershipPrivilege", TRUE))         err;     EnablePrivilege(L"SeTcbPrivilege", TRUE);     EnablePrivilege(L"SeChangeNotifyPrivilege", TRUE);     EnablePrivilege(L"SeIncreaseQuotaPrivilege", TRUE);     EnablePrivilege(L"SeAssignPrimaryTokenPrivilege", TRUE);     EnablePrivilege(L"SeCreateTokenPrivilege", TRUE);     //get caller thread's WinStat and Desktop,     //used to decide whether SetProcessWinStat is needed     bRet = GetUserObjectInformation(        hSrcWinStat,              // handle to object        UOI_NAME,                 // type of information to retrieve        (LPVOID)szSrcWinStat,     // information buffer        MAX_PATH * sizeof(TCHAR), // size of the buffer        &dwFakeLen                // receives required buffer size     );     if(!bRet || dwFakeLen > MAX_PATH * sizeof(TCHAR)) err;     bRet = GetUserObjectInformation(        hSrcDesktop,              // handle to object        UOI_NAME,                 // type of information to retrieve        (LPVOID)szSrcDesktop,     // information buffer        MAX_PATH * sizeof(TCHAR), // size of the buffer        &dwFakeLen                // receives required buffer size     );     if(!bRet || dwFakeLen > MAX_PATH * sizeof(TCHAR)) err;     if(pszDesktop == NULL) //|| _tcsstr(pszDesktop, _T("//"))                            //== NULL)     {        ::lstrcpy(szWinStat, szSrcWinStat);        ::lstrcpy(szDesktop, szSrcDesktop);     }     else if(::lstrcmpi(pszDesktop, _T("NULL")) == 0)     {        bNullDesktop = TRUE;     }     else if(::lstrcmpi(pszDesktop, _T("EMPTY")) == 0)     {        bEmptyDesktop = TRUE;     }     else     {        //check the integrity of the pszDesktop        TCHAR* pSlash1 = _tcsstr(pszDesktop, _T("//"));        TCHAR* pSlash2 = _tcsrchr(pszDesktop, TCHAR('//'));        if(pSlash1 != pSlash2) return FALSE;        TCHAR* psz = (TCHAR*)pszDesktop;        ::lstrcpyn(szWinStat, (LPCTSTR)pszDesktop, pSlash1                   - psz + 1);        ::lstrcpy(szDesktop, pSlash1 + 1);     }     if(!bNullDesktop && !bEmptyDesktop)     {        if(::lstrcmp(szWinStat, szSrcWinStat) == 0)   //same winstat        {           bSameWinStat = TRUE;           if(::lstrcmp(szDesktop, szSrcDesktop) == 0) //same desktop               bSameDesktop = TRUE;           else               bSameDesktop = FALSE;        }        else        {           bSameWinStat = FALSE;           bSameDesktop = FALSE;        }        if(!bSameDesktop || !bSameWinStat)        {           //for quick reversion           hwinstaOld = GetProcessWindowStation();           hdeskOld = GetThreadDesktop(GetCurrentThreadId());        }     }     if(!bNullDesktop && !bEmptyDesktop && !bSameWinStat)     {         //To Test The existing of a WinStat, you can         //1. EnumWindowStations and compare the string returned         //2. Call OpenWindowStation with WINSTA_ENUMERATE and test         //   the handle         //Way 2:         //Because the caller func is from a Admin Grp Member,         //this call is always OK unless WinStat not exists         ::SetLastError(ERROR_SUCCESS);         hwinstaNew = ::OpenWindowStation(szWinStat, FALSE,                                          WINSTA_ENUMERATE);         if(!hwinstaNew)         {            //winstat not existing            ::CloseWindowStation(hwinstaNew);            bWinStatCreated = TRUE;         }         else if(::GetLastError() == ERROR_ACCESS_DENIED)            err;         else         {            bWinStatCreated = FALSE;         }    }    if(pszUserName == NULL || ::lstrlen(pszUserName) == 0 )      //LogOn as LocalSystem    {         if(!bCreateTokenDirectly)         {            hToken = GetLSAToken();            if(hToken == NULL) err;         }         else         {            if(!CreateTokenDirectlyEx(hToken,                  bCopyTokenPropFromCaller,                  pszUserName, pszDomainName,                  "RunAsEx+", NULL, NULL, NULL, TRUE, bKeepPriv,                  FALSE, NULL, NULL, NULL, dwLogonType,                  dwLogonProvider) ||               hToken == NULL) err;         }    }    else    {         if(!bCreateTokenDirectly && !LogonUser(pszUserName,            pszDomainName,            pszPassword, dwLogonType, dwLogonProvider, &hToken))         {             err;         }         else if(bCreateTokenDirectly &&             !CreateTokenDirectlyEx(hToken,                   bCopyTokenPropFromCaller,                   pszUserName, pszDomainName,                   "RunAsEx+", NULL, NULL, NULL, TRUE, bKeepPriv,                   FALSE, NULL, NULL, NULL, dwLogonType,                   dwLogonProvider))         {             err;         }    }    //Set Token Seesion ID    if(dwSession != (DWORD)-1)    {        //need to set?        DWORD dwSelfSession;        if(ProcessIdToSessionId(::GetCurrentProcessId(),                                &dwSelfSession))        {            if(dwSelfSession != dwSession)            {               if(!SetTokenInformation(hToken, TokenSessionId,                  &dwSession, sizeof(DWORD)))               {                  if(GetLastError() == ERROR_ACCESS_DENIED)                  {                      //try again                      if (!ModifySecurity(hToken, TOKEN_DUPLICATE                            | TOKEN_ASSIGN_PRIMARY                            | TOKEN_QUERY | TOKEN_ADJUST_SESSIONID))                      {                          err;                      }                      if(!SetTokenInformation(hToken,                         TokenSessionId,                         &dwSession, sizeof(DWORD)))                          err;                  }               }            }         }    }    pSD = HeapAlloc(GetProcessHeap(), 0,                    SECURITY_DESCRIPTOR_MIN_LENGTH);    if(pSD == NULL) err;    // We now have an empty security descriptor    if (!InitializeSecurityDescriptor(pSD,        SECURITY_DESCRIPTOR_REVISION))       err;    if(!SetSecurityDescriptorDacl(pSD, TRUE, NULL, FALSE))       err;    // Then we point to our SD from a SECURITY_ATTRIBUTES structure    SECURITY_ATTRIBUTES sa = {0};    sa.nLength = sizeof(sa);    sa.lpSecurityDescriptor = pSD;    if(!bNullDesktop && !bEmptyDesktop && bWinStatCreated)    {        //Create a WinStat and naturally a new desktop        //First make it a NULL DACL :=)        hwinstaNew = CreateWindowStation(szWinStat, 0,                                         MAXIMUM_ALLOWED, &sa);        //using default security is good here since we are the owner        if(!hwinstaNew) __leave;        //We must SetProcessWindowStation when new desktop        //needs created on a different WinStat        if(!SetProcessWindowStation(hwinstaNew))        {           ::CloseWindowStation(hwinstaNew);           hwinstaNew = NULL;           err;        }        hdeskNew = ::CreateDesktop(szDesktop, NULL, NULL, 0,        //or DF_ALLOWOTHERACCOUNTHOOK             MAXIMUM_ALLOWED, &sa);        if(hdeskNew == NULL)        {           SetProcessWindowStation(hwinstaOld);           ::CloseWindowStation(hwinstaNew);           hwinstaNew = NULL;           err;        }        if(!AllowTokenFullAccessToObject(hTokenSelf, hwinstaNew,          SE_WINDOW_OBJECT, _T("WinStat")))   err;        if(!AllowTokenFullAccessToObject(hTokenSelf, hdeskNew,          SE_WINDOW_OBJECT, _T("Desktop"))) err;        if(!AllowTokenFullAccessToObject(hToken, hwinstaNew,           SE_WINDOW_OBJECT, _T("WinStat"))) err;        if(!AllowTokenFullAccessToObject(hToken, hdeskNew,           SE_WINDOW_OBJECT, _T("Desktop"))) err;   }   else if(!bNullDesktop && !bEmptyDesktop)//the WinStat exists   {        hwinstaNew = OpenWindowStation(szWinStat, FALSE,                                       READ_CONTROL | WRITE_DAC);        if(hwinstaNew == NULL) err;        //give self such rights --        //WINSTA_CREATEDESKTOP WINSTA_ENUMDESKTOPS        if(!bSameWinStat && !SetProcessWindowStation(hwinstaNew))        {           ::CloseWindowStation(hwinstaNew);           hwinstaNew = NULL;           err;        }        //if bSameWinStat --        //if not          -- SetProcessWindowStation called        //check the desk        //Does the desktop exists?        ::SetLastError(ERROR_SUCCESS);        hdeskNew = OpenDesktop(szDesktop, 0,           //DF_ALLOWOTHERACCOUNTHOOK,           FALSE, READ_CONTROL | WRITE_DAC);        if(hdeskNew == NULL)        {           if(::GetLastError() == ERROR_ACCESS_DENIED)              err;           else     //not existing           {              bDeskCreated = TRUE;              hdeskNew = ::CreateDesktop(szDesktop, NULL, NULL, 0,              //or DF_ALLOWOTHERACCOUNTHOOK                 MAXIMUM_ALLOWED, &sa);              if(hdeskNew == NULL)  err;           }        }        else           bDeskCreated = FALSE;        //modify DACL of the desk        if(!AllowTokenFullAccessToObject(hTokenSelf, hwinstaNew,         SE_WINDOW_OBJECT, _T("WinStat")))           err;        if(!AllowTokenFullAccessToObject(hTokenSelf, hdeskNew,          SE_WINDOW_OBJECT, _T("Desktop")))           err;        if(!AllowTokenFullAccessToObject(hToken, hwinstaNew,          SE_WINDOW_OBJECT, _T("WinStat")))           err;        if(!AllowTokenFullAccessToObject(hToken, hdeskNew,          SE_WINDOW_OBJECT, _T("Desktop")))           err;    }    //ready to launch    if(bLoadProfile)    {        // load the user profile        // PROFILEINFO profInfo = { sizeof(profInfo), 0,        //                          pszUserName };        if (!_LoadUserProfileW( hToken, &profInfo))  err;        // set up an environment block        //void* pEnvBlock = NULL;        if(!_CreateEnvironmentBlock( &pEnvBlock, hToken, FALSE)) err;    }    si.cb  = sizeof(si);    //Desktop or WinStat/Desktop    //MSDN Error Here! Note: to be 100% safe use the latter!!!    TCHAR szFullDesktop[MAX_PATH];    ::lstrcpy(szFullDesktop, szWinStat);    ::lstrcat(szFullDesktop, _T("//"));    ::lstrcat(szFullDesktop, szDesktop);    //si.lpDesktop   = bSameWinStat && bSameDesktop ? NULL :    //                 szFullDesktop;    if(!bNullDesktop && !bEmptyDesktop)    {       si.lpDesktop   = szFullDesktop;    }    else if(bNullDesktop)    {       si.lpDesktop   = NULL;    }    else    //if bEmptyDesktop    {       si.lpDesktop = _T("");    }    si.lpTitle     = NULL;    si.dwFlags     = 0;    si.cbReserved2 = 0;    si.lpReserved  = NULL;    si.lpReserved2 = NULL;    TCHAR szLocalCmdLine[2 * MAX_PATH];    ::SetLastError(ERROR_SUCCESS);    ::lstrcpy(szLocalCmdLine, _T("/""));    ::lstrcat(szLocalCmdLine, pszEXE);    ::lstrcat(szLocalCmdLine, _T("/""));    ::lstrcat(szLocalCmdLine, _T(" "));    if(pszCmdLine)       ::lstrcat(szLocalCmdLine, pszCmdLine);    //LPCTSTR-->LPTSTR    fProcess = CreateProcessAsUser(hToken, NULL,              (LPTSTR)szLocalCmdLine,       &sa, &sa,    //lpProcessAttributes, lpThreadAttributes       FALSE,       bLoadProfile ? CREATE_UNICODE_ENVIRONMENT : 0,       bLoadProfile ? pEnvBlock : NULL,       NULL, &si, &pi);    if(!fProcess)       err;    fSuccess = TRUE;  }  __finally  {    if(bLoadProfile && pEnvBlock)    // free the environment block    _DestroyEnvironmentBlock(pEnvBlock);    if(bLoadProfile && hToken && profInfo.hProfile)    // unload the user profile       _UnloadUserProfile( hToken, profInfo.hProfile );    if(pSD) HeapFree(GetProcessHeap(), 0, pSD);    if(hToken) CloseHandle(hToken);    if(hwinstaOld) ::SetProcessWindowStation(hwinstaOld);    if(hdeskOld) ::SetThreadDesktop(hdeskOld);    //Do NOT DO THAT!    //if(hdeskNew) ::CloseDesktop(hdeskNew);    //if(hwinstaNew) ::CloseWindowStation(hwinstaNew);    if (fProcess)    {       CloseHandle(pi.hProcess);       CloseHandle(pi.hThread);    }    if(hdeskOld)   ::CloseDesktop(hdeskOld);    if(hwinstaOld) ::CloseWindowStation(hwinstaOld);  }  return(fSuccess);}
原创粉丝点击