362 lines
20 KiB
C++
362 lines
20 KiB
C++
#include "stdafx.h"
|
||
#include "resource.h"
|
||
#include "Win7Elevate_Utils.h"
|
||
#include "Win7Elevate_Inject.h"
|
||
|
||
#include <iostream>
|
||
#include <sstream>
|
||
#include <algorithm>
|
||
|
||
#include ".\..\Redirector.h"
|
||
#include ".\..\CMMN.h"
|
||
|
||
//
|
||
// By Pavlov P.S. (PavPS)
|
||
//
|
||
|
||
void PrintUsage()
|
||
{
|
||
std::cout << "Incorrect input. Please find samples below. " << std::endl;
|
||
std::cout << "Note, 'elevate stuff' will be executed in the elevated shell as 'cmd.exe stuff' " << std::endl;
|
||
std::cout << "\televate /c <ANY COMMAND SEQUENCE THAT IS ALLOWED BY CMD.EXE SHELL>" << std::endl;
|
||
std::cout << "\televate /c <command> [arg1] [arg2] .. [argn]" << std::endl;
|
||
std::cout << "\televate --pid 1234 /c <command> [arg1] [arg2] .. [argn]" << std::endl;
|
||
std::cout << "\televate /c c:\\path\\foo.exe [arg1] [arg2] .. [argn]" << std::endl;
|
||
std::cout << "\televate --pid 1234 /c c:\\path\\foo.exe [arg1] [arg2] .. [argn]" << std::endl;
|
||
}
|
||
|
||
HANDLE PipeIn = NULL;
|
||
OVERLAPPED PipeInO;
|
||
|
||
HANDLE PipeOut = NULL;
|
||
OVERLAPPED PipeOutO;
|
||
|
||
HANDLE PipeErr = NULL;
|
||
OVERLAPPED PipeErrO;
|
||
|
||
//
|
||
// Initializes named pipes that will be used for connection with TIOR
|
||
//
|
||
bool SetupNamedPipe()
|
||
{
|
||
PipeInO.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
|
||
PipeIn = CreateNamedPipe(
|
||
STDIn_PIPE,
|
||
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED,
|
||
PIPE_TYPE_BYTE | PIPE_WAIT,
|
||
PIPE_UNLIMITED_INSTANCES,
|
||
0, 0,
|
||
NMPWAIT_USE_DEFAULT_WAIT,
|
||
NULL );
|
||
|
||
ConnectNamedPipe( PipeIn, &PipeInO );
|
||
|
||
PipeOutO.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
|
||
PipeOut = CreateNamedPipe(
|
||
STDOut_PIPE,
|
||
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED,
|
||
PIPE_TYPE_BYTE | PIPE_WAIT,
|
||
PIPE_UNLIMITED_INSTANCES,
|
||
0, 0,
|
||
NMPWAIT_USE_DEFAULT_WAIT,
|
||
NULL );
|
||
|
||
ConnectNamedPipe( PipeOut, &PipeOutO );
|
||
|
||
PipeErrO.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
|
||
PipeErr = CreateNamedPipe(
|
||
STDErr_PIPE,
|
||
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED,
|
||
PIPE_TYPE_BYTE | PIPE_WAIT,
|
||
PIPE_UNLIMITED_INSTANCES,
|
||
0, 0,
|
||
NMPWAIT_USE_DEFAULT_WAIT,
|
||
NULL );
|
||
|
||
ConnectNamedPipe( PipeErr, &PipeErrO );
|
||
|
||
return true;
|
||
}
|
||
|
||
//
|
||
// Initiates data pumping.
|
||
//
|
||
DWORD __stdcall Redirector()
|
||
{
|
||
if ( !PipeIn )
|
||
return -1;
|
||
|
||
if ( PipeInO.hEvent )
|
||
WaitForSingleObject( PipeInO.hEvent, -1000 );
|
||
if ( PipeOutO.hEvent )
|
||
WaitForSingleObject( PipeOutO.hEvent, -1000 );
|
||
if ( PipeErrO.hEvent )
|
||
WaitForSingleObject( PipeErrO.hEvent, -1000 );
|
||
|
||
TRedirectorPair in = {0};
|
||
in.Source = GetStdHandle(STD_INPUT_HANDLE);
|
||
in.Destination = PipeIn;
|
||
in.Linux = true;
|
||
in.Name.assign(TEXT("w7e-in"));
|
||
in.Thread= CreateThread( NULL, 0, Redirector, &in, 0, NULL);
|
||
|
||
TRedirectorPair out = {0};
|
||
out.Destination = GetStdHandle(STD_OUTPUT_HANDLE);
|
||
out.Source = PipeOut;
|
||
out.Name.assign(TEXT("w7e-out"));
|
||
out.Thread= CreateThread( NULL, 0, Redirector, &out, 0, NULL);
|
||
|
||
TRedirectorPair err = {0};
|
||
err.Destination = GetStdHandle(STD_ERROR_HANDLE);
|
||
err.Source = PipeErr;
|
||
err.Name.assign(TEXT("w7e-err"));
|
||
err.Thread= CreateThread( NULL, 0, Redirector, &err, 0, NULL);
|
||
|
||
HANDLE waiters[3] = { in.Thread, out.Thread, err.Thread };
|
||
WaitForMultipleObjects( 3, waiters, FALSE, INFINITE );
|
||
|
||
return 0;
|
||
}
|
||
|
||
bool IsDefaultProcess ( std::pair<DWORD, std::wstring> pair ) {
|
||
return lstrcmpi( pair.second.c_str(), TEXT("explorer.exe") ) == 0;
|
||
}
|
||
|
||
//
|
||
// To avoid some problems with deadlocked processes we need to find way how to run program
|
||
// once more. Since program uses named papes, it can not be started twice (in current realization).
|
||
// So, if instance of this process already exists, we need to kill it. Regular exe, started from the
|
||
// user's account has no access to kill existing app.
|
||
// Here i use named event to listen for and perform suicide. So, i just need to set this event (if one)
|
||
// and already existsing app will kill itself.
|
||
//
|
||
DWORD WINAPI Suicide( LPVOID Parameter )
|
||
{
|
||
CLogger::LogLine(TEXT("Waiting for suicide..."));
|
||
WaitForSingleObject( reinterpret_cast<HANDLE>( Parameter ), INFINITE );
|
||
SetEvent( reinterpret_cast<HANDLE>( Parameter ) );
|
||
CLogger::LogLine(TEXT("Suicide..."));
|
||
ExitProcess( EXIT_FAILURE );
|
||
|
||
return EXIT_SUCCESS;
|
||
}
|
||
|
||
int _tmain(int argc, _TCHAR* argv[])
|
||
{
|
||
CLogger::Reset();
|
||
CLogger::LogLine(TEXT("Started"));
|
||
|
||
//
|
||
// Looking for suicide.
|
||
//
|
||
HANDLE obj = CreateEvent( NULL, FALSE, TRUE, TEXT("ws7Suicide") );
|
||
if ( !obj )
|
||
{
|
||
CLogger::LogLine(TEXT("Unable to create suicide object"));
|
||
ExitProcess( EXIT_FAILURE );
|
||
}
|
||
|
||
//
|
||
// If we see that suicide event is in reset state, we just pulce one and wait for
|
||
// it's owner to die. When its done, we acuire this event object and also starting listening for
|
||
// any signals of this object.
|
||
//
|
||
do
|
||
{
|
||
DWORD rv = WaitForSingleObject( obj, 100 );
|
||
if ( rv == WAIT_OBJECT_0 ) break;
|
||
|
||
if ( rv != WAIT_TIMEOUT )
|
||
{
|
||
CLogger::LogLine(TEXT("Suicide wait error"));
|
||
ExitProcess( EXIT_FAILURE );
|
||
}
|
||
|
||
CLogger::LogLine(TEXT("Somebody alive. Pulse."));
|
||
PulseEvent( obj );
|
||
Sleep(1000); // wee need to wait;
|
||
|
||
}while( true );
|
||
|
||
HANDLE hSuicide = CreateThread( NULL, 0, Suicide, obj, 0, NULL );
|
||
if ( !hSuicide )
|
||
{
|
||
CLogger::LogLine(TEXT("Immortals are not allowed"));
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
do
|
||
{
|
||
int pass_through_index = 1;
|
||
if ( argc <= pass_through_index )
|
||
{
|
||
std::cout << "Too few arguments" << std::endl;
|
||
break;
|
||
}
|
||
|
||
|
||
DWORD pid = 0;
|
||
if ( lstrcmpi( argv[1], TEXT("--pid") ) == 0 )
|
||
{
|
||
pass_through_index = 3;
|
||
if ( argc <= pass_through_index )
|
||
{
|
||
std::cout << "Too few arguments" << std::endl;
|
||
break;
|
||
}
|
||
|
||
std::wistringstream pid_stream( argv[2] );
|
||
if ( ! ( pid_stream >> pid ) )
|
||
{
|
||
std::cout << "Invalid pid" << std::endl;
|
||
pid = 0;
|
||
}
|
||
}
|
||
|
||
if ( ! pid )
|
||
{
|
||
std::map< DWORD, std::wstring > procs;
|
||
if (!W7EUtils::GetProcessList(GetConsoleWindow(), procs))
|
||
{
|
||
std::cout << "Unable to obtain list of processes" << std::endl;
|
||
break;
|
||
}
|
||
|
||
std::map< DWORD, std::wstring >::const_iterator iter = std::find_if( procs.begin(), procs.end(), IsDefaultProcess );
|
||
if (iter == procs.end())
|
||
{
|
||
std::cout << "Unable to find default process" << std::endl;
|
||
break;
|
||
}
|
||
|
||
pid = (*iter).first;
|
||
}
|
||
|
||
TOKEN_ELEVATION_TYPE g_tet = TokenElevationTypeDefault;
|
||
if (!W7EUtils::GetElevationType(&g_tet))
|
||
{
|
||
_tprintf(_T("GetElevationType failed"));
|
||
break;
|
||
}
|
||
|
||
switch(g_tet)
|
||
{
|
||
default:
|
||
case TokenElevationTypeDefault:
|
||
CLogger::LogLine(_T("<< UNKNOWN elevation level. >>\n"));
|
||
break;
|
||
case TokenElevationTypeFull:
|
||
CLogger::LogLine(_T("*** Since the program is already elevated the tests below are fairly meaningless. Re-run it without elevation. ***\n"));
|
||
break;
|
||
case TokenElevationTypeLimited:
|
||
CLogger::LogLine(_T("This program attempts to bypass Windows 7's default UAC settings to run the specified command with silent elevation.\n"));
|
||
break;
|
||
}
|
||
|
||
W7EUtils::CTempResource dllResource(NULL, IDD_EMBEDDED_DLL);
|
||
std::wstring strOurDllPath;
|
||
if (!dllResource.GetFilePath(strOurDllPath))
|
||
{
|
||
//MessageBox(GetConsoleWindow(), L"Error extracting dll resource.", L"W7Elevate", MB_OK | MB_ICONERROR);
|
||
CLogger::LogLine(TEXT("Error extracting dll resource."));
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Extraction TIOR.exe from resources and saves exe in the folder where current application
|
||
// exists.
|
||
//
|
||
W7EUtils::CTempResource TIORResource(NULL, IDD_EMBEDDED_TIOR);
|
||
std::wstring strOurTIORPath;
|
||
std::wstring tior;
|
||
bool tior_succeed = false;
|
||
if (TIORResource.GetFilePath(strOurTIORPath))
|
||
{
|
||
CLogger::LogLine(TEXT("TIOR extracted"));
|
||
|
||
TCHAR me_buff[MAX_PATH];
|
||
DWORD me_count = GetModuleFileName( NULL, me_buff, MAX_PATH );
|
||
if ( me_count )
|
||
{
|
||
TCHAR *me_tail = me_buff + me_count - 1;
|
||
for( ; me_tail > me_buff; me_tail-- )
|
||
if ( *me_tail == '\\' )
|
||
{
|
||
me_tail++;
|
||
*me_tail = 0;
|
||
break;
|
||
}
|
||
|
||
tior.assign(me_buff);
|
||
tior.append( TEXT("tior.exe") );
|
||
|
||
if ( CopyFile( strOurTIORPath.c_str(), tior.c_str(), FALSE ) )
|
||
{
|
||
CLogger::LogLine(TEXT("TIOR copied"));
|
||
tior_succeed = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( tior_succeed )
|
||
{
|
||
tior_succeed = false;
|
||
|
||
CInterprocessStorage *tior_storage = CInterprocessStorage::Create( TEXT("w7e_TIORPath") );
|
||
if ( tior_storage )
|
||
{
|
||
CLogger::LogLine(TEXT("TIOR path set"));
|
||
tior_storage->SetString( tior );
|
||
tior_succeed = true;
|
||
}
|
||
}
|
||
|
||
if ( !tior_succeed )
|
||
{
|
||
//MessageBox(GetConsoleWindow(), L"Error extracting tior resource.", L"W7Elevate", MB_OK | MB_ICONERROR);
|
||
CLogger::LogLine(L"Error extracting tior resource.");
|
||
break;
|
||
}
|
||
|
||
std::wstring args;
|
||
for ( int i = pass_through_index; i < argc; i++ )
|
||
{
|
||
bool q = wcsstr(argv[i], TEXT(" ")) || wcsstr(argv[i], TEXT("\t"));
|
||
|
||
if ( q ) args.append( TEXT("\"") );
|
||
args.append( argv[i] );
|
||
if ( q ) args.append( TEXT("\"") );
|
||
args.append( TEXT(" ") );
|
||
}
|
||
|
||
if ( !SetupNamedPipe() )
|
||
std::cout << "Unable to setup named pipe" << std::endl;
|
||
|
||
//
|
||
// Preparing shared variables to be used by TIOR that is going to start after we will inject
|
||
// and load dll into elevated process.
|
||
//
|
||
CInterprocessStorage::Create( TEXT("w7e_TIORShell"), std::wstring(TEXT("cmd.exe")) );
|
||
CInterprocessStorage::Create( TEXT("w7e_TIORArgs"), args );
|
||
CInterprocessStorage::Create( TEXT("w7e_TIORDir"), std::wstring(TEXT("C:\\Windows\\System32")) );
|
||
|
||
W7EInject::AttemptOperation(
|
||
GetConsoleWindow(),
|
||
true,
|
||
true,
|
||
pid,
|
||
TEXT("n/a"),
|
||
argv[pass_through_index],
|
||
args.c_str(),
|
||
TEXT("C:\\Windows\\System32"),
|
||
strOurDllPath.c_str(),
|
||
Redirector);
|
||
|
||
return EXIT_SUCCESS;
|
||
|
||
}while(false);
|
||
|
||
PrintUsage();
|
||
return EXIT_FAILURE;
|
||
}
|