From: Ben on
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

"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
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

"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
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