From: Ben on 10 Dec 2009 16:56 hi, i have a c++ service i'm trying to get to work in Windows 7. it has to launch a popup dialog to the user. i use now a method that finds the Console Session ID, and runs CreateProcessAsUser. It works fine while logged on to console. But if I'm connected to my Win7 using RDP and the service starts the process, u can't see the UI. I see the Session ID it gets launched in is the same as my new RDP session ID, so i'm not sure why it fails. The code i use is below, if anyone has pointers I'd really appreciate it: BOOL CUtils::RunProcessOnMainDesktop(tstring csCmdLine, BOOL bWaitToEnd/*=TRUE*/, BOOL bHide/*=TRUE*/, HANDLE* phProcess/*=NULL*/) { HANDLE hTokenUser = NULL, hTokenDup = NULL; DWORD winlogonPid = 0; DWORD dwSessionId = pfWTSGetActiveConsoleSessionId(); //Find the winlogon process PROCESSENTRY32 procEntry; HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnap == INVALID_HANDLE_VALUE) { CUtils::Instance().AddToLog_Error(_T("Failed to launch process (CreateToolhelp32Snapshot failed): ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); return FALSE; } procEntry.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(hSnap, &procEntry)) { CUtils::Instance().AddToLog_Error(_T("Failed to launch process (Process32First failed): ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); return FALSE; } do { if (_tcsicmp(procEntry.szExeFile, _T("winlogon.exe")) == 0) { // We found a winlogon process... // make sure it's running in the console session DWORD winlogonSessId = 0; if (ProcessIdToSessionId(procEntry.th32ProcessID, &winlogonSessId) && winlogonSessId == dwSessionId) { winlogonPid = procEntry.th32ProcessID; break; } } } while (Process32Next(hSnap, &procEntry)); // close snapshot handle if(hSnap != INVALID_HANDLE_VALUE) CloseHandle(hSnap); TOKEN_PRIVILEGES tp; //LUID luid; HANDLE hWinlogonProcess = OpenProcess (MAXIMUM_ALLOWED,FALSE,winlogonPid); HANDLE hPToken; if(!::OpenProcessToken(hWinlogonProcess,TOKEN_ADJUST_PRIVILEGES| TOKEN_QUERY |TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY|TOKEN_ADJUST_SESSIONID |TOKEN_READ|TOKEN_WRITE,&hPToken)) { CUtils::Instance().AddToLog_Error(_T("Failed to launch process (OpenProcessToken failed): ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); return FALSE; } tp.PrivilegeCount =1; tp.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED; pfWTSQueryUserToken(dwSessionId, &hTokenUser); DuplicateTokenEx (hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,TokenPrimary,&hTokenDup); //Adjust Token privilege SetTokenInformation(hTokenDup,TokenSessionId,(void*) &dwSessionId,sizeof(DWORD)); if ( !AdjustTokenPrivileges(hTokenDup,FALSE,&tp,sizeof (TOKEN_PRIVILEGES),(PTOKEN_PRIVILEGES)NULL,NULL) ) { CUtils::Instance().AddToLog_Error(_T("Failed to launch process (AdjustTokenPrivileges failed): ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); return FALSE; } //launch process PROCESS_INFORMATION pi; STARTUPINFO si; ZeroMemory( &si, sizeof( STARTUPINFO ) ); si.cb = sizeof( STARTUPINFO ); si.lpDesktop = _T("winsta0\\default"); si.dwFlags = STARTF_USESHOWWINDOW; LPVOID pEnv = NULL; DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE; if(CreateEnvironmentBlock(&pEnv,hTokenDup,TRUE)) { dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; } if(bHide) si.wShowWindow = SW_HIDE; /* maybe even SW_HIDE */ else si.wShowWindow = SW_SHOW; /* maybe even SW_HIDE */ ZeroMemory( &pi,sizeof(pi)); TCHAR szCmdLine[1024]; _tcscpy(szCmdLine, csCmdLine.c_str()); BOOL bRet = CreateProcessAsUser( hTokenDup, NULL, szCmdLine, NULL, NULL, FALSE, dwCreationFlags, pEnv, NULL, &si, &pi ); if(!bRet) CUtils::Instance().AddToLog_Error(_T("Failed to launch process: ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText ()); if(bWaitToEnd && bRet) WaitForSingleObject( pi.hProcess, INFINITE ); if(bRet && phProcess) { //copy handle if requested DuplicateHandle(GetCurrentProcess(), pi.hProcess, GetCurrentProcess(), phProcess, DUPLICATE_SAME_ACCESS, FALSE, DUPLICATE_SAME_ACCESS); //*phProcess = pi.hProcess; } //Perform All the Close Handles tasks CloseHandle(hWinlogonProcess); CloseHandle(hPToken); CloseHandle(hTokenDup); CloseHandle(hTokenUser); DestroyEnvironmentBlock(pEnv); return bRet; }
From: Remy Lebeau on 10 Dec 2009 20:17 "Ben" <benm5678(a)gmail.com> wrote in message news:17f7acea-e0e1-43e9-a5c7-457a46a1ce75(a)d10g2000yqh.googlegroups.com... > //Find the winlogon process Why are you adjusting privileges on, or even looking for, the winlogon process within the user's session? You don't need to do that. WTSQueryUserToken() already gives you everything you need. Just make sure your calling process has the SE_TCB_NAME privilege first. Try this code, which I adapted from code I use in my own service: BOOL CUtils::RunProcessOnMainDesktop(tstring csCmdLine, BOOL bWaitToEnd/*=TRUE*/, BOOL bHide/*=TRUE*/, HANDLE* phProcess/*=NULL*/) { DWORD dwSessionId = pfWTSGetActiveConsoleSessionId(); if( dwSessionId == 0xFFFFFFFF ) { if( GetLastError() != ERROR_CALL_NOT_IMPLEMENTED ) { CUtils::Instance().AddToLog_Error(_T("Failed to retreive Active Console Session ID (WTSGetActiveConsoleSessionId failed): ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); } return FALSE; // no active session at this time } // must have SE_TCB_NAME privileges for WTSQueryUserToken() to work... HANDLE hProcessToken = NULL; TOKEN_PRIVILEGES TokenPriv, OldTokenPriv; DWORD OldSize = 0; BOOL bAdjusted = FALSE; if( OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken) ) { if( LookupPrivilegeValue(NULL, SE_TCB_NAME, &TokenPriv.Privileges[0].Luid) ) { TokenPriv.PrivilegeCount = 1; TokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; bAdjusted = AdjustTokenPrivileges(hProcessToken, FALSE, &TokenPriv, sizeof(TokenPriv), &OldTokenPriv, &OldSize); } } if( !bAdjusted ) { CUtils::Instance().AddToLog_Warning(_T("Failed to adjust adjust SE_TCB_NAME Token Privilege: ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); } // try it anyway, just in case it happens to work... HANDLE hUserToken = NULL; HANDLE hToken = NULL; if( !pfWTSQueryUserToken(dwSessionId, &hToken) ) { if( GetLastError() == ERROR_NO_TOKEN ) { CUtils::Instance().AddToLog_Warning(_T("No User Token for the Active Console Session: ") + csCmdLine); } else { CUtils::Instance().AddToLog_Error(_T("Failed to retreive User Token for the Active Console Session (WTSQueryUserToken failed): ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); } } else { if( !DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hUserToken) ) { CUtils::Instance().AddToLog_Error(_T("Failed to retreive Primary Token from User Token on the Active Console Session: ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); } CloseHandle(hToken); } BOOL bRet = FALSe; if( hUserToken != NULL ) { LPVOID pEnvironment = NULL; if( !CreateEnvironmentBlock(&pEnvironment, hUserToken, TRUE) ) { CUtils::Instance().AddToLog_Error(_T("Failed to create Environment block for the Active Console Session (CreateEnvironmentBlock failed): ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); } else { STARTUPINFO si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.lpDesktop = _T("winSta0\\Default"); si.dwFlags = STARTF_USESHOWWINDOW; if( bHide ) si.wShowWindow = SW_HIDE; else si.wShowWindow = SW_SHOW; PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi)); TCHAR szCmdLine[1024]; _tcscpy(szCmdLine, csCmdLine.c_str()); bRet = CreateProcessAsUser(hUserToken, NULL, szCmdLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, pEnv, NULL, &si, &pi); if( !bRet ) { CUtils::Instance().AddToLog_Error(_T("Failed to launch process (CreateProcessAsUser failed):") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); } else { if( bWaitToEnd ) WaitForSingleObject(pi.hProcess, INFINITE); if( phProcess ) { //copy handle if requested DuplicateHandle(GetCurrentProcess(), pi.hProcess, GetCurrentProcess(), phProcess, DUPLICATE_SAME_ACCESS, FALSE, DUPLICATE_SAME_ACCESS); } CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } DestroyEnvironmentBlock(pEnv); } } if( hProcessToken != NULL ) { if( bAdjusted ) AdjustTokenPrivileges(hProcessToken, FALSE, &OldTokenPriv, sizeof(OldTokenPriv), NULL, NULL); CloseHandle(hProcessToken); } return bRet; } -- Remy Lebeau (TeamB)
From: Ben on 11 Dec 2009 00:11 On Dec 10, 7:17 pm, "Remy Lebeau" <no.s...(a)no.spam.com> wrote: > "Ben" <benm5...(a)gmail.com> wrote in messagenews:17f7acea-e0e1-43e9-a5c7-457a46a1ce75(a)d10g2000yqh.googlegroups.com... > > //Find the winlogon process > > Why are you adjusting privileges on, or even looking for, the winlogon process within the user's session? You don't need to do that. WTSQueryUserToken() already gives you everything you need. Just make sure your calling process has the SE_TCB_NAME privilege first. > > Try this code, which I adapted from code I use in my own service: > > BOOL CUtils::RunProcessOnMainDesktop(tstring csCmdLine, BOOL bWaitToEnd/*=TRUE*/, BOOL bHide/*=TRUE*/, HANDLE* phProcess/*=NULL*/) > { > DWORD dwSessionId = pfWTSGetActiveConsoleSessionId(); > if( dwSessionId == 0xFFFFFFFF ) > { > if( GetLastError() != ERROR_CALL_NOT_IMPLEMENTED ) > { > CUtils::Instance().AddToLog_Error(_T("Failed to retreive Active Console Session ID (WTSGetActiveConsoleSessionId failed): ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); > } > > return FALSE; // no active session at this time > } > > // must have SE_TCB_NAME privileges for WTSQueryUserToken() to work... > > HANDLE hProcessToken = NULL; > TOKEN_PRIVILEGES TokenPriv, OldTokenPriv; > DWORD OldSize = 0; > BOOL bAdjusted = FALSE; > > if( OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken) ) > { > if( LookupPrivilegeValue(NULL, SE_TCB_NAME, &TokenPriv.Privileges[0].Luid) ) > { > TokenPriv.PrivilegeCount = 1; > TokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; > bAdjusted = AdjustTokenPrivileges(hProcessToken, FALSE, &TokenPriv, sizeof(TokenPriv), &OldTokenPriv, &OldSize); > } > } > > if( !bAdjusted ) > { > CUtils::Instance().AddToLog_Warning(_T("Failed to adjust adjust SE_TCB_NAME Token Privilege: ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); > } > > // try it anyway, just in case it happens to work... > > HANDLE hUserToken = NULL; > > HANDLE hToken = NULL; > if( !pfWTSQueryUserToken(dwSessionId, &hToken) ) > { > if( GetLastError() == ERROR_NO_TOKEN ) > { > CUtils::Instance().AddToLog_Warning(_T("No User Token for the Active Console Session: ") + csCmdLine); > } > else > { > CUtils::Instance().AddToLog_Error(_T("Failed to retreive User Token for the Active Console Session (WTSQueryUserToken failed): ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); > } > } > else > { > if( !DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hUserToken) ) > { > CUtils::Instance().AddToLog_Error(_T("Failed to retreive Primary Token from User Token on the Active Console Session: ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); > } > > CloseHandle(hToken); > } > > BOOL bRet = FALSe; > > if( hUserToken != NULL ) > { > LPVOID pEnvironment = NULL; > if( !CreateEnvironmentBlock(&pEnvironment, hUserToken, TRUE) ) > { > CUtils::Instance().AddToLog_Error(_T("Failed to create Environment block for the Active Console Session (CreateEnvironmentBlock failed): ") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); > } > else > { > STARTUPINFO si; > ZeroMemory(&si, sizeof(si)); > si.cb = sizeof(si); > si.lpDesktop = _T("winSta0\\Default"); > si.dwFlags = STARTF_USESHOWWINDOW; > > if( bHide ) > si.wShowWindow = SW_HIDE; > else > si.wShowWindow = SW_SHOW; > > PROCESS_INFORMATION pi; > ZeroMemory(&pi, sizeof(pi)); > > TCHAR szCmdLine[1024]; > _tcscpy(szCmdLine, csCmdLine.c_str()); > > bRet = CreateProcessAsUser(hUserToken, NULL, szCmdLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, pEnv, NULL, &si, &pi); > if( !bRet ) > { > CUtils::Instance().AddToLog_Error(_T("Failed to launch process (CreateProcessAsUser failed):") + csCmdLine + _T(", EC: ") + CUtils::Instance().GetLastErrorText()); > } > else > { > if( bWaitToEnd ) > WaitForSingleObject(pi.hProcess, INFINITE); > > if( phProcess ) > { > //copy handle if requested > DuplicateHandle(GetCurrentProcess(), pi.hProcess, GetCurrentProcess(), phProcess, DUPLICATE_SAME_ACCESS, FALSE, DUPLICATE_SAME_ACCESS); > } > > CloseHandle(pi.hThread); > CloseHandle(pi.hProcess); > } > > DestroyEnvironmentBlock(pEnv); > } > } > > if( hProcessToken != NULL ) > { > if( bAdjusted ) > AdjustTokenPrivileges(hProcessToken, FALSE, &OldTokenPriv, sizeof(OldTokenPriv), NULL, NULL); > > CloseHandle(hProcessToken); > } > > return bRet; > > } > > -- > Remy Lebeau (TeamB) THANKS A LOT for that code - it's very helpful!:) I tried swapping to your version, but it won't launch the process. It's failing on this error: "No User Token for the Active Console Session: 4, while launching C: \Windows\temp\sims.exe -servicemonitor" ....maybe u know why? (I added the session id to the error)... if i look at session #4, it just has UserInit.exe, winlogon.exe, csrss.exe, and rest of my processes are in session #2. I'm logged on via RDP when i try, but it's not a fresh logon, it's redirecting an existing session. That makes me wonder testing with a fresh RDP logon...however i'm hoping i can make it work in either circumstance. Thanks!! Ben.
From: Remy Lebeau on 11 Dec 2009 03:50 "Ben" <benm5678(a)gmail.com> wrote in message news:4a4a000d-e830-4064-a53e-90a41df3bbf5(a)9g2000yqa.googlegroups.com... > if i look at session #4, it just has UserInit.exe, winlogon.exe, > csrss.exe, > and rest of my processes are in session #2. WTSQueryUserToken() returning ERROR_NO_TOKEN for session 4 would typically mean that no user is actually logged into the session 4 desktop. However, after some research (for instance, http://social.msdn.microsoft.com/Forums/en-US/windowssecurity/thread/31bfa13d-982b-4b1a-bff3-2761ade5214f), it seems that WTSQueryUserToken() does not always work correctly with RDP sessions. I don't ever work with RDP, so I don't know all of the issues with it. -- Remy Lebeau (TeamB)
From: Stefan Kuhr on 13 Dec 2009 13:39
Hello m, On 12/13/2009 6:47 PM, m wrote: > No - launching a separate process is a way to do it that is immune to > shatter attacks - it does not mean that needing to show UI from a > service is okay! > Can you please elaborate what Remy's approach has to do with shatter attacks? Shatter attacks are privilege escalations in a privileged UI component of a service style logon session that has access to an interactive desktop. But Remy's approach is to fire a child process in the interactive user's logon session, by doing some sort of system blessed token stealing using the WTSQueryUserToken API (which IIRC only SYSTEM can call successfully anyway). So how can this child process do a privilige escalation? -- S |